removed local variable only used for storing copy of global variable
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 void NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963
4964   game.LevelSolved_GameEnd = TRUE;
4965
4966   if (game.LevelSolved_SaveTape)
4967   {
4968     // make sure that request dialog to save tape does not open door again
4969     if (!global.use_envelope_request)
4970       CloseDoor(DOOR_CLOSE_1);
4971
4972     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4973
4974     // set unique basename for score tape (also saved in high score table)
4975     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4976   }
4977
4978   // if no tape is to be saved, close both doors simultaneously
4979   CloseDoor(DOOR_CLOSE_ALL);
4980
4981   if (level_editor_test_game)
4982   {
4983     SetGameStatus(GAME_MODE_MAIN);
4984
4985     DrawMainMenu();
4986
4987     return;
4988   }
4989
4990   if (!game.LevelSolved_SaveScore)
4991   {
4992     SetGameStatus(GAME_MODE_MAIN);
4993
4994     DrawMainMenu();
4995
4996     return;
4997   }
4998
4999   if (level_nr == leveldir_current->handicap_level)
5000   {
5001     leveldir_current->handicap_level++;
5002
5003     SaveLevelSetup_SeriesInfo();
5004   }
5005
5006   // save score and score tape before potentially erasing tape below
5007   NewHighScore(last_level_nr);
5008
5009   if (setup.increment_levels &&
5010       level_nr < leveldir_current->last_level &&
5011       !network_playing)
5012   {
5013     level_nr++;         // advance to next level
5014     TapeErase();        // start with empty tape
5015
5016     if (setup.auto_play_next_level)
5017     {
5018       LoadLevel(level_nr);
5019
5020       SaveLevelSetup_SeriesInfo();
5021     }
5022   }
5023
5024   if (scores.last_added >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr, scores.last_added);
5029   }
5030   else if (setup.auto_play_next_level && setup.increment_levels &&
5031            last_level_nr < leveldir_current->last_level &&
5032            !network_playing)
5033   {
5034     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5035   }
5036   else
5037   {
5038     SetGameStatus(GAME_MODE_MAIN);
5039
5040     DrawMainMenu();
5041   }
5042 }
5043
5044 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
5045 {
5046   boolean one_score_entry_per_name = !program.many_scores_per_name;
5047   int i;
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME))
5050     return -1;
5051
5052   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5053   {
5054     struct ScoreEntry *entry = &list->entry[i];
5055     boolean score_is_better = (new_entry->score >  entry->score);
5056     boolean score_is_equal  = (new_entry->score == entry->score);
5057     boolean time_is_better  = (new_entry->time  <  entry->time);
5058     boolean time_is_equal   = (new_entry->time  == entry->time);
5059     boolean better_by_score = (score_is_better ||
5060                                (score_is_equal && time_is_better));
5061     boolean better_by_time  = (time_is_better ||
5062                                (time_is_equal && score_is_better));
5063     boolean is_better = (level.rate_time_over_score ? better_by_time :
5064                          better_by_score);
5065     boolean entry_is_empty = (entry->score == 0 &&
5066                               entry->time == 0);
5067
5068     // prevent adding server score entries if also existing in local score file
5069     if (strEqual(new_entry->tape_basename, entry->tape_basename))
5070       return -1;
5071
5072     if (is_better || entry_is_empty)
5073     {
5074       // player has made it to the hall of fame
5075
5076       if (i < MAX_SCORE_ENTRIES - 1)
5077       {
5078         int m = MAX_SCORE_ENTRIES - 1;
5079         int l;
5080
5081         if (one_score_entry_per_name)
5082         {
5083           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5084             if (strEqual(list->entry[l].name, setup.player_name))
5085               m = l;
5086
5087           if (m == i)   // player's new highscore overwrites his old one
5088             goto put_into_list;
5089         }
5090
5091         for (l = m; l > i; l--)
5092           list->entry[l] = list->entry[l - 1];
5093       }
5094
5095       put_into_list:
5096
5097       *entry = *new_entry;
5098
5099       return i;
5100     }
5101     else if (one_score_entry_per_name &&
5102              strEqual(entry->name, setup.player_name))
5103     {
5104       // player already in high score list with better score or time
5105
5106       return -1;
5107     }
5108   }
5109
5110   return -1;
5111 }
5112
5113 void NewHighScore(int level_nr)
5114 {
5115   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5116
5117   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5118   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5119
5120   new_entry.score = game.score_final;
5121   new_entry.time = game.score_time_final;
5122
5123   LoadScore(level_nr);
5124
5125   scores.last_added = addScoreEntry(&scores, &new_entry);
5126
5127   if (scores.last_added >= 0)
5128   {
5129     SaveScore(level_nr);
5130
5131     if (game.LevelSolved_SaveTape)
5132     {
5133       SaveScoreTape(level_nr);
5134       SaveServerScore(level_nr);
5135     }
5136   }
5137 }
5138
5139 void MergeServerScore(void)
5140 {
5141   int i;
5142
5143   for (i = 0; i < server_scores.num_entries; i++)
5144   {
5145     int pos = addScoreEntry(&scores, &server_scores.entry[i]);
5146
5147     if (pos >= 0 && pos <= scores.last_added)
5148       scores.last_added++;
5149   }
5150
5151   if (scores.last_added >= MAX_SCORE_ENTRIES)
5152     scores.last_added = -1;
5153 }
5154
5155 static int getElementMoveStepsizeExt(int x, int y, int direction)
5156 {
5157   int element = Tile[x][y];
5158   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5159   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5160   int horiz_move = (dx != 0);
5161   int sign = (horiz_move ? dx : dy);
5162   int step = sign * element_info[element].move_stepsize;
5163
5164   // special values for move stepsize for spring and things on conveyor belt
5165   if (horiz_move)
5166   {
5167     if (CAN_FALL(element) &&
5168         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5169       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5170     else if (element == EL_SPRING)
5171       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5172   }
5173
5174   return step;
5175 }
5176
5177 static int getElementMoveStepsize(int x, int y)
5178 {
5179   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5180 }
5181
5182 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5183 {
5184   if (player->GfxAction != action || player->GfxDir != dir)
5185   {
5186     player->GfxAction = action;
5187     player->GfxDir = dir;
5188     player->Frame = 0;
5189     player->StepFrame = 0;
5190   }
5191 }
5192
5193 static void ResetGfxFrame(int x, int y)
5194 {
5195   // profiling showed that "autotest" spends 10~20% of its time in this function
5196   if (DrawingDeactivatedField())
5197     return;
5198
5199   int element = Tile[x][y];
5200   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5201
5202   if (graphic_info[graphic].anim_global_sync)
5203     GfxFrame[x][y] = FrameCounter;
5204   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5205     GfxFrame[x][y] = CustomValue[x][y];
5206   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5207     GfxFrame[x][y] = element_info[element].collect_score;
5208   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5209     GfxFrame[x][y] = ChangeDelay[x][y];
5210 }
5211
5212 static void ResetGfxAnimation(int x, int y)
5213 {
5214   GfxAction[x][y] = ACTION_DEFAULT;
5215   GfxDir[x][y] = MovDir[x][y];
5216   GfxFrame[x][y] = 0;
5217
5218   ResetGfxFrame(x, y);
5219 }
5220
5221 static void ResetRandomAnimationValue(int x, int y)
5222 {
5223   GfxRandom[x][y] = INIT_GFX_RANDOM();
5224 }
5225
5226 static void InitMovingField(int x, int y, int direction)
5227 {
5228   int element = Tile[x][y];
5229   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5230   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5231   int newx = x + dx;
5232   int newy = y + dy;
5233   boolean is_moving_before, is_moving_after;
5234
5235   // check if element was/is moving or being moved before/after mode change
5236   is_moving_before = (WasJustMoving[x][y] != 0);
5237   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5238
5239   // reset animation only for moving elements which change direction of moving
5240   // or which just started or stopped moving
5241   // (else CEs with property "can move" / "not moving" are reset each frame)
5242   if (is_moving_before != is_moving_after ||
5243       direction != MovDir[x][y])
5244     ResetGfxAnimation(x, y);
5245
5246   MovDir[x][y] = direction;
5247   GfxDir[x][y] = direction;
5248
5249   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5250                      direction == MV_DOWN && CAN_FALL(element) ?
5251                      ACTION_FALLING : ACTION_MOVING);
5252
5253   // this is needed for CEs with property "can move" / "not moving"
5254
5255   if (is_moving_after)
5256   {
5257     if (Tile[newx][newy] == EL_EMPTY)
5258       Tile[newx][newy] = EL_BLOCKED;
5259
5260     MovDir[newx][newy] = MovDir[x][y];
5261
5262     CustomValue[newx][newy] = CustomValue[x][y];
5263
5264     GfxFrame[newx][newy] = GfxFrame[x][y];
5265     GfxRandom[newx][newy] = GfxRandom[x][y];
5266     GfxAction[newx][newy] = GfxAction[x][y];
5267     GfxDir[newx][newy] = GfxDir[x][y];
5268   }
5269 }
5270
5271 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5272 {
5273   int direction = MovDir[x][y];
5274   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5275   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5276
5277   *goes_to_x = newx;
5278   *goes_to_y = newy;
5279 }
5280
5281 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5282 {
5283   int oldx = x, oldy = y;
5284   int direction = MovDir[x][y];
5285
5286   if (direction == MV_LEFT)
5287     oldx++;
5288   else if (direction == MV_RIGHT)
5289     oldx--;
5290   else if (direction == MV_UP)
5291     oldy++;
5292   else if (direction == MV_DOWN)
5293     oldy--;
5294
5295   *comes_from_x = oldx;
5296   *comes_from_y = oldy;
5297 }
5298
5299 static int MovingOrBlocked2Element(int x, int y)
5300 {
5301   int element = Tile[x][y];
5302
5303   if (element == EL_BLOCKED)
5304   {
5305     int oldx, oldy;
5306
5307     Blocked2Moving(x, y, &oldx, &oldy);
5308     return Tile[oldx][oldy];
5309   }
5310   else
5311     return element;
5312 }
5313
5314 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5315 {
5316   // like MovingOrBlocked2Element(), but if element is moving
5317   // and (x,y) is the field the moving element is just leaving,
5318   // return EL_BLOCKED instead of the element value
5319   int element = Tile[x][y];
5320
5321   if (IS_MOVING(x, y))
5322   {
5323     if (element == EL_BLOCKED)
5324     {
5325       int oldx, oldy;
5326
5327       Blocked2Moving(x, y, &oldx, &oldy);
5328       return Tile[oldx][oldy];
5329     }
5330     else
5331       return EL_BLOCKED;
5332   }
5333   else
5334     return element;
5335 }
5336
5337 static void RemoveField(int x, int y)
5338 {
5339   Tile[x][y] = EL_EMPTY;
5340
5341   MovPos[x][y] = 0;
5342   MovDir[x][y] = 0;
5343   MovDelay[x][y] = 0;
5344
5345   CustomValue[x][y] = 0;
5346
5347   AmoebaNr[x][y] = 0;
5348   ChangeDelay[x][y] = 0;
5349   ChangePage[x][y] = -1;
5350   Pushed[x][y] = FALSE;
5351
5352   GfxElement[x][y] = EL_UNDEFINED;
5353   GfxAction[x][y] = ACTION_DEFAULT;
5354   GfxDir[x][y] = MV_NONE;
5355 }
5356
5357 static void RemoveMovingField(int x, int y)
5358 {
5359   int oldx = x, oldy = y, newx = x, newy = y;
5360   int element = Tile[x][y];
5361   int next_element = EL_UNDEFINED;
5362
5363   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5364     return;
5365
5366   if (IS_MOVING(x, y))
5367   {
5368     Moving2Blocked(x, y, &newx, &newy);
5369
5370     if (Tile[newx][newy] != EL_BLOCKED)
5371     {
5372       // element is moving, but target field is not free (blocked), but
5373       // already occupied by something different (example: acid pool);
5374       // in this case, only remove the moving field, but not the target
5375
5376       RemoveField(oldx, oldy);
5377
5378       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5379
5380       TEST_DrawLevelField(oldx, oldy);
5381
5382       return;
5383     }
5384   }
5385   else if (element == EL_BLOCKED)
5386   {
5387     Blocked2Moving(x, y, &oldx, &oldy);
5388     if (!IS_MOVING(oldx, oldy))
5389       return;
5390   }
5391
5392   if (element == EL_BLOCKED &&
5393       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5394        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5395        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5396        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5397        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5398        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5399     next_element = get_next_element(Tile[oldx][oldy]);
5400
5401   RemoveField(oldx, oldy);
5402   RemoveField(newx, newy);
5403
5404   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5405
5406   if (next_element != EL_UNDEFINED)
5407     Tile[oldx][oldy] = next_element;
5408
5409   TEST_DrawLevelField(oldx, oldy);
5410   TEST_DrawLevelField(newx, newy);
5411 }
5412
5413 void DrawDynamite(int x, int y)
5414 {
5415   int sx = SCREENX(x), sy = SCREENY(y);
5416   int graphic = el2img(Tile[x][y]);
5417   int frame;
5418
5419   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5420     return;
5421
5422   if (IS_WALKABLE_INSIDE(Back[x][y]))
5423     return;
5424
5425   if (Back[x][y])
5426     DrawLevelElement(x, y, Back[x][y]);
5427   else if (Store[x][y])
5428     DrawLevelElement(x, y, Store[x][y]);
5429   else if (game.use_masked_elements)
5430     DrawLevelElement(x, y, EL_EMPTY);
5431
5432   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5433
5434   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5435     DrawGraphicThruMask(sx, sy, graphic, frame);
5436   else
5437     DrawGraphic(sx, sy, graphic, frame);
5438 }
5439
5440 static void CheckDynamite(int x, int y)
5441 {
5442   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5443   {
5444     MovDelay[x][y]--;
5445
5446     if (MovDelay[x][y] != 0)
5447     {
5448       DrawDynamite(x, y);
5449       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5450
5451       return;
5452     }
5453   }
5454
5455   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5456
5457   Bang(x, y);
5458 }
5459
5460 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5461 {
5462   boolean num_checked_players = 0;
5463   int i;
5464
5465   for (i = 0; i < MAX_PLAYERS; i++)
5466   {
5467     if (stored_player[i].active)
5468     {
5469       int sx = stored_player[i].jx;
5470       int sy = stored_player[i].jy;
5471
5472       if (num_checked_players == 0)
5473       {
5474         *sx1 = *sx2 = sx;
5475         *sy1 = *sy2 = sy;
5476       }
5477       else
5478       {
5479         *sx1 = MIN(*sx1, sx);
5480         *sy1 = MIN(*sy1, sy);
5481         *sx2 = MAX(*sx2, sx);
5482         *sy2 = MAX(*sy2, sy);
5483       }
5484
5485       num_checked_players++;
5486     }
5487   }
5488 }
5489
5490 static boolean checkIfAllPlayersFitToScreen_RND(void)
5491 {
5492   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5493
5494   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5495
5496   return (sx2 - sx1 < SCR_FIELDX &&
5497           sy2 - sy1 < SCR_FIELDY);
5498 }
5499
5500 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5501 {
5502   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5503
5504   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5505
5506   *sx = (sx1 + sx2) / 2;
5507   *sy = (sy1 + sy2) / 2;
5508 }
5509
5510 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5511                                boolean center_screen, boolean quick_relocation)
5512 {
5513   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5514   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5515   boolean no_delay = (tape.warp_forward);
5516   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5517   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5518   int new_scroll_x, new_scroll_y;
5519
5520   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5521   {
5522     // case 1: quick relocation inside visible screen (without scrolling)
5523
5524     RedrawPlayfield();
5525
5526     return;
5527   }
5528
5529   if (!level.shifted_relocation || center_screen)
5530   {
5531     // relocation _with_ centering of screen
5532
5533     new_scroll_x = SCROLL_POSITION_X(x);
5534     new_scroll_y = SCROLL_POSITION_Y(y);
5535   }
5536   else
5537   {
5538     // relocation _without_ centering of screen
5539
5540     int center_scroll_x = SCROLL_POSITION_X(old_x);
5541     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5542     int offset_x = x + (scroll_x - center_scroll_x);
5543     int offset_y = y + (scroll_y - center_scroll_y);
5544
5545     // for new screen position, apply previous offset to center position
5546     new_scroll_x = SCROLL_POSITION_X(offset_x);
5547     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5548   }
5549
5550   if (quick_relocation)
5551   {
5552     // case 2: quick relocation (redraw without visible scrolling)
5553
5554     scroll_x = new_scroll_x;
5555     scroll_y = new_scroll_y;
5556
5557     RedrawPlayfield();
5558
5559     return;
5560   }
5561
5562   // case 3: visible relocation (with scrolling to new position)
5563
5564   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5565
5566   SetVideoFrameDelay(wait_delay_value);
5567
5568   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5569   {
5570     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5571     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5572
5573     if (dx == 0 && dy == 0)             // no scrolling needed at all
5574       break;
5575
5576     scroll_x -= dx;
5577     scroll_y -= dy;
5578
5579     // set values for horizontal/vertical screen scrolling (half tile size)
5580     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5581     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5582     int pos_x = dx * TILEX / 2;
5583     int pos_y = dy * TILEY / 2;
5584     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5585     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5586
5587     ScrollLevel(dx, dy);
5588     DrawAllPlayers();
5589
5590     // scroll in two steps of half tile size to make things smoother
5591     BlitScreenToBitmapExt_RND(window, fx, fy);
5592
5593     // scroll second step to align at full tile size
5594     BlitScreenToBitmap(window);
5595   }
5596
5597   DrawAllPlayers();
5598   BackToFront();
5599
5600   SetVideoFrameDelay(frame_delay_value_old);
5601 }
5602
5603 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5604 {
5605   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5606   int player_nr = GET_PLAYER_NR(el_player);
5607   struct PlayerInfo *player = &stored_player[player_nr];
5608   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5609   boolean no_delay = (tape.warp_forward);
5610   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5611   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5612   int old_jx = player->jx;
5613   int old_jy = player->jy;
5614   int old_element = Tile[old_jx][old_jy];
5615   int element = Tile[jx][jy];
5616   boolean player_relocated = (old_jx != jx || old_jy != jy);
5617
5618   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5619   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5620   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5621   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5622   int leave_side_horiz = move_dir_horiz;
5623   int leave_side_vert  = move_dir_vert;
5624   int enter_side = enter_side_horiz | enter_side_vert;
5625   int leave_side = leave_side_horiz | leave_side_vert;
5626
5627   if (player->buried)           // do not reanimate dead player
5628     return;
5629
5630   if (!player_relocated)        // no need to relocate the player
5631     return;
5632
5633   if (IS_PLAYER(jx, jy))        // player already placed at new position
5634   {
5635     RemoveField(jx, jy);        // temporarily remove newly placed player
5636     DrawLevelField(jx, jy);
5637   }
5638
5639   if (player->present)
5640   {
5641     while (player->MovPos)
5642     {
5643       ScrollPlayer(player, SCROLL_GO_ON);
5644       ScrollScreen(NULL, SCROLL_GO_ON);
5645
5646       AdvanceFrameAndPlayerCounters(player->index_nr);
5647
5648       DrawPlayer(player);
5649
5650       BackToFront_WithFrameDelay(wait_delay_value);
5651     }
5652
5653     DrawPlayer(player);         // needed here only to cleanup last field
5654     DrawLevelField(player->jx, player->jy);     // remove player graphic
5655
5656     player->is_moving = FALSE;
5657   }
5658
5659   if (IS_CUSTOM_ELEMENT(old_element))
5660     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5661                                CE_LEFT_BY_PLAYER,
5662                                player->index_bit, leave_side);
5663
5664   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5665                                       CE_PLAYER_LEAVES_X,
5666                                       player->index_bit, leave_side);
5667
5668   Tile[jx][jy] = el_player;
5669   InitPlayerField(jx, jy, el_player, TRUE);
5670
5671   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5672      possible that the relocation target field did not contain a player element,
5673      but a walkable element, to which the new player was relocated -- in this
5674      case, restore that (already initialized!) element on the player field */
5675   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5676   {
5677     Tile[jx][jy] = element;     // restore previously existing element
5678   }
5679
5680   // only visually relocate centered player
5681   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5682                      FALSE, level.instant_relocation);
5683
5684   TestIfPlayerTouchesBadThing(jx, jy);
5685   TestIfPlayerTouchesCustomElement(jx, jy);
5686
5687   if (IS_CUSTOM_ELEMENT(element))
5688     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5689                                player->index_bit, enter_side);
5690
5691   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5692                                       player->index_bit, enter_side);
5693
5694   if (player->is_switching)
5695   {
5696     /* ensure that relocation while still switching an element does not cause
5697        a new element to be treated as also switched directly after relocation
5698        (this is important for teleporter switches that teleport the player to
5699        a place where another teleporter switch is in the same direction, which
5700        would then incorrectly be treated as immediately switched before the
5701        direction key that caused the switch was released) */
5702
5703     player->switch_x += jx - old_jx;
5704     player->switch_y += jy - old_jy;
5705   }
5706 }
5707
5708 static void Explode(int ex, int ey, int phase, int mode)
5709 {
5710   int x, y;
5711   int last_phase;
5712   int border_element;
5713
5714   // !!! eliminate this variable !!!
5715   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5716
5717   if (game.explosions_delayed)
5718   {
5719     ExplodeField[ex][ey] = mode;
5720     return;
5721   }
5722
5723   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5724   {
5725     int center_element = Tile[ex][ey];
5726     int artwork_element, explosion_element;     // set these values later
5727
5728     // remove things displayed in background while burning dynamite
5729     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5730       Back[ex][ey] = 0;
5731
5732     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5733     {
5734       // put moving element to center field (and let it explode there)
5735       center_element = MovingOrBlocked2Element(ex, ey);
5736       RemoveMovingField(ex, ey);
5737       Tile[ex][ey] = center_element;
5738     }
5739
5740     // now "center_element" is finally determined -- set related values now
5741     artwork_element = center_element;           // for custom player artwork
5742     explosion_element = center_element;         // for custom player artwork
5743
5744     if (IS_PLAYER(ex, ey))
5745     {
5746       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5747
5748       artwork_element = stored_player[player_nr].artwork_element;
5749
5750       if (level.use_explosion_element[player_nr])
5751       {
5752         explosion_element = level.explosion_element[player_nr];
5753         artwork_element = explosion_element;
5754       }
5755     }
5756
5757     if (mode == EX_TYPE_NORMAL ||
5758         mode == EX_TYPE_CENTER ||
5759         mode == EX_TYPE_CROSS)
5760       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5761
5762     last_phase = element_info[explosion_element].explosion_delay + 1;
5763
5764     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5765     {
5766       int xx = x - ex + 1;
5767       int yy = y - ey + 1;
5768       int element;
5769
5770       if (!IN_LEV_FIELD(x, y) ||
5771           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5772           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5773         continue;
5774
5775       element = Tile[x][y];
5776
5777       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5778       {
5779         element = MovingOrBlocked2Element(x, y);
5780
5781         if (!IS_EXPLOSION_PROOF(element))
5782           RemoveMovingField(x, y);
5783       }
5784
5785       // indestructible elements can only explode in center (but not flames)
5786       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5787                                            mode == EX_TYPE_BORDER)) ||
5788           element == EL_FLAMES)
5789         continue;
5790
5791       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5792          behaviour, for example when touching a yamyam that explodes to rocks
5793          with active deadly shield, a rock is created under the player !!! */
5794       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5795 #if 0
5796       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5797           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5798            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5799 #else
5800       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5801 #endif
5802       {
5803         if (IS_ACTIVE_BOMB(element))
5804         {
5805           // re-activate things under the bomb like gate or penguin
5806           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5807           Back[x][y] = 0;
5808         }
5809
5810         continue;
5811       }
5812
5813       // save walkable background elements while explosion on same tile
5814       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5815           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5816         Back[x][y] = element;
5817
5818       // ignite explodable elements reached by other explosion
5819       if (element == EL_EXPLOSION)
5820         element = Store2[x][y];
5821
5822       if (AmoebaNr[x][y] &&
5823           (element == EL_AMOEBA_FULL ||
5824            element == EL_BD_AMOEBA ||
5825            element == EL_AMOEBA_GROWING))
5826       {
5827         AmoebaCnt[AmoebaNr[x][y]]--;
5828         AmoebaCnt2[AmoebaNr[x][y]]--;
5829       }
5830
5831       RemoveField(x, y);
5832
5833       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5834       {
5835         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5836
5837         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5838
5839         if (PLAYERINFO(ex, ey)->use_murphy)
5840           Store[x][y] = EL_EMPTY;
5841       }
5842
5843       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5844       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5845       else if (ELEM_IS_PLAYER(center_element))
5846         Store[x][y] = EL_EMPTY;
5847       else if (center_element == EL_YAMYAM)
5848         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5849       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5850         Store[x][y] = element_info[center_element].content.e[xx][yy];
5851 #if 1
5852       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5853       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5854       // otherwise) -- FIX THIS !!!
5855       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5856         Store[x][y] = element_info[element].content.e[1][1];
5857 #else
5858       else if (!CAN_EXPLODE(element))
5859         Store[x][y] = element_info[element].content.e[1][1];
5860 #endif
5861       else
5862         Store[x][y] = EL_EMPTY;
5863
5864       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5865           center_element == EL_AMOEBA_TO_DIAMOND)
5866         Store2[x][y] = element;
5867
5868       Tile[x][y] = EL_EXPLOSION;
5869       GfxElement[x][y] = artwork_element;
5870
5871       ExplodePhase[x][y] = 1;
5872       ExplodeDelay[x][y] = last_phase;
5873
5874       Stop[x][y] = TRUE;
5875     }
5876
5877     if (center_element == EL_YAMYAM)
5878       game.yamyam_content_nr =
5879         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5880
5881     return;
5882   }
5883
5884   if (Stop[ex][ey])
5885     return;
5886
5887   x = ex;
5888   y = ey;
5889
5890   if (phase == 1)
5891     GfxFrame[x][y] = 0;         // restart explosion animation
5892
5893   last_phase = ExplodeDelay[x][y];
5894
5895   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5896
5897   // this can happen if the player leaves an explosion just in time
5898   if (GfxElement[x][y] == EL_UNDEFINED)
5899     GfxElement[x][y] = EL_EMPTY;
5900
5901   border_element = Store2[x][y];
5902   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5903     border_element = StorePlayer[x][y];
5904
5905   if (phase == element_info[border_element].ignition_delay ||
5906       phase == last_phase)
5907   {
5908     boolean border_explosion = FALSE;
5909
5910     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5911         !PLAYER_EXPLOSION_PROTECTED(x, y))
5912     {
5913       KillPlayerUnlessExplosionProtected(x, y);
5914       border_explosion = TRUE;
5915     }
5916     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5917     {
5918       Tile[x][y] = Store2[x][y];
5919       Store2[x][y] = 0;
5920       Bang(x, y);
5921       border_explosion = TRUE;
5922     }
5923     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5924     {
5925       AmoebaToDiamond(x, y);
5926       Store2[x][y] = 0;
5927       border_explosion = TRUE;
5928     }
5929
5930     // if an element just explodes due to another explosion (chain-reaction),
5931     // do not immediately end the new explosion when it was the last frame of
5932     // the explosion (as it would be done in the following "if"-statement!)
5933     if (border_explosion && phase == last_phase)
5934       return;
5935   }
5936
5937   if (phase == last_phase)
5938   {
5939     int element;
5940
5941     element = Tile[x][y] = Store[x][y];
5942     Store[x][y] = Store2[x][y] = 0;
5943     GfxElement[x][y] = EL_UNDEFINED;
5944
5945     // player can escape from explosions and might therefore be still alive
5946     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5947         element <= EL_PLAYER_IS_EXPLODING_4)
5948     {
5949       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5950       int explosion_element = EL_PLAYER_1 + player_nr;
5951       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5952       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5953
5954       if (level.use_explosion_element[player_nr])
5955         explosion_element = level.explosion_element[player_nr];
5956
5957       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5958                     element_info[explosion_element].content.e[xx][yy]);
5959     }
5960
5961     // restore probably existing indestructible background element
5962     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5963       element = Tile[x][y] = Back[x][y];
5964     Back[x][y] = 0;
5965
5966     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5967     GfxDir[x][y] = MV_NONE;
5968     ChangeDelay[x][y] = 0;
5969     ChangePage[x][y] = -1;
5970
5971     CustomValue[x][y] = 0;
5972
5973     InitField_WithBug2(x, y, FALSE);
5974
5975     TEST_DrawLevelField(x, y);
5976
5977     TestIfElementTouchesCustomElement(x, y);
5978
5979     if (GFX_CRUMBLED(element))
5980       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5981
5982     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5983       StorePlayer[x][y] = 0;
5984
5985     if (ELEM_IS_PLAYER(element))
5986       RelocatePlayer(x, y, element);
5987   }
5988   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5989   {
5990     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5991     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5992
5993     if (phase == delay)
5994       TEST_DrawLevelFieldCrumbled(x, y);
5995
5996     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5997     {
5998       DrawLevelElement(x, y, Back[x][y]);
5999       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6000     }
6001     else if (IS_WALKABLE_UNDER(Back[x][y]))
6002     {
6003       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6004       DrawLevelElementThruMask(x, y, Back[x][y]);
6005     }
6006     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6007       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6008   }
6009 }
6010
6011 static void DynaExplode(int ex, int ey)
6012 {
6013   int i, j;
6014   int dynabomb_element = Tile[ex][ey];
6015   int dynabomb_size = 1;
6016   boolean dynabomb_xl = FALSE;
6017   struct PlayerInfo *player;
6018   static int xy[4][2] =
6019   {
6020     { 0, -1 },
6021     { -1, 0 },
6022     { +1, 0 },
6023     { 0, +1 }
6024   };
6025
6026   if (IS_ACTIVE_BOMB(dynabomb_element))
6027   {
6028     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6029     dynabomb_size = player->dynabomb_size;
6030     dynabomb_xl = player->dynabomb_xl;
6031     player->dynabombs_left++;
6032   }
6033
6034   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6035
6036   for (i = 0; i < NUM_DIRECTIONS; i++)
6037   {
6038     for (j = 1; j <= dynabomb_size; j++)
6039     {
6040       int x = ex + j * xy[i][0];
6041       int y = ey + j * xy[i][1];
6042       int element;
6043
6044       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6045         break;
6046
6047       element = Tile[x][y];
6048
6049       // do not restart explosions of fields with active bombs
6050       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6051         continue;
6052
6053       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6054
6055       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6056           !IS_DIGGABLE(element) && !dynabomb_xl)
6057         break;
6058     }
6059   }
6060 }
6061
6062 void Bang(int x, int y)
6063 {
6064   int element = MovingOrBlocked2Element(x, y);
6065   int explosion_type = EX_TYPE_NORMAL;
6066
6067   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6068   {
6069     struct PlayerInfo *player = PLAYERINFO(x, y);
6070
6071     element = Tile[x][y] = player->initial_element;
6072
6073     if (level.use_explosion_element[player->index_nr])
6074     {
6075       int explosion_element = level.explosion_element[player->index_nr];
6076
6077       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6078         explosion_type = EX_TYPE_CROSS;
6079       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6080         explosion_type = EX_TYPE_CENTER;
6081     }
6082   }
6083
6084   switch (element)
6085   {
6086     case EL_BUG:
6087     case EL_SPACESHIP:
6088     case EL_BD_BUTTERFLY:
6089     case EL_BD_FIREFLY:
6090     case EL_YAMYAM:
6091     case EL_DARK_YAMYAM:
6092     case EL_ROBOT:
6093     case EL_PACMAN:
6094     case EL_MOLE:
6095       RaiseScoreElement(element);
6096       break;
6097
6098     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6099     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6100     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6101     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6102     case EL_DYNABOMB_INCREASE_NUMBER:
6103     case EL_DYNABOMB_INCREASE_SIZE:
6104     case EL_DYNABOMB_INCREASE_POWER:
6105       explosion_type = EX_TYPE_DYNA;
6106       break;
6107
6108     case EL_DC_LANDMINE:
6109       explosion_type = EX_TYPE_CENTER;
6110       break;
6111
6112     case EL_PENGUIN:
6113     case EL_LAMP:
6114     case EL_LAMP_ACTIVE:
6115     case EL_AMOEBA_TO_DIAMOND:
6116       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6117         explosion_type = EX_TYPE_CENTER;
6118       break;
6119
6120     default:
6121       if (element_info[element].explosion_type == EXPLODES_CROSS)
6122         explosion_type = EX_TYPE_CROSS;
6123       else if (element_info[element].explosion_type == EXPLODES_1X1)
6124         explosion_type = EX_TYPE_CENTER;
6125       break;
6126   }
6127
6128   if (explosion_type == EX_TYPE_DYNA)
6129     DynaExplode(x, y);
6130   else
6131     Explode(x, y, EX_PHASE_START, explosion_type);
6132
6133   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6134 }
6135
6136 static void SplashAcid(int x, int y)
6137 {
6138   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6139       (!IN_LEV_FIELD(x - 1, y - 2) ||
6140        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6141     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6142
6143   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6144       (!IN_LEV_FIELD(x + 1, y - 2) ||
6145        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6146     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6147
6148   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6149 }
6150
6151 static void InitBeltMovement(void)
6152 {
6153   static int belt_base_element[4] =
6154   {
6155     EL_CONVEYOR_BELT_1_LEFT,
6156     EL_CONVEYOR_BELT_2_LEFT,
6157     EL_CONVEYOR_BELT_3_LEFT,
6158     EL_CONVEYOR_BELT_4_LEFT
6159   };
6160   static int belt_base_active_element[4] =
6161   {
6162     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6163     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6164     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6165     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6166   };
6167
6168   int x, y, i, j;
6169
6170   // set frame order for belt animation graphic according to belt direction
6171   for (i = 0; i < NUM_BELTS; i++)
6172   {
6173     int belt_nr = i;
6174
6175     for (j = 0; j < NUM_BELT_PARTS; j++)
6176     {
6177       int element = belt_base_active_element[belt_nr] + j;
6178       int graphic_1 = el2img(element);
6179       int graphic_2 = el2panelimg(element);
6180
6181       if (game.belt_dir[i] == MV_LEFT)
6182       {
6183         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6184         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6185       }
6186       else
6187       {
6188         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6189         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6190       }
6191     }
6192   }
6193
6194   SCAN_PLAYFIELD(x, y)
6195   {
6196     int element = Tile[x][y];
6197
6198     for (i = 0; i < NUM_BELTS; i++)
6199     {
6200       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6201       {
6202         int e_belt_nr = getBeltNrFromBeltElement(element);
6203         int belt_nr = i;
6204
6205         if (e_belt_nr == belt_nr)
6206         {
6207           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6208
6209           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6210         }
6211       }
6212     }
6213   }
6214 }
6215
6216 static void ToggleBeltSwitch(int x, int y)
6217 {
6218   static int belt_base_element[4] =
6219   {
6220     EL_CONVEYOR_BELT_1_LEFT,
6221     EL_CONVEYOR_BELT_2_LEFT,
6222     EL_CONVEYOR_BELT_3_LEFT,
6223     EL_CONVEYOR_BELT_4_LEFT
6224   };
6225   static int belt_base_active_element[4] =
6226   {
6227     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6228     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6229     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6230     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6231   };
6232   static int belt_base_switch_element[4] =
6233   {
6234     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6235     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6236     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6237     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6238   };
6239   static int belt_move_dir[4] =
6240   {
6241     MV_LEFT,
6242     MV_NONE,
6243     MV_RIGHT,
6244     MV_NONE,
6245   };
6246
6247   int element = Tile[x][y];
6248   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6249   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6250   int belt_dir = belt_move_dir[belt_dir_nr];
6251   int xx, yy, i;
6252
6253   if (!IS_BELT_SWITCH(element))
6254     return;
6255
6256   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6257   game.belt_dir[belt_nr] = belt_dir;
6258
6259   if (belt_dir_nr == 3)
6260     belt_dir_nr = 1;
6261
6262   // set frame order for belt animation graphic according to belt direction
6263   for (i = 0; i < NUM_BELT_PARTS; i++)
6264   {
6265     int element = belt_base_active_element[belt_nr] + i;
6266     int graphic_1 = el2img(element);
6267     int graphic_2 = el2panelimg(element);
6268
6269     if (belt_dir == MV_LEFT)
6270     {
6271       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6272       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6273     }
6274     else
6275     {
6276       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6277       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6278     }
6279   }
6280
6281   SCAN_PLAYFIELD(xx, yy)
6282   {
6283     int element = Tile[xx][yy];
6284
6285     if (IS_BELT_SWITCH(element))
6286     {
6287       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6288
6289       if (e_belt_nr == belt_nr)
6290       {
6291         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6292         TEST_DrawLevelField(xx, yy);
6293       }
6294     }
6295     else if (IS_BELT(element) && belt_dir != MV_NONE)
6296     {
6297       int e_belt_nr = getBeltNrFromBeltElement(element);
6298
6299       if (e_belt_nr == belt_nr)
6300       {
6301         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6302
6303         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6304         TEST_DrawLevelField(xx, yy);
6305       }
6306     }
6307     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6308     {
6309       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6310
6311       if (e_belt_nr == belt_nr)
6312       {
6313         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6314
6315         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6316         TEST_DrawLevelField(xx, yy);
6317       }
6318     }
6319   }
6320 }
6321
6322 static void ToggleSwitchgateSwitch(int x, int y)
6323 {
6324   int xx, yy;
6325
6326   game.switchgate_pos = !game.switchgate_pos;
6327
6328   SCAN_PLAYFIELD(xx, yy)
6329   {
6330     int element = Tile[xx][yy];
6331
6332     if (element == EL_SWITCHGATE_SWITCH_UP)
6333     {
6334       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6335       TEST_DrawLevelField(xx, yy);
6336     }
6337     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6338     {
6339       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6340       TEST_DrawLevelField(xx, yy);
6341     }
6342     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6343     {
6344       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6345       TEST_DrawLevelField(xx, yy);
6346     }
6347     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6348     {
6349       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6350       TEST_DrawLevelField(xx, yy);
6351     }
6352     else if (element == EL_SWITCHGATE_OPEN ||
6353              element == EL_SWITCHGATE_OPENING)
6354     {
6355       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6356
6357       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6358     }
6359     else if (element == EL_SWITCHGATE_CLOSED ||
6360              element == EL_SWITCHGATE_CLOSING)
6361     {
6362       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6363
6364       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6365     }
6366   }
6367 }
6368
6369 static int getInvisibleActiveFromInvisibleElement(int element)
6370 {
6371   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6372           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6373           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6374           element);
6375 }
6376
6377 static int getInvisibleFromInvisibleActiveElement(int element)
6378 {
6379   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6380           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6381           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6382           element);
6383 }
6384
6385 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6386 {
6387   int x, y;
6388
6389   SCAN_PLAYFIELD(x, y)
6390   {
6391     int element = Tile[x][y];
6392
6393     if (element == EL_LIGHT_SWITCH &&
6394         game.light_time_left > 0)
6395     {
6396       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6397       TEST_DrawLevelField(x, y);
6398     }
6399     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6400              game.light_time_left == 0)
6401     {
6402       Tile[x][y] = EL_LIGHT_SWITCH;
6403       TEST_DrawLevelField(x, y);
6404     }
6405     else if (element == EL_EMC_DRIPPER &&
6406              game.light_time_left > 0)
6407     {
6408       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6409       TEST_DrawLevelField(x, y);
6410     }
6411     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6412              game.light_time_left == 0)
6413     {
6414       Tile[x][y] = EL_EMC_DRIPPER;
6415       TEST_DrawLevelField(x, y);
6416     }
6417     else if (element == EL_INVISIBLE_STEELWALL ||
6418              element == EL_INVISIBLE_WALL ||
6419              element == EL_INVISIBLE_SAND)
6420     {
6421       if (game.light_time_left > 0)
6422         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6423
6424       TEST_DrawLevelField(x, y);
6425
6426       // uncrumble neighbour fields, if needed
6427       if (element == EL_INVISIBLE_SAND)
6428         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6429     }
6430     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6431              element == EL_INVISIBLE_WALL_ACTIVE ||
6432              element == EL_INVISIBLE_SAND_ACTIVE)
6433     {
6434       if (game.light_time_left == 0)
6435         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6436
6437       TEST_DrawLevelField(x, y);
6438
6439       // re-crumble neighbour fields, if needed
6440       if (element == EL_INVISIBLE_SAND)
6441         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6442     }
6443   }
6444 }
6445
6446 static void RedrawAllInvisibleElementsForLenses(void)
6447 {
6448   int x, y;
6449
6450   SCAN_PLAYFIELD(x, y)
6451   {
6452     int element = Tile[x][y];
6453
6454     if (element == EL_EMC_DRIPPER &&
6455         game.lenses_time_left > 0)
6456     {
6457       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6458       TEST_DrawLevelField(x, y);
6459     }
6460     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6461              game.lenses_time_left == 0)
6462     {
6463       Tile[x][y] = EL_EMC_DRIPPER;
6464       TEST_DrawLevelField(x, y);
6465     }
6466     else if (element == EL_INVISIBLE_STEELWALL ||
6467              element == EL_INVISIBLE_WALL ||
6468              element == EL_INVISIBLE_SAND)
6469     {
6470       if (game.lenses_time_left > 0)
6471         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6472
6473       TEST_DrawLevelField(x, y);
6474
6475       // uncrumble neighbour fields, if needed
6476       if (element == EL_INVISIBLE_SAND)
6477         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6478     }
6479     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6480              element == EL_INVISIBLE_WALL_ACTIVE ||
6481              element == EL_INVISIBLE_SAND_ACTIVE)
6482     {
6483       if (game.lenses_time_left == 0)
6484         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6485
6486       TEST_DrawLevelField(x, y);
6487
6488       // re-crumble neighbour fields, if needed
6489       if (element == EL_INVISIBLE_SAND)
6490         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6491     }
6492   }
6493 }
6494
6495 static void RedrawAllInvisibleElementsForMagnifier(void)
6496 {
6497   int x, y;
6498
6499   SCAN_PLAYFIELD(x, y)
6500   {
6501     int element = Tile[x][y];
6502
6503     if (element == EL_EMC_FAKE_GRASS &&
6504         game.magnify_time_left > 0)
6505     {
6506       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6507       TEST_DrawLevelField(x, y);
6508     }
6509     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6510              game.magnify_time_left == 0)
6511     {
6512       Tile[x][y] = EL_EMC_FAKE_GRASS;
6513       TEST_DrawLevelField(x, y);
6514     }
6515     else if (IS_GATE_GRAY(element) &&
6516              game.magnify_time_left > 0)
6517     {
6518       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6519                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6520                     IS_EM_GATE_GRAY(element) ?
6521                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6522                     IS_EMC_GATE_GRAY(element) ?
6523                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6524                     IS_DC_GATE_GRAY(element) ?
6525                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6526                     element);
6527       TEST_DrawLevelField(x, y);
6528     }
6529     else if (IS_GATE_GRAY_ACTIVE(element) &&
6530              game.magnify_time_left == 0)
6531     {
6532       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6533                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6534                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6535                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6536                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6537                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6538                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6539                     EL_DC_GATE_WHITE_GRAY :
6540                     element);
6541       TEST_DrawLevelField(x, y);
6542     }
6543   }
6544 }
6545
6546 static void ToggleLightSwitch(int x, int y)
6547 {
6548   int element = Tile[x][y];
6549
6550   game.light_time_left =
6551     (element == EL_LIGHT_SWITCH ?
6552      level.time_light * FRAMES_PER_SECOND : 0);
6553
6554   RedrawAllLightSwitchesAndInvisibleElements();
6555 }
6556
6557 static void ActivateTimegateSwitch(int x, int y)
6558 {
6559   int xx, yy;
6560
6561   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6562
6563   SCAN_PLAYFIELD(xx, yy)
6564   {
6565     int element = Tile[xx][yy];
6566
6567     if (element == EL_TIMEGATE_CLOSED ||
6568         element == EL_TIMEGATE_CLOSING)
6569     {
6570       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6571       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6572     }
6573
6574     /*
6575     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6576     {
6577       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6578       TEST_DrawLevelField(xx, yy);
6579     }
6580     */
6581
6582   }
6583
6584   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6585                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6586 }
6587
6588 static void Impact(int x, int y)
6589 {
6590   boolean last_line = (y == lev_fieldy - 1);
6591   boolean object_hit = FALSE;
6592   boolean impact = (last_line || object_hit);
6593   int element = Tile[x][y];
6594   int smashed = EL_STEELWALL;
6595
6596   if (!last_line)       // check if element below was hit
6597   {
6598     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6599       return;
6600
6601     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6602                                          MovDir[x][y + 1] != MV_DOWN ||
6603                                          MovPos[x][y + 1] <= TILEY / 2));
6604
6605     // do not smash moving elements that left the smashed field in time
6606     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6607         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6608       object_hit = FALSE;
6609
6610 #if USE_QUICKSAND_IMPACT_BUGFIX
6611     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6612     {
6613       RemoveMovingField(x, y + 1);
6614       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6615       Tile[x][y + 2] = EL_ROCK;
6616       TEST_DrawLevelField(x, y + 2);
6617
6618       object_hit = TRUE;
6619     }
6620
6621     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6622     {
6623       RemoveMovingField(x, y + 1);
6624       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6625       Tile[x][y + 2] = EL_ROCK;
6626       TEST_DrawLevelField(x, y + 2);
6627
6628       object_hit = TRUE;
6629     }
6630 #endif
6631
6632     if (object_hit)
6633       smashed = MovingOrBlocked2Element(x, y + 1);
6634
6635     impact = (last_line || object_hit);
6636   }
6637
6638   if (!last_line && smashed == EL_ACID) // element falls into acid
6639   {
6640     SplashAcid(x, y + 1);
6641     return;
6642   }
6643
6644   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6645   // only reset graphic animation if graphic really changes after impact
6646   if (impact &&
6647       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6648   {
6649     ResetGfxAnimation(x, y);
6650     TEST_DrawLevelField(x, y);
6651   }
6652
6653   if (impact && CAN_EXPLODE_IMPACT(element))
6654   {
6655     Bang(x, y);
6656     return;
6657   }
6658   else if (impact && element == EL_PEARL &&
6659            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6660   {
6661     ResetGfxAnimation(x, y);
6662
6663     Tile[x][y] = EL_PEARL_BREAKING;
6664     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6665     return;
6666   }
6667   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6668   {
6669     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6670
6671     return;
6672   }
6673
6674   if (impact && element == EL_AMOEBA_DROP)
6675   {
6676     if (object_hit && IS_PLAYER(x, y + 1))
6677       KillPlayerUnlessEnemyProtected(x, y + 1);
6678     else if (object_hit && smashed == EL_PENGUIN)
6679       Bang(x, y + 1);
6680     else
6681     {
6682       Tile[x][y] = EL_AMOEBA_GROWING;
6683       Store[x][y] = EL_AMOEBA_WET;
6684
6685       ResetRandomAnimationValue(x, y);
6686     }
6687     return;
6688   }
6689
6690   if (object_hit)               // check which object was hit
6691   {
6692     if ((CAN_PASS_MAGIC_WALL(element) && 
6693          (smashed == EL_MAGIC_WALL ||
6694           smashed == EL_BD_MAGIC_WALL)) ||
6695         (CAN_PASS_DC_MAGIC_WALL(element) &&
6696          smashed == EL_DC_MAGIC_WALL))
6697     {
6698       int xx, yy;
6699       int activated_magic_wall =
6700         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6701          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6702          EL_DC_MAGIC_WALL_ACTIVE);
6703
6704       // activate magic wall / mill
6705       SCAN_PLAYFIELD(xx, yy)
6706       {
6707         if (Tile[xx][yy] == smashed)
6708           Tile[xx][yy] = activated_magic_wall;
6709       }
6710
6711       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6712       game.magic_wall_active = TRUE;
6713
6714       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6715                             SND_MAGIC_WALL_ACTIVATING :
6716                             smashed == EL_BD_MAGIC_WALL ?
6717                             SND_BD_MAGIC_WALL_ACTIVATING :
6718                             SND_DC_MAGIC_WALL_ACTIVATING));
6719     }
6720
6721     if (IS_PLAYER(x, y + 1))
6722     {
6723       if (CAN_SMASH_PLAYER(element))
6724       {
6725         KillPlayerUnlessEnemyProtected(x, y + 1);
6726         return;
6727       }
6728     }
6729     else if (smashed == EL_PENGUIN)
6730     {
6731       if (CAN_SMASH_PLAYER(element))
6732       {
6733         Bang(x, y + 1);
6734         return;
6735       }
6736     }
6737     else if (element == EL_BD_DIAMOND)
6738     {
6739       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6740       {
6741         Bang(x, y + 1);
6742         return;
6743       }
6744     }
6745     else if (((element == EL_SP_INFOTRON ||
6746                element == EL_SP_ZONK) &&
6747               (smashed == EL_SP_SNIKSNAK ||
6748                smashed == EL_SP_ELECTRON ||
6749                smashed == EL_SP_DISK_ORANGE)) ||
6750              (element == EL_SP_INFOTRON &&
6751               smashed == EL_SP_DISK_YELLOW))
6752     {
6753       Bang(x, y + 1);
6754       return;
6755     }
6756     else if (CAN_SMASH_EVERYTHING(element))
6757     {
6758       if (IS_CLASSIC_ENEMY(smashed) ||
6759           CAN_EXPLODE_SMASHED(smashed))
6760       {
6761         Bang(x, y + 1);
6762         return;
6763       }
6764       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6765       {
6766         if (smashed == EL_LAMP ||
6767             smashed == EL_LAMP_ACTIVE)
6768         {
6769           Bang(x, y + 1);
6770           return;
6771         }
6772         else if (smashed == EL_NUT)
6773         {
6774           Tile[x][y + 1] = EL_NUT_BREAKING;
6775           PlayLevelSound(x, y, SND_NUT_BREAKING);
6776           RaiseScoreElement(EL_NUT);
6777           return;
6778         }
6779         else if (smashed == EL_PEARL)
6780         {
6781           ResetGfxAnimation(x, y);
6782
6783           Tile[x][y + 1] = EL_PEARL_BREAKING;
6784           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6785           return;
6786         }
6787         else if (smashed == EL_DIAMOND)
6788         {
6789           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6790           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6791           return;
6792         }
6793         else if (IS_BELT_SWITCH(smashed))
6794         {
6795           ToggleBeltSwitch(x, y + 1);
6796         }
6797         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6798                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6799                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6800                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6801         {
6802           ToggleSwitchgateSwitch(x, y + 1);
6803         }
6804         else if (smashed == EL_LIGHT_SWITCH ||
6805                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6806         {
6807           ToggleLightSwitch(x, y + 1);
6808         }
6809         else
6810         {
6811           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6812
6813           CheckElementChangeBySide(x, y + 1, smashed, element,
6814                                    CE_SWITCHED, CH_SIDE_TOP);
6815           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6816                                             CH_SIDE_TOP);
6817         }
6818       }
6819       else
6820       {
6821         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6822       }
6823     }
6824   }
6825
6826   // play sound of magic wall / mill
6827   if (!last_line &&
6828       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6829        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6830        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6831   {
6832     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6833       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6834     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6835       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6836     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6837       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6838
6839     return;
6840   }
6841
6842   // play sound of object that hits the ground
6843   if (last_line || object_hit)
6844     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6845 }
6846
6847 static void TurnRoundExt(int x, int y)
6848 {
6849   static struct
6850   {
6851     int dx, dy;
6852   } move_xy[] =
6853   {
6854     {  0,  0 },
6855     { -1,  0 },
6856     { +1,  0 },
6857     {  0,  0 },
6858     {  0, -1 },
6859     {  0,  0 }, { 0, 0 }, { 0, 0 },
6860     {  0, +1 }
6861   };
6862   static struct
6863   {
6864     int left, right, back;
6865   } turn[] =
6866   {
6867     { 0,        0,              0        },
6868     { MV_DOWN,  MV_UP,          MV_RIGHT },
6869     { MV_UP,    MV_DOWN,        MV_LEFT  },
6870     { 0,        0,              0        },
6871     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6872     { 0,        0,              0        },
6873     { 0,        0,              0        },
6874     { 0,        0,              0        },
6875     { MV_RIGHT, MV_LEFT,        MV_UP    }
6876   };
6877
6878   int element = Tile[x][y];
6879   int move_pattern = element_info[element].move_pattern;
6880
6881   int old_move_dir = MovDir[x][y];
6882   int left_dir  = turn[old_move_dir].left;
6883   int right_dir = turn[old_move_dir].right;
6884   int back_dir  = turn[old_move_dir].back;
6885
6886   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6887   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6888   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6889   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6890
6891   int left_x  = x + left_dx,  left_y  = y + left_dy;
6892   int right_x = x + right_dx, right_y = y + right_dy;
6893   int move_x  = x + move_dx,  move_y  = y + move_dy;
6894
6895   int xx, yy;
6896
6897   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6898   {
6899     TestIfBadThingTouchesOtherBadThing(x, y);
6900
6901     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6902       MovDir[x][y] = right_dir;
6903     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6904       MovDir[x][y] = left_dir;
6905
6906     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6907       MovDelay[x][y] = 9;
6908     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6909       MovDelay[x][y] = 1;
6910   }
6911   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6912   {
6913     TestIfBadThingTouchesOtherBadThing(x, y);
6914
6915     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6916       MovDir[x][y] = left_dir;
6917     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6918       MovDir[x][y] = right_dir;
6919
6920     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6921       MovDelay[x][y] = 9;
6922     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6923       MovDelay[x][y] = 1;
6924   }
6925   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6926   {
6927     TestIfBadThingTouchesOtherBadThing(x, y);
6928
6929     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6930       MovDir[x][y] = left_dir;
6931     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6932       MovDir[x][y] = right_dir;
6933
6934     if (MovDir[x][y] != old_move_dir)
6935       MovDelay[x][y] = 9;
6936   }
6937   else if (element == EL_YAMYAM)
6938   {
6939     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6940     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6941
6942     if (can_turn_left && can_turn_right)
6943       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6944     else if (can_turn_left)
6945       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6946     else if (can_turn_right)
6947       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6948     else
6949       MovDir[x][y] = back_dir;
6950
6951     MovDelay[x][y] = 16 + 16 * RND(3);
6952   }
6953   else if (element == EL_DARK_YAMYAM)
6954   {
6955     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6956                                                          left_x, left_y);
6957     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6958                                                          right_x, right_y);
6959
6960     if (can_turn_left && can_turn_right)
6961       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6962     else if (can_turn_left)
6963       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6964     else if (can_turn_right)
6965       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6966     else
6967       MovDir[x][y] = back_dir;
6968
6969     MovDelay[x][y] = 16 + 16 * RND(3);
6970   }
6971   else if (element == EL_PACMAN)
6972   {
6973     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6974     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6975
6976     if (can_turn_left && can_turn_right)
6977       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6978     else if (can_turn_left)
6979       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6980     else if (can_turn_right)
6981       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6982     else
6983       MovDir[x][y] = back_dir;
6984
6985     MovDelay[x][y] = 6 + RND(40);
6986   }
6987   else if (element == EL_PIG)
6988   {
6989     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6990     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6991     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6992     boolean should_turn_left, should_turn_right, should_move_on;
6993     int rnd_value = 24;
6994     int rnd = RND(rnd_value);
6995
6996     should_turn_left = (can_turn_left &&
6997                         (!can_move_on ||
6998                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6999                                                    y + back_dy + left_dy)));
7000     should_turn_right = (can_turn_right &&
7001                          (!can_move_on ||
7002                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7003                                                     y + back_dy + right_dy)));
7004     should_move_on = (can_move_on &&
7005                       (!can_turn_left ||
7006                        !can_turn_right ||
7007                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7008                                                  y + move_dy + left_dy) ||
7009                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7010                                                  y + move_dy + right_dy)));
7011
7012     if (should_turn_left || should_turn_right || should_move_on)
7013     {
7014       if (should_turn_left && should_turn_right && should_move_on)
7015         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7016                         rnd < 2 * rnd_value / 3 ? right_dir :
7017                         old_move_dir);
7018       else if (should_turn_left && should_turn_right)
7019         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7020       else if (should_turn_left && should_move_on)
7021         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7022       else if (should_turn_right && should_move_on)
7023         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7024       else if (should_turn_left)
7025         MovDir[x][y] = left_dir;
7026       else if (should_turn_right)
7027         MovDir[x][y] = right_dir;
7028       else if (should_move_on)
7029         MovDir[x][y] = old_move_dir;
7030     }
7031     else if (can_move_on && rnd > rnd_value / 8)
7032       MovDir[x][y] = old_move_dir;
7033     else if (can_turn_left && can_turn_right)
7034       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7035     else if (can_turn_left && rnd > rnd_value / 8)
7036       MovDir[x][y] = left_dir;
7037     else if (can_turn_right && rnd > rnd_value/8)
7038       MovDir[x][y] = right_dir;
7039     else
7040       MovDir[x][y] = back_dir;
7041
7042     xx = x + move_xy[MovDir[x][y]].dx;
7043     yy = y + move_xy[MovDir[x][y]].dy;
7044
7045     if (!IN_LEV_FIELD(xx, yy) ||
7046         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7047       MovDir[x][y] = old_move_dir;
7048
7049     MovDelay[x][y] = 0;
7050   }
7051   else if (element == EL_DRAGON)
7052   {
7053     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7054     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7055     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7056     int rnd_value = 24;
7057     int rnd = RND(rnd_value);
7058
7059     if (can_move_on && rnd > rnd_value / 8)
7060       MovDir[x][y] = old_move_dir;
7061     else if (can_turn_left && can_turn_right)
7062       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7063     else if (can_turn_left && rnd > rnd_value / 8)
7064       MovDir[x][y] = left_dir;
7065     else if (can_turn_right && rnd > rnd_value / 8)
7066       MovDir[x][y] = right_dir;
7067     else
7068       MovDir[x][y] = back_dir;
7069
7070     xx = x + move_xy[MovDir[x][y]].dx;
7071     yy = y + move_xy[MovDir[x][y]].dy;
7072
7073     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7074       MovDir[x][y] = old_move_dir;
7075
7076     MovDelay[x][y] = 0;
7077   }
7078   else if (element == EL_MOLE)
7079   {
7080     boolean can_move_on =
7081       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7082                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7083                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7084     if (!can_move_on)
7085     {
7086       boolean can_turn_left =
7087         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7088                               IS_AMOEBOID(Tile[left_x][left_y])));
7089
7090       boolean can_turn_right =
7091         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7092                               IS_AMOEBOID(Tile[right_x][right_y])));
7093
7094       if (can_turn_left && can_turn_right)
7095         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7096       else if (can_turn_left)
7097         MovDir[x][y] = left_dir;
7098       else
7099         MovDir[x][y] = right_dir;
7100     }
7101
7102     if (MovDir[x][y] != old_move_dir)
7103       MovDelay[x][y] = 9;
7104   }
7105   else if (element == EL_BALLOON)
7106   {
7107     MovDir[x][y] = game.wind_direction;
7108     MovDelay[x][y] = 0;
7109   }
7110   else if (element == EL_SPRING)
7111   {
7112     if (MovDir[x][y] & MV_HORIZONTAL)
7113     {
7114       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7115           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7116       {
7117         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7118         ResetGfxAnimation(move_x, move_y);
7119         TEST_DrawLevelField(move_x, move_y);
7120
7121         MovDir[x][y] = back_dir;
7122       }
7123       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7124                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7125         MovDir[x][y] = MV_NONE;
7126     }
7127
7128     MovDelay[x][y] = 0;
7129   }
7130   else if (element == EL_ROBOT ||
7131            element == EL_SATELLITE ||
7132            element == EL_PENGUIN ||
7133            element == EL_EMC_ANDROID)
7134   {
7135     int attr_x = -1, attr_y = -1;
7136
7137     if (game.all_players_gone)
7138     {
7139       attr_x = game.exit_x;
7140       attr_y = game.exit_y;
7141     }
7142     else
7143     {
7144       int i;
7145
7146       for (i = 0; i < MAX_PLAYERS; i++)
7147       {
7148         struct PlayerInfo *player = &stored_player[i];
7149         int jx = player->jx, jy = player->jy;
7150
7151         if (!player->active)
7152           continue;
7153
7154         if (attr_x == -1 ||
7155             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7156         {
7157           attr_x = jx;
7158           attr_y = jy;
7159         }
7160       }
7161     }
7162
7163     if (element == EL_ROBOT &&
7164         game.robot_wheel_x >= 0 &&
7165         game.robot_wheel_y >= 0 &&
7166         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7167          game.engine_version < VERSION_IDENT(3,1,0,0)))
7168     {
7169       attr_x = game.robot_wheel_x;
7170       attr_y = game.robot_wheel_y;
7171     }
7172
7173     if (element == EL_PENGUIN)
7174     {
7175       int i;
7176       static int xy[4][2] =
7177       {
7178         { 0, -1 },
7179         { -1, 0 },
7180         { +1, 0 },
7181         { 0, +1 }
7182       };
7183
7184       for (i = 0; i < NUM_DIRECTIONS; i++)
7185       {
7186         int ex = x + xy[i][0];
7187         int ey = y + xy[i][1];
7188
7189         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7190                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7191                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7192                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7193         {
7194           attr_x = ex;
7195           attr_y = ey;
7196           break;
7197         }
7198       }
7199     }
7200
7201     MovDir[x][y] = MV_NONE;
7202     if (attr_x < x)
7203       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7204     else if (attr_x > x)
7205       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7206     if (attr_y < y)
7207       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7208     else if (attr_y > y)
7209       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7210
7211     if (element == EL_ROBOT)
7212     {
7213       int newx, newy;
7214
7215       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7216         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7217       Moving2Blocked(x, y, &newx, &newy);
7218
7219       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7220         MovDelay[x][y] = 8 + 8 * !RND(3);
7221       else
7222         MovDelay[x][y] = 16;
7223     }
7224     else if (element == EL_PENGUIN)
7225     {
7226       int newx, newy;
7227
7228       MovDelay[x][y] = 1;
7229
7230       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7231       {
7232         boolean first_horiz = RND(2);
7233         int new_move_dir = MovDir[x][y];
7234
7235         MovDir[x][y] =
7236           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7237         Moving2Blocked(x, y, &newx, &newy);
7238
7239         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7240           return;
7241
7242         MovDir[x][y] =
7243           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7244         Moving2Blocked(x, y, &newx, &newy);
7245
7246         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7247           return;
7248
7249         MovDir[x][y] = old_move_dir;
7250         return;
7251       }
7252     }
7253     else if (element == EL_SATELLITE)
7254     {
7255       int newx, newy;
7256
7257       MovDelay[x][y] = 1;
7258
7259       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7260       {
7261         boolean first_horiz = RND(2);
7262         int new_move_dir = MovDir[x][y];
7263
7264         MovDir[x][y] =
7265           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7266         Moving2Blocked(x, y, &newx, &newy);
7267
7268         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7269           return;
7270
7271         MovDir[x][y] =
7272           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7273         Moving2Blocked(x, y, &newx, &newy);
7274
7275         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7276           return;
7277
7278         MovDir[x][y] = old_move_dir;
7279         return;
7280       }
7281     }
7282     else if (element == EL_EMC_ANDROID)
7283     {
7284       static int check_pos[16] =
7285       {
7286         -1,             //  0 => (invalid)
7287         7,              //  1 => MV_LEFT
7288         3,              //  2 => MV_RIGHT
7289         -1,             //  3 => (invalid)
7290         1,              //  4 =>            MV_UP
7291         0,              //  5 => MV_LEFT  | MV_UP
7292         2,              //  6 => MV_RIGHT | MV_UP
7293         -1,             //  7 => (invalid)
7294         5,              //  8 =>            MV_DOWN
7295         6,              //  9 => MV_LEFT  | MV_DOWN
7296         4,              // 10 => MV_RIGHT | MV_DOWN
7297         -1,             // 11 => (invalid)
7298         -1,             // 12 => (invalid)
7299         -1,             // 13 => (invalid)
7300         -1,             // 14 => (invalid)
7301         -1,             // 15 => (invalid)
7302       };
7303       static struct
7304       {
7305         int dx, dy;
7306         int dir;
7307       } check_xy[8] =
7308       {
7309         { -1, -1,       MV_LEFT  | MV_UP   },
7310         {  0, -1,                  MV_UP   },
7311         { +1, -1,       MV_RIGHT | MV_UP   },
7312         { +1,  0,       MV_RIGHT           },
7313         { +1, +1,       MV_RIGHT | MV_DOWN },
7314         {  0, +1,                  MV_DOWN },
7315         { -1, +1,       MV_LEFT  | MV_DOWN },
7316         { -1,  0,       MV_LEFT            },
7317       };
7318       int start_pos, check_order;
7319       boolean can_clone = FALSE;
7320       int i;
7321
7322       // check if there is any free field around current position
7323       for (i = 0; i < 8; i++)
7324       {
7325         int newx = x + check_xy[i].dx;
7326         int newy = y + check_xy[i].dy;
7327
7328         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7329         {
7330           can_clone = TRUE;
7331
7332           break;
7333         }
7334       }
7335
7336       if (can_clone)            // randomly find an element to clone
7337       {
7338         can_clone = FALSE;
7339
7340         start_pos = check_pos[RND(8)];
7341         check_order = (RND(2) ? -1 : +1);
7342
7343         for (i = 0; i < 8; i++)
7344         {
7345           int pos_raw = start_pos + i * check_order;
7346           int pos = (pos_raw + 8) % 8;
7347           int newx = x + check_xy[pos].dx;
7348           int newy = y + check_xy[pos].dy;
7349
7350           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7351           {
7352             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7353             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7354
7355             Store[x][y] = Tile[newx][newy];
7356
7357             can_clone = TRUE;
7358
7359             break;
7360           }
7361         }
7362       }
7363
7364       if (can_clone)            // randomly find a direction to move
7365       {
7366         can_clone = FALSE;
7367
7368         start_pos = check_pos[RND(8)];
7369         check_order = (RND(2) ? -1 : +1);
7370
7371         for (i = 0; i < 8; i++)
7372         {
7373           int pos_raw = start_pos + i * check_order;
7374           int pos = (pos_raw + 8) % 8;
7375           int newx = x + check_xy[pos].dx;
7376           int newy = y + check_xy[pos].dy;
7377           int new_move_dir = check_xy[pos].dir;
7378
7379           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7380           {
7381             MovDir[x][y] = new_move_dir;
7382             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7383
7384             can_clone = TRUE;
7385
7386             break;
7387           }
7388         }
7389       }
7390
7391       if (can_clone)            // cloning and moving successful
7392         return;
7393
7394       // cannot clone -- try to move towards player
7395
7396       start_pos = check_pos[MovDir[x][y] & 0x0f];
7397       check_order = (RND(2) ? -1 : +1);
7398
7399       for (i = 0; i < 3; i++)
7400       {
7401         // first check start_pos, then previous/next or (next/previous) pos
7402         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7403         int pos = (pos_raw + 8) % 8;
7404         int newx = x + check_xy[pos].dx;
7405         int newy = y + check_xy[pos].dy;
7406         int new_move_dir = check_xy[pos].dir;
7407
7408         if (IS_PLAYER(newx, newy))
7409           break;
7410
7411         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7412         {
7413           MovDir[x][y] = new_move_dir;
7414           MovDelay[x][y] = level.android_move_time * 8 + 1;
7415
7416           break;
7417         }
7418       }
7419     }
7420   }
7421   else if (move_pattern == MV_TURNING_LEFT ||
7422            move_pattern == MV_TURNING_RIGHT ||
7423            move_pattern == MV_TURNING_LEFT_RIGHT ||
7424            move_pattern == MV_TURNING_RIGHT_LEFT ||
7425            move_pattern == MV_TURNING_RANDOM ||
7426            move_pattern == MV_ALL_DIRECTIONS)
7427   {
7428     boolean can_turn_left =
7429       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7430     boolean can_turn_right =
7431       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7432
7433     if (element_info[element].move_stepsize == 0)       // "not moving"
7434       return;
7435
7436     if (move_pattern == MV_TURNING_LEFT)
7437       MovDir[x][y] = left_dir;
7438     else if (move_pattern == MV_TURNING_RIGHT)
7439       MovDir[x][y] = right_dir;
7440     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7441       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7442     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7443       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7444     else if (move_pattern == MV_TURNING_RANDOM)
7445       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7446                       can_turn_right && !can_turn_left ? right_dir :
7447                       RND(2) ? left_dir : right_dir);
7448     else if (can_turn_left && can_turn_right)
7449       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7450     else if (can_turn_left)
7451       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7452     else if (can_turn_right)
7453       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7454     else
7455       MovDir[x][y] = back_dir;
7456
7457     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7458   }
7459   else if (move_pattern == MV_HORIZONTAL ||
7460            move_pattern == MV_VERTICAL)
7461   {
7462     if (move_pattern & old_move_dir)
7463       MovDir[x][y] = back_dir;
7464     else if (move_pattern == MV_HORIZONTAL)
7465       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7466     else if (move_pattern == MV_VERTICAL)
7467       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7468
7469     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7470   }
7471   else if (move_pattern & MV_ANY_DIRECTION)
7472   {
7473     MovDir[x][y] = move_pattern;
7474     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7475   }
7476   else if (move_pattern & MV_WIND_DIRECTION)
7477   {
7478     MovDir[x][y] = game.wind_direction;
7479     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7480   }
7481   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7482   {
7483     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7484       MovDir[x][y] = left_dir;
7485     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7486       MovDir[x][y] = right_dir;
7487
7488     if (MovDir[x][y] != old_move_dir)
7489       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7490   }
7491   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7492   {
7493     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7494       MovDir[x][y] = right_dir;
7495     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7496       MovDir[x][y] = left_dir;
7497
7498     if (MovDir[x][y] != old_move_dir)
7499       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7500   }
7501   else if (move_pattern == MV_TOWARDS_PLAYER ||
7502            move_pattern == MV_AWAY_FROM_PLAYER)
7503   {
7504     int attr_x = -1, attr_y = -1;
7505     int newx, newy;
7506     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7507
7508     if (game.all_players_gone)
7509     {
7510       attr_x = game.exit_x;
7511       attr_y = game.exit_y;
7512     }
7513     else
7514     {
7515       int i;
7516
7517       for (i = 0; i < MAX_PLAYERS; i++)
7518       {
7519         struct PlayerInfo *player = &stored_player[i];
7520         int jx = player->jx, jy = player->jy;
7521
7522         if (!player->active)
7523           continue;
7524
7525         if (attr_x == -1 ||
7526             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7527         {
7528           attr_x = jx;
7529           attr_y = jy;
7530         }
7531       }
7532     }
7533
7534     MovDir[x][y] = MV_NONE;
7535     if (attr_x < x)
7536       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7537     else if (attr_x > x)
7538       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7539     if (attr_y < y)
7540       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7541     else if (attr_y > y)
7542       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7543
7544     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7545
7546     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7547     {
7548       boolean first_horiz = RND(2);
7549       int new_move_dir = MovDir[x][y];
7550
7551       if (element_info[element].move_stepsize == 0)     // "not moving"
7552       {
7553         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7554         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7555
7556         return;
7557       }
7558
7559       MovDir[x][y] =
7560         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7561       Moving2Blocked(x, y, &newx, &newy);
7562
7563       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7564         return;
7565
7566       MovDir[x][y] =
7567         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7568       Moving2Blocked(x, y, &newx, &newy);
7569
7570       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7571         return;
7572
7573       MovDir[x][y] = old_move_dir;
7574     }
7575   }
7576   else if (move_pattern == MV_WHEN_PUSHED ||
7577            move_pattern == MV_WHEN_DROPPED)
7578   {
7579     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7580       MovDir[x][y] = MV_NONE;
7581
7582     MovDelay[x][y] = 0;
7583   }
7584   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7585   {
7586     static int test_xy[7][2] =
7587     {
7588       { 0, -1 },
7589       { -1, 0 },
7590       { +1, 0 },
7591       { 0, +1 },
7592       { 0, -1 },
7593       { -1, 0 },
7594       { +1, 0 },
7595     };
7596     static int test_dir[7] =
7597     {
7598       MV_UP,
7599       MV_LEFT,
7600       MV_RIGHT,
7601       MV_DOWN,
7602       MV_UP,
7603       MV_LEFT,
7604       MV_RIGHT,
7605     };
7606     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7607     int move_preference = -1000000;     // start with very low preference
7608     int new_move_dir = MV_NONE;
7609     int start_test = RND(4);
7610     int i;
7611
7612     for (i = 0; i < NUM_DIRECTIONS; i++)
7613     {
7614       int move_dir = test_dir[start_test + i];
7615       int move_dir_preference;
7616
7617       xx = x + test_xy[start_test + i][0];
7618       yy = y + test_xy[start_test + i][1];
7619
7620       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7621           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7622       {
7623         new_move_dir = move_dir;
7624
7625         break;
7626       }
7627
7628       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7629         continue;
7630
7631       move_dir_preference = -1 * RunnerVisit[xx][yy];
7632       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7633         move_dir_preference = PlayerVisit[xx][yy];
7634
7635       if (move_dir_preference > move_preference)
7636       {
7637         // prefer field that has not been visited for the longest time
7638         move_preference = move_dir_preference;
7639         new_move_dir = move_dir;
7640       }
7641       else if (move_dir_preference == move_preference &&
7642                move_dir == old_move_dir)
7643       {
7644         // prefer last direction when all directions are preferred equally
7645         move_preference = move_dir_preference;
7646         new_move_dir = move_dir;
7647       }
7648     }
7649
7650     MovDir[x][y] = new_move_dir;
7651     if (old_move_dir != new_move_dir)
7652       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7653   }
7654 }
7655
7656 static void TurnRound(int x, int y)
7657 {
7658   int direction = MovDir[x][y];
7659
7660   TurnRoundExt(x, y);
7661
7662   GfxDir[x][y] = MovDir[x][y];
7663
7664   if (direction != MovDir[x][y])
7665     GfxFrame[x][y] = 0;
7666
7667   if (MovDelay[x][y])
7668     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7669
7670   ResetGfxFrame(x, y);
7671 }
7672
7673 static boolean JustBeingPushed(int x, int y)
7674 {
7675   int i;
7676
7677   for (i = 0; i < MAX_PLAYERS; i++)
7678   {
7679     struct PlayerInfo *player = &stored_player[i];
7680
7681     if (player->active && player->is_pushing && player->MovPos)
7682     {
7683       int next_jx = player->jx + (player->jx - player->last_jx);
7684       int next_jy = player->jy + (player->jy - player->last_jy);
7685
7686       if (x == next_jx && y == next_jy)
7687         return TRUE;
7688     }
7689   }
7690
7691   return FALSE;
7692 }
7693
7694 static void StartMoving(int x, int y)
7695 {
7696   boolean started_moving = FALSE;       // some elements can fall _and_ move
7697   int element = Tile[x][y];
7698
7699   if (Stop[x][y])
7700     return;
7701
7702   if (MovDelay[x][y] == 0)
7703     GfxAction[x][y] = ACTION_DEFAULT;
7704
7705   if (CAN_FALL(element) && y < lev_fieldy - 1)
7706   {
7707     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7708         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7709       if (JustBeingPushed(x, y))
7710         return;
7711
7712     if (element == EL_QUICKSAND_FULL)
7713     {
7714       if (IS_FREE(x, y + 1))
7715       {
7716         InitMovingField(x, y, MV_DOWN);
7717         started_moving = TRUE;
7718
7719         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7720 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7721         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7722           Store[x][y] = EL_ROCK;
7723 #else
7724         Store[x][y] = EL_ROCK;
7725 #endif
7726
7727         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7728       }
7729       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7730       {
7731         if (!MovDelay[x][y])
7732         {
7733           MovDelay[x][y] = TILEY + 1;
7734
7735           ResetGfxAnimation(x, y);
7736           ResetGfxAnimation(x, y + 1);
7737         }
7738
7739         if (MovDelay[x][y])
7740         {
7741           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7742           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7743
7744           MovDelay[x][y]--;
7745           if (MovDelay[x][y])
7746             return;
7747         }
7748
7749         Tile[x][y] = EL_QUICKSAND_EMPTY;
7750         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7751         Store[x][y + 1] = Store[x][y];
7752         Store[x][y] = 0;
7753
7754         PlayLevelSoundAction(x, y, ACTION_FILLING);
7755       }
7756       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7757       {
7758         if (!MovDelay[x][y])
7759         {
7760           MovDelay[x][y] = TILEY + 1;
7761
7762           ResetGfxAnimation(x, y);
7763           ResetGfxAnimation(x, y + 1);
7764         }
7765
7766         if (MovDelay[x][y])
7767         {
7768           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7769           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7770
7771           MovDelay[x][y]--;
7772           if (MovDelay[x][y])
7773             return;
7774         }
7775
7776         Tile[x][y] = EL_QUICKSAND_EMPTY;
7777         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7778         Store[x][y + 1] = Store[x][y];
7779         Store[x][y] = 0;
7780
7781         PlayLevelSoundAction(x, y, ACTION_FILLING);
7782       }
7783     }
7784     else if (element == EL_QUICKSAND_FAST_FULL)
7785     {
7786       if (IS_FREE(x, y + 1))
7787       {
7788         InitMovingField(x, y, MV_DOWN);
7789         started_moving = TRUE;
7790
7791         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7792 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7793         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7794           Store[x][y] = EL_ROCK;
7795 #else
7796         Store[x][y] = EL_ROCK;
7797 #endif
7798
7799         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7800       }
7801       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7802       {
7803         if (!MovDelay[x][y])
7804         {
7805           MovDelay[x][y] = TILEY + 1;
7806
7807           ResetGfxAnimation(x, y);
7808           ResetGfxAnimation(x, y + 1);
7809         }
7810
7811         if (MovDelay[x][y])
7812         {
7813           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7814           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7815
7816           MovDelay[x][y]--;
7817           if (MovDelay[x][y])
7818             return;
7819         }
7820
7821         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7822         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7823         Store[x][y + 1] = Store[x][y];
7824         Store[x][y] = 0;
7825
7826         PlayLevelSoundAction(x, y, ACTION_FILLING);
7827       }
7828       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7829       {
7830         if (!MovDelay[x][y])
7831         {
7832           MovDelay[x][y] = TILEY + 1;
7833
7834           ResetGfxAnimation(x, y);
7835           ResetGfxAnimation(x, y + 1);
7836         }
7837
7838         if (MovDelay[x][y])
7839         {
7840           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7841           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7842
7843           MovDelay[x][y]--;
7844           if (MovDelay[x][y])
7845             return;
7846         }
7847
7848         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7849         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7850         Store[x][y + 1] = Store[x][y];
7851         Store[x][y] = 0;
7852
7853         PlayLevelSoundAction(x, y, ACTION_FILLING);
7854       }
7855     }
7856     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7857              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7858     {
7859       InitMovingField(x, y, MV_DOWN);
7860       started_moving = TRUE;
7861
7862       Tile[x][y] = EL_QUICKSAND_FILLING;
7863       Store[x][y] = element;
7864
7865       PlayLevelSoundAction(x, y, ACTION_FILLING);
7866     }
7867     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7868              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7869     {
7870       InitMovingField(x, y, MV_DOWN);
7871       started_moving = TRUE;
7872
7873       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7874       Store[x][y] = element;
7875
7876       PlayLevelSoundAction(x, y, ACTION_FILLING);
7877     }
7878     else if (element == EL_MAGIC_WALL_FULL)
7879     {
7880       if (IS_FREE(x, y + 1))
7881       {
7882         InitMovingField(x, y, MV_DOWN);
7883         started_moving = TRUE;
7884
7885         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7886         Store[x][y] = EL_CHANGED(Store[x][y]);
7887       }
7888       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7889       {
7890         if (!MovDelay[x][y])
7891           MovDelay[x][y] = TILEY / 4 + 1;
7892
7893         if (MovDelay[x][y])
7894         {
7895           MovDelay[x][y]--;
7896           if (MovDelay[x][y])
7897             return;
7898         }
7899
7900         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7901         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7902         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7903         Store[x][y] = 0;
7904       }
7905     }
7906     else if (element == EL_BD_MAGIC_WALL_FULL)
7907     {
7908       if (IS_FREE(x, y + 1))
7909       {
7910         InitMovingField(x, y, MV_DOWN);
7911         started_moving = TRUE;
7912
7913         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7914         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7915       }
7916       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7917       {
7918         if (!MovDelay[x][y])
7919           MovDelay[x][y] = TILEY / 4 + 1;
7920
7921         if (MovDelay[x][y])
7922         {
7923           MovDelay[x][y]--;
7924           if (MovDelay[x][y])
7925             return;
7926         }
7927
7928         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7929         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7930         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7931         Store[x][y] = 0;
7932       }
7933     }
7934     else if (element == EL_DC_MAGIC_WALL_FULL)
7935     {
7936       if (IS_FREE(x, y + 1))
7937       {
7938         InitMovingField(x, y, MV_DOWN);
7939         started_moving = TRUE;
7940
7941         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7942         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7943       }
7944       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7945       {
7946         if (!MovDelay[x][y])
7947           MovDelay[x][y] = TILEY / 4 + 1;
7948
7949         if (MovDelay[x][y])
7950         {
7951           MovDelay[x][y]--;
7952           if (MovDelay[x][y])
7953             return;
7954         }
7955
7956         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7957         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7958         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7959         Store[x][y] = 0;
7960       }
7961     }
7962     else if ((CAN_PASS_MAGIC_WALL(element) &&
7963               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7964                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7965              (CAN_PASS_DC_MAGIC_WALL(element) &&
7966               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7967
7968     {
7969       InitMovingField(x, y, MV_DOWN);
7970       started_moving = TRUE;
7971
7972       Tile[x][y] =
7973         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7974          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7975          EL_DC_MAGIC_WALL_FILLING);
7976       Store[x][y] = element;
7977     }
7978     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7979     {
7980       SplashAcid(x, y + 1);
7981
7982       InitMovingField(x, y, MV_DOWN);
7983       started_moving = TRUE;
7984
7985       Store[x][y] = EL_ACID;
7986     }
7987     else if (
7988              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7989               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7990              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7991               CAN_FALL(element) && WasJustFalling[x][y] &&
7992               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7993
7994              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7995               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7996               (Tile[x][y + 1] == EL_BLOCKED)))
7997     {
7998       /* this is needed for a special case not covered by calling "Impact()"
7999          from "ContinueMoving()": if an element moves to a tile directly below
8000          another element which was just falling on that tile (which was empty
8001          in the previous frame), the falling element above would just stop
8002          instead of smashing the element below (in previous version, the above
8003          element was just checked for "moving" instead of "falling", resulting
8004          in incorrect smashes caused by horizontal movement of the above
8005          element; also, the case of the player being the element to smash was
8006          simply not covered here... :-/ ) */
8007
8008       CheckCollision[x][y] = 0;
8009       CheckImpact[x][y] = 0;
8010
8011       Impact(x, y);
8012     }
8013     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8014     {
8015       if (MovDir[x][y] == MV_NONE)
8016       {
8017         InitMovingField(x, y, MV_DOWN);
8018         started_moving = TRUE;
8019       }
8020     }
8021     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8022     {
8023       if (WasJustFalling[x][y]) // prevent animation from being restarted
8024         MovDir[x][y] = MV_DOWN;
8025
8026       InitMovingField(x, y, MV_DOWN);
8027       started_moving = TRUE;
8028     }
8029     else if (element == EL_AMOEBA_DROP)
8030     {
8031       Tile[x][y] = EL_AMOEBA_GROWING;
8032       Store[x][y] = EL_AMOEBA_WET;
8033     }
8034     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8035               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8036              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8037              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8038     {
8039       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8040                                 (IS_FREE(x - 1, y + 1) ||
8041                                  Tile[x - 1][y + 1] == EL_ACID));
8042       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8043                                 (IS_FREE(x + 1, y + 1) ||
8044                                  Tile[x + 1][y + 1] == EL_ACID));
8045       boolean can_fall_any  = (can_fall_left || can_fall_right);
8046       boolean can_fall_both = (can_fall_left && can_fall_right);
8047       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8048
8049       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8050       {
8051         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8052           can_fall_right = FALSE;
8053         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8054           can_fall_left = FALSE;
8055         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8056           can_fall_right = FALSE;
8057         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8058           can_fall_left = FALSE;
8059
8060         can_fall_any  = (can_fall_left || can_fall_right);
8061         can_fall_both = FALSE;
8062       }
8063
8064       if (can_fall_both)
8065       {
8066         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8067           can_fall_right = FALSE;       // slip down on left side
8068         else
8069           can_fall_left = !(can_fall_right = RND(2));
8070
8071         can_fall_both = FALSE;
8072       }
8073
8074       if (can_fall_any)
8075       {
8076         // if not determined otherwise, prefer left side for slipping down
8077         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8078         started_moving = TRUE;
8079       }
8080     }
8081     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8082     {
8083       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8084       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8085       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8086       int belt_dir = game.belt_dir[belt_nr];
8087
8088       if ((belt_dir == MV_LEFT  && left_is_free) ||
8089           (belt_dir == MV_RIGHT && right_is_free))
8090       {
8091         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8092
8093         InitMovingField(x, y, belt_dir);
8094         started_moving = TRUE;
8095
8096         Pushed[x][y] = TRUE;
8097         Pushed[nextx][y] = TRUE;
8098
8099         GfxAction[x][y] = ACTION_DEFAULT;
8100       }
8101       else
8102       {
8103         MovDir[x][y] = 0;       // if element was moving, stop it
8104       }
8105     }
8106   }
8107
8108   // not "else if" because of elements that can fall and move (EL_SPRING)
8109   if (CAN_MOVE(element) && !started_moving)
8110   {
8111     int move_pattern = element_info[element].move_pattern;
8112     int newx, newy;
8113
8114     Moving2Blocked(x, y, &newx, &newy);
8115
8116     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8117       return;
8118
8119     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8120         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8121     {
8122       WasJustMoving[x][y] = 0;
8123       CheckCollision[x][y] = 0;
8124
8125       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8126
8127       if (Tile[x][y] != element)        // element has changed
8128         return;
8129     }
8130
8131     if (!MovDelay[x][y])        // start new movement phase
8132     {
8133       // all objects that can change their move direction after each step
8134       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8135
8136       if (element != EL_YAMYAM &&
8137           element != EL_DARK_YAMYAM &&
8138           element != EL_PACMAN &&
8139           !(move_pattern & MV_ANY_DIRECTION) &&
8140           move_pattern != MV_TURNING_LEFT &&
8141           move_pattern != MV_TURNING_RIGHT &&
8142           move_pattern != MV_TURNING_LEFT_RIGHT &&
8143           move_pattern != MV_TURNING_RIGHT_LEFT &&
8144           move_pattern != MV_TURNING_RANDOM)
8145       {
8146         TurnRound(x, y);
8147
8148         if (MovDelay[x][y] && (element == EL_BUG ||
8149                                element == EL_SPACESHIP ||
8150                                element == EL_SP_SNIKSNAK ||
8151                                element == EL_SP_ELECTRON ||
8152                                element == EL_MOLE))
8153           TEST_DrawLevelField(x, y);
8154       }
8155     }
8156
8157     if (MovDelay[x][y])         // wait some time before next movement
8158     {
8159       MovDelay[x][y]--;
8160
8161       if (element == EL_ROBOT ||
8162           element == EL_YAMYAM ||
8163           element == EL_DARK_YAMYAM)
8164       {
8165         DrawLevelElementAnimationIfNeeded(x, y, element);
8166         PlayLevelSoundAction(x, y, ACTION_WAITING);
8167       }
8168       else if (element == EL_SP_ELECTRON)
8169         DrawLevelElementAnimationIfNeeded(x, y, element);
8170       else if (element == EL_DRAGON)
8171       {
8172         int i;
8173         int dir = MovDir[x][y];
8174         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8175         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8176         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8177                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8178                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8179                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8180         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8181
8182         GfxAction[x][y] = ACTION_ATTACKING;
8183
8184         if (IS_PLAYER(x, y))
8185           DrawPlayerField(x, y);
8186         else
8187           TEST_DrawLevelField(x, y);
8188
8189         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8190
8191         for (i = 1; i <= 3; i++)
8192         {
8193           int xx = x + i * dx;
8194           int yy = y + i * dy;
8195           int sx = SCREENX(xx);
8196           int sy = SCREENY(yy);
8197           int flame_graphic = graphic + (i - 1);
8198
8199           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8200             break;
8201
8202           if (MovDelay[x][y])
8203           {
8204             int flamed = MovingOrBlocked2Element(xx, yy);
8205
8206             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8207               Bang(xx, yy);
8208             else
8209               RemoveMovingField(xx, yy);
8210
8211             ChangeDelay[xx][yy] = 0;
8212
8213             Tile[xx][yy] = EL_FLAMES;
8214
8215             if (IN_SCR_FIELD(sx, sy))
8216             {
8217               TEST_DrawLevelFieldCrumbled(xx, yy);
8218               DrawGraphic(sx, sy, flame_graphic, frame);
8219             }
8220           }
8221           else
8222           {
8223             if (Tile[xx][yy] == EL_FLAMES)
8224               Tile[xx][yy] = EL_EMPTY;
8225             TEST_DrawLevelField(xx, yy);
8226           }
8227         }
8228       }
8229
8230       if (MovDelay[x][y])       // element still has to wait some time
8231       {
8232         PlayLevelSoundAction(x, y, ACTION_WAITING);
8233
8234         return;
8235       }
8236     }
8237
8238     // now make next step
8239
8240     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8241
8242     if (DONT_COLLIDE_WITH(element) &&
8243         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8244         !PLAYER_ENEMY_PROTECTED(newx, newy))
8245     {
8246       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8247
8248       return;
8249     }
8250
8251     else if (CAN_MOVE_INTO_ACID(element) &&
8252              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8253              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8254              (MovDir[x][y] == MV_DOWN ||
8255               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8256     {
8257       SplashAcid(newx, newy);
8258       Store[x][y] = EL_ACID;
8259     }
8260     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8261     {
8262       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8263           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8264           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8265           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8266       {
8267         RemoveField(x, y);
8268         TEST_DrawLevelField(x, y);
8269
8270         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8271         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8272           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8273
8274         game.friends_still_needed--;
8275         if (!game.friends_still_needed &&
8276             !game.GameOver &&
8277             game.all_players_gone)
8278           LevelSolved();
8279
8280         return;
8281       }
8282       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8283       {
8284         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8285           TEST_DrawLevelField(newx, newy);
8286         else
8287           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8288       }
8289       else if (!IS_FREE(newx, newy))
8290       {
8291         GfxAction[x][y] = ACTION_WAITING;
8292
8293         if (IS_PLAYER(x, y))
8294           DrawPlayerField(x, y);
8295         else
8296           TEST_DrawLevelField(x, y);
8297
8298         return;
8299       }
8300     }
8301     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8302     {
8303       if (IS_FOOD_PIG(Tile[newx][newy]))
8304       {
8305         if (IS_MOVING(newx, newy))
8306           RemoveMovingField(newx, newy);
8307         else
8308         {
8309           Tile[newx][newy] = EL_EMPTY;
8310           TEST_DrawLevelField(newx, newy);
8311         }
8312
8313         PlayLevelSound(x, y, SND_PIG_DIGGING);
8314       }
8315       else if (!IS_FREE(newx, newy))
8316       {
8317         if (IS_PLAYER(x, y))
8318           DrawPlayerField(x, y);
8319         else
8320           TEST_DrawLevelField(x, y);
8321
8322         return;
8323       }
8324     }
8325     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8326     {
8327       if (Store[x][y] != EL_EMPTY)
8328       {
8329         boolean can_clone = FALSE;
8330         int xx, yy;
8331
8332         // check if element to clone is still there
8333         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8334         {
8335           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8336           {
8337             can_clone = TRUE;
8338
8339             break;
8340           }
8341         }
8342
8343         // cannot clone or target field not free anymore -- do not clone
8344         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8345           Store[x][y] = EL_EMPTY;
8346       }
8347
8348       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8349       {
8350         if (IS_MV_DIAGONAL(MovDir[x][y]))
8351         {
8352           int diagonal_move_dir = MovDir[x][y];
8353           int stored = Store[x][y];
8354           int change_delay = 8;
8355           int graphic;
8356
8357           // android is moving diagonally
8358
8359           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8360
8361           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8362           GfxElement[x][y] = EL_EMC_ANDROID;
8363           GfxAction[x][y] = ACTION_SHRINKING;
8364           GfxDir[x][y] = diagonal_move_dir;
8365           ChangeDelay[x][y] = change_delay;
8366
8367           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8368                                    GfxDir[x][y]);
8369
8370           DrawLevelGraphicAnimation(x, y, graphic);
8371           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8372
8373           if (Tile[newx][newy] == EL_ACID)
8374           {
8375             SplashAcid(newx, newy);
8376
8377             return;
8378           }
8379
8380           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8381
8382           Store[newx][newy] = EL_EMC_ANDROID;
8383           GfxElement[newx][newy] = EL_EMC_ANDROID;
8384           GfxAction[newx][newy] = ACTION_GROWING;
8385           GfxDir[newx][newy] = diagonal_move_dir;
8386           ChangeDelay[newx][newy] = change_delay;
8387
8388           graphic = el_act_dir2img(GfxElement[newx][newy],
8389                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8390
8391           DrawLevelGraphicAnimation(newx, newy, graphic);
8392           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8393
8394           return;
8395         }
8396         else
8397         {
8398           Tile[newx][newy] = EL_EMPTY;
8399           TEST_DrawLevelField(newx, newy);
8400
8401           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8402         }
8403       }
8404       else if (!IS_FREE(newx, newy))
8405       {
8406         return;
8407       }
8408     }
8409     else if (IS_CUSTOM_ELEMENT(element) &&
8410              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8411     {
8412       if (!DigFieldByCE(newx, newy, element))
8413         return;
8414
8415       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8416       {
8417         RunnerVisit[x][y] = FrameCounter;
8418         PlayerVisit[x][y] /= 8;         // expire player visit path
8419       }
8420     }
8421     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8422     {
8423       if (!IS_FREE(newx, newy))
8424       {
8425         if (IS_PLAYER(x, y))
8426           DrawPlayerField(x, y);
8427         else
8428           TEST_DrawLevelField(x, y);
8429
8430         return;
8431       }
8432       else
8433       {
8434         boolean wanna_flame = !RND(10);
8435         int dx = newx - x, dy = newy - y;
8436         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8437         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8438         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8439                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8440         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8441                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8442
8443         if ((wanna_flame ||
8444              IS_CLASSIC_ENEMY(element1) ||
8445              IS_CLASSIC_ENEMY(element2)) &&
8446             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8447             element1 != EL_FLAMES && element2 != EL_FLAMES)
8448         {
8449           ResetGfxAnimation(x, y);
8450           GfxAction[x][y] = ACTION_ATTACKING;
8451
8452           if (IS_PLAYER(x, y))
8453             DrawPlayerField(x, y);
8454           else
8455             TEST_DrawLevelField(x, y);
8456
8457           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8458
8459           MovDelay[x][y] = 50;
8460
8461           Tile[newx][newy] = EL_FLAMES;
8462           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8463             Tile[newx1][newy1] = EL_FLAMES;
8464           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8465             Tile[newx2][newy2] = EL_FLAMES;
8466
8467           return;
8468         }
8469       }
8470     }
8471     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8472              Tile[newx][newy] == EL_DIAMOND)
8473     {
8474       if (IS_MOVING(newx, newy))
8475         RemoveMovingField(newx, newy);
8476       else
8477       {
8478         Tile[newx][newy] = EL_EMPTY;
8479         TEST_DrawLevelField(newx, newy);
8480       }
8481
8482       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8483     }
8484     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8485              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8486     {
8487       if (AmoebaNr[newx][newy])
8488       {
8489         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8490         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8491             Tile[newx][newy] == EL_BD_AMOEBA)
8492           AmoebaCnt[AmoebaNr[newx][newy]]--;
8493       }
8494
8495       if (IS_MOVING(newx, newy))
8496       {
8497         RemoveMovingField(newx, newy);
8498       }
8499       else
8500       {
8501         Tile[newx][newy] = EL_EMPTY;
8502         TEST_DrawLevelField(newx, newy);
8503       }
8504
8505       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8506     }
8507     else if ((element == EL_PACMAN || element == EL_MOLE)
8508              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8509     {
8510       if (AmoebaNr[newx][newy])
8511       {
8512         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8513         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8514             Tile[newx][newy] == EL_BD_AMOEBA)
8515           AmoebaCnt[AmoebaNr[newx][newy]]--;
8516       }
8517
8518       if (element == EL_MOLE)
8519       {
8520         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8521         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8522
8523         ResetGfxAnimation(x, y);
8524         GfxAction[x][y] = ACTION_DIGGING;
8525         TEST_DrawLevelField(x, y);
8526
8527         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8528
8529         return;                         // wait for shrinking amoeba
8530       }
8531       else      // element == EL_PACMAN
8532       {
8533         Tile[newx][newy] = EL_EMPTY;
8534         TEST_DrawLevelField(newx, newy);
8535         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8536       }
8537     }
8538     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8539              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8540               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8541     {
8542       // wait for shrinking amoeba to completely disappear
8543       return;
8544     }
8545     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8546     {
8547       // object was running against a wall
8548
8549       TurnRound(x, y);
8550
8551       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8552         DrawLevelElementAnimation(x, y, element);
8553
8554       if (DONT_TOUCH(element))
8555         TestIfBadThingTouchesPlayer(x, y);
8556
8557       return;
8558     }
8559
8560     InitMovingField(x, y, MovDir[x][y]);
8561
8562     PlayLevelSoundAction(x, y, ACTION_MOVING);
8563   }
8564
8565   if (MovDir[x][y])
8566     ContinueMoving(x, y);
8567 }
8568
8569 void ContinueMoving(int x, int y)
8570 {
8571   int element = Tile[x][y];
8572   struct ElementInfo *ei = &element_info[element];
8573   int direction = MovDir[x][y];
8574   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8575   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8576   int newx = x + dx, newy = y + dy;
8577   int stored = Store[x][y];
8578   int stored_new = Store[newx][newy];
8579   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8580   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8581   boolean last_line = (newy == lev_fieldy - 1);
8582   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8583
8584   if (pushed_by_player)         // special case: moving object pushed by player
8585   {
8586     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8587   }
8588   else if (use_step_delay)      // special case: moving object has step delay
8589   {
8590     if (!MovDelay[x][y])
8591       MovPos[x][y] += getElementMoveStepsize(x, y);
8592
8593     if (MovDelay[x][y])
8594       MovDelay[x][y]--;
8595     else
8596       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8597
8598     if (MovDelay[x][y])
8599     {
8600       TEST_DrawLevelField(x, y);
8601
8602       return;   // element is still waiting
8603     }
8604   }
8605   else                          // normal case: generically moving object
8606   {
8607     MovPos[x][y] += getElementMoveStepsize(x, y);
8608   }
8609
8610   if (ABS(MovPos[x][y]) < TILEX)
8611   {
8612     TEST_DrawLevelField(x, y);
8613
8614     return;     // element is still moving
8615   }
8616
8617   // element reached destination field
8618
8619   Tile[x][y] = EL_EMPTY;
8620   Tile[newx][newy] = element;
8621   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8622
8623   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8624   {
8625     element = Tile[newx][newy] = EL_ACID;
8626   }
8627   else if (element == EL_MOLE)
8628   {
8629     Tile[x][y] = EL_SAND;
8630
8631     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8632   }
8633   else if (element == EL_QUICKSAND_FILLING)
8634   {
8635     element = Tile[newx][newy] = get_next_element(element);
8636     Store[newx][newy] = Store[x][y];
8637   }
8638   else if (element == EL_QUICKSAND_EMPTYING)
8639   {
8640     Tile[x][y] = get_next_element(element);
8641     element = Tile[newx][newy] = Store[x][y];
8642   }
8643   else if (element == EL_QUICKSAND_FAST_FILLING)
8644   {
8645     element = Tile[newx][newy] = get_next_element(element);
8646     Store[newx][newy] = Store[x][y];
8647   }
8648   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8649   {
8650     Tile[x][y] = get_next_element(element);
8651     element = Tile[newx][newy] = Store[x][y];
8652   }
8653   else if (element == EL_MAGIC_WALL_FILLING)
8654   {
8655     element = Tile[newx][newy] = get_next_element(element);
8656     if (!game.magic_wall_active)
8657       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8658     Store[newx][newy] = Store[x][y];
8659   }
8660   else if (element == EL_MAGIC_WALL_EMPTYING)
8661   {
8662     Tile[x][y] = get_next_element(element);
8663     if (!game.magic_wall_active)
8664       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8665     element = Tile[newx][newy] = Store[x][y];
8666
8667     InitField(newx, newy, FALSE);
8668   }
8669   else if (element == EL_BD_MAGIC_WALL_FILLING)
8670   {
8671     element = Tile[newx][newy] = get_next_element(element);
8672     if (!game.magic_wall_active)
8673       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8674     Store[newx][newy] = Store[x][y];
8675   }
8676   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8677   {
8678     Tile[x][y] = get_next_element(element);
8679     if (!game.magic_wall_active)
8680       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8681     element = Tile[newx][newy] = Store[x][y];
8682
8683     InitField(newx, newy, FALSE);
8684   }
8685   else if (element == EL_DC_MAGIC_WALL_FILLING)
8686   {
8687     element = Tile[newx][newy] = get_next_element(element);
8688     if (!game.magic_wall_active)
8689       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8690     Store[newx][newy] = Store[x][y];
8691   }
8692   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8693   {
8694     Tile[x][y] = get_next_element(element);
8695     if (!game.magic_wall_active)
8696       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8697     element = Tile[newx][newy] = Store[x][y];
8698
8699     InitField(newx, newy, FALSE);
8700   }
8701   else if (element == EL_AMOEBA_DROPPING)
8702   {
8703     Tile[x][y] = get_next_element(element);
8704     element = Tile[newx][newy] = Store[x][y];
8705   }
8706   else if (element == EL_SOKOBAN_OBJECT)
8707   {
8708     if (Back[x][y])
8709       Tile[x][y] = Back[x][y];
8710
8711     if (Back[newx][newy])
8712       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8713
8714     Back[x][y] = Back[newx][newy] = 0;
8715   }
8716
8717   Store[x][y] = EL_EMPTY;
8718   MovPos[x][y] = 0;
8719   MovDir[x][y] = 0;
8720   MovDelay[x][y] = 0;
8721
8722   MovDelay[newx][newy] = 0;
8723
8724   if (CAN_CHANGE_OR_HAS_ACTION(element))
8725   {
8726     // copy element change control values to new field
8727     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8728     ChangePage[newx][newy]  = ChangePage[x][y];
8729     ChangeCount[newx][newy] = ChangeCount[x][y];
8730     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8731   }
8732
8733   CustomValue[newx][newy] = CustomValue[x][y];
8734
8735   ChangeDelay[x][y] = 0;
8736   ChangePage[x][y] = -1;
8737   ChangeCount[x][y] = 0;
8738   ChangeEvent[x][y] = -1;
8739
8740   CustomValue[x][y] = 0;
8741
8742   // copy animation control values to new field
8743   GfxFrame[newx][newy]  = GfxFrame[x][y];
8744   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8745   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8746   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8747
8748   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8749
8750   // some elements can leave other elements behind after moving
8751   if (ei->move_leave_element != EL_EMPTY &&
8752       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8753       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8754   {
8755     int move_leave_element = ei->move_leave_element;
8756
8757     // this makes it possible to leave the removed element again
8758     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8759       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8760
8761     Tile[x][y] = move_leave_element;
8762
8763     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8764       MovDir[x][y] = direction;
8765
8766     InitField(x, y, FALSE);
8767
8768     if (GFX_CRUMBLED(Tile[x][y]))
8769       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8770
8771     if (ELEM_IS_PLAYER(move_leave_element))
8772       RelocatePlayer(x, y, move_leave_element);
8773   }
8774
8775   // do this after checking for left-behind element
8776   ResetGfxAnimation(x, y);      // reset animation values for old field
8777
8778   if (!CAN_MOVE(element) ||
8779       (CAN_FALL(element) && direction == MV_DOWN &&
8780        (element == EL_SPRING ||
8781         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8782         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8783     GfxDir[x][y] = MovDir[newx][newy] = 0;
8784
8785   TEST_DrawLevelField(x, y);
8786   TEST_DrawLevelField(newx, newy);
8787
8788   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8789
8790   // prevent pushed element from moving on in pushed direction
8791   if (pushed_by_player && CAN_MOVE(element) &&
8792       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8793       !(element_info[element].move_pattern & direction))
8794     TurnRound(newx, newy);
8795
8796   // prevent elements on conveyor belt from moving on in last direction
8797   if (pushed_by_conveyor && CAN_FALL(element) &&
8798       direction & MV_HORIZONTAL)
8799     MovDir[newx][newy] = 0;
8800
8801   if (!pushed_by_player)
8802   {
8803     int nextx = newx + dx, nexty = newy + dy;
8804     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8805
8806     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8807
8808     if (CAN_FALL(element) && direction == MV_DOWN)
8809       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8810
8811     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8812       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8813
8814     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8815       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8816   }
8817
8818   if (DONT_TOUCH(element))      // object may be nasty to player or others
8819   {
8820     TestIfBadThingTouchesPlayer(newx, newy);
8821     TestIfBadThingTouchesFriend(newx, newy);
8822
8823     if (!IS_CUSTOM_ELEMENT(element))
8824       TestIfBadThingTouchesOtherBadThing(newx, newy);
8825   }
8826   else if (element == EL_PENGUIN)
8827     TestIfFriendTouchesBadThing(newx, newy);
8828
8829   if (DONT_GET_HIT_BY(element))
8830   {
8831     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8832   }
8833
8834   // give the player one last chance (one more frame) to move away
8835   if (CAN_FALL(element) && direction == MV_DOWN &&
8836       (last_line || (!IS_FREE(x, newy + 1) &&
8837                      (!IS_PLAYER(x, newy + 1) ||
8838                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8839     Impact(x, newy);
8840
8841   if (pushed_by_player && !game.use_change_when_pushing_bug)
8842   {
8843     int push_side = MV_DIR_OPPOSITE(direction);
8844     struct PlayerInfo *player = PLAYERINFO(x, y);
8845
8846     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8847                                player->index_bit, push_side);
8848     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8849                                         player->index_bit, push_side);
8850   }
8851
8852   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8853     MovDelay[newx][newy] = 1;
8854
8855   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8856
8857   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8858   TestIfElementHitsCustomElement(newx, newy, direction);
8859   TestIfPlayerTouchesCustomElement(newx, newy);
8860   TestIfElementTouchesCustomElement(newx, newy);
8861
8862   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8863       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8864     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8865                              MV_DIR_OPPOSITE(direction));
8866 }
8867
8868 int AmoebaNeighbourNr(int ax, int ay)
8869 {
8870   int i;
8871   int element = Tile[ax][ay];
8872   int group_nr = 0;
8873   static int xy[4][2] =
8874   {
8875     { 0, -1 },
8876     { -1, 0 },
8877     { +1, 0 },
8878     { 0, +1 }
8879   };
8880
8881   for (i = 0; i < NUM_DIRECTIONS; i++)
8882   {
8883     int x = ax + xy[i][0];
8884     int y = ay + xy[i][1];
8885
8886     if (!IN_LEV_FIELD(x, y))
8887       continue;
8888
8889     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8890       group_nr = AmoebaNr[x][y];
8891   }
8892
8893   return group_nr;
8894 }
8895
8896 static void AmoebaMerge(int ax, int ay)
8897 {
8898   int i, x, y, xx, yy;
8899   int new_group_nr = AmoebaNr[ax][ay];
8900   static int xy[4][2] =
8901   {
8902     { 0, -1 },
8903     { -1, 0 },
8904     { +1, 0 },
8905     { 0, +1 }
8906   };
8907
8908   if (new_group_nr == 0)
8909     return;
8910
8911   for (i = 0; i < NUM_DIRECTIONS; i++)
8912   {
8913     x = ax + xy[i][0];
8914     y = ay + xy[i][1];
8915
8916     if (!IN_LEV_FIELD(x, y))
8917       continue;
8918
8919     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8920          Tile[x][y] == EL_BD_AMOEBA ||
8921          Tile[x][y] == EL_AMOEBA_DEAD) &&
8922         AmoebaNr[x][y] != new_group_nr)
8923     {
8924       int old_group_nr = AmoebaNr[x][y];
8925
8926       if (old_group_nr == 0)
8927         return;
8928
8929       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8930       AmoebaCnt[old_group_nr] = 0;
8931       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8932       AmoebaCnt2[old_group_nr] = 0;
8933
8934       SCAN_PLAYFIELD(xx, yy)
8935       {
8936         if (AmoebaNr[xx][yy] == old_group_nr)
8937           AmoebaNr[xx][yy] = new_group_nr;
8938       }
8939     }
8940   }
8941 }
8942
8943 void AmoebaToDiamond(int ax, int ay)
8944 {
8945   int i, x, y;
8946
8947   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8948   {
8949     int group_nr = AmoebaNr[ax][ay];
8950
8951 #ifdef DEBUG
8952     if (group_nr == 0)
8953     {
8954       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8955       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8956
8957       return;
8958     }
8959 #endif
8960
8961     SCAN_PLAYFIELD(x, y)
8962     {
8963       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8964       {
8965         AmoebaNr[x][y] = 0;
8966         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8967       }
8968     }
8969
8970     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8971                             SND_AMOEBA_TURNING_TO_GEM :
8972                             SND_AMOEBA_TURNING_TO_ROCK));
8973     Bang(ax, ay);
8974   }
8975   else
8976   {
8977     static int xy[4][2] =
8978     {
8979       { 0, -1 },
8980       { -1, 0 },
8981       { +1, 0 },
8982       { 0, +1 }
8983     };
8984
8985     for (i = 0; i < NUM_DIRECTIONS; i++)
8986     {
8987       x = ax + xy[i][0];
8988       y = ay + xy[i][1];
8989
8990       if (!IN_LEV_FIELD(x, y))
8991         continue;
8992
8993       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8994       {
8995         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8996                               SND_AMOEBA_TURNING_TO_GEM :
8997                               SND_AMOEBA_TURNING_TO_ROCK));
8998         Bang(x, y);
8999       }
9000     }
9001   }
9002 }
9003
9004 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9005 {
9006   int x, y;
9007   int group_nr = AmoebaNr[ax][ay];
9008   boolean done = FALSE;
9009
9010 #ifdef DEBUG
9011   if (group_nr == 0)
9012   {
9013     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9014     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9015
9016     return;
9017   }
9018 #endif
9019
9020   SCAN_PLAYFIELD(x, y)
9021   {
9022     if (AmoebaNr[x][y] == group_nr &&
9023         (Tile[x][y] == EL_AMOEBA_DEAD ||
9024          Tile[x][y] == EL_BD_AMOEBA ||
9025          Tile[x][y] == EL_AMOEBA_GROWING))
9026     {
9027       AmoebaNr[x][y] = 0;
9028       Tile[x][y] = new_element;
9029       InitField(x, y, FALSE);
9030       TEST_DrawLevelField(x, y);
9031       done = TRUE;
9032     }
9033   }
9034
9035   if (done)
9036     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9037                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9038                             SND_BD_AMOEBA_TURNING_TO_GEM));
9039 }
9040
9041 static void AmoebaGrowing(int x, int y)
9042 {
9043   static unsigned int sound_delay = 0;
9044   static unsigned int sound_delay_value = 0;
9045
9046   if (!MovDelay[x][y])          // start new growing cycle
9047   {
9048     MovDelay[x][y] = 7;
9049
9050     if (DelayReached(&sound_delay, sound_delay_value))
9051     {
9052       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9053       sound_delay_value = 30;
9054     }
9055   }
9056
9057   if (MovDelay[x][y])           // wait some time before growing bigger
9058   {
9059     MovDelay[x][y]--;
9060     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9061     {
9062       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9063                                            6 - MovDelay[x][y]);
9064
9065       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9066     }
9067
9068     if (!MovDelay[x][y])
9069     {
9070       Tile[x][y] = Store[x][y];
9071       Store[x][y] = 0;
9072       TEST_DrawLevelField(x, y);
9073     }
9074   }
9075 }
9076
9077 static void AmoebaShrinking(int x, int y)
9078 {
9079   static unsigned int sound_delay = 0;
9080   static unsigned int sound_delay_value = 0;
9081
9082   if (!MovDelay[x][y])          // start new shrinking cycle
9083   {
9084     MovDelay[x][y] = 7;
9085
9086     if (DelayReached(&sound_delay, sound_delay_value))
9087       sound_delay_value = 30;
9088   }
9089
9090   if (MovDelay[x][y])           // wait some time before shrinking
9091   {
9092     MovDelay[x][y]--;
9093     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9094     {
9095       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9096                                            6 - MovDelay[x][y]);
9097
9098       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9099     }
9100
9101     if (!MovDelay[x][y])
9102     {
9103       Tile[x][y] = EL_EMPTY;
9104       TEST_DrawLevelField(x, y);
9105
9106       // don't let mole enter this field in this cycle;
9107       // (give priority to objects falling to this field from above)
9108       Stop[x][y] = TRUE;
9109     }
9110   }
9111 }
9112
9113 static void AmoebaReproduce(int ax, int ay)
9114 {
9115   int i;
9116   int element = Tile[ax][ay];
9117   int graphic = el2img(element);
9118   int newax = ax, neway = ay;
9119   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9120   static int xy[4][2] =
9121   {
9122     { 0, -1 },
9123     { -1, 0 },
9124     { +1, 0 },
9125     { 0, +1 }
9126   };
9127
9128   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9129   {
9130     Tile[ax][ay] = EL_AMOEBA_DEAD;
9131     TEST_DrawLevelField(ax, ay);
9132     return;
9133   }
9134
9135   if (IS_ANIMATED(graphic))
9136     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9137
9138   if (!MovDelay[ax][ay])        // start making new amoeba field
9139     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9140
9141   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9142   {
9143     MovDelay[ax][ay]--;
9144     if (MovDelay[ax][ay])
9145       return;
9146   }
9147
9148   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9149   {
9150     int start = RND(4);
9151     int x = ax + xy[start][0];
9152     int y = ay + xy[start][1];
9153
9154     if (!IN_LEV_FIELD(x, y))
9155       return;
9156
9157     if (IS_FREE(x, y) ||
9158         CAN_GROW_INTO(Tile[x][y]) ||
9159         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9160         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9161     {
9162       newax = x;
9163       neway = y;
9164     }
9165
9166     if (newax == ax && neway == ay)
9167       return;
9168   }
9169   else                          // normal or "filled" (BD style) amoeba
9170   {
9171     int start = RND(4);
9172     boolean waiting_for_player = FALSE;
9173
9174     for (i = 0; i < NUM_DIRECTIONS; i++)
9175     {
9176       int j = (start + i) % 4;
9177       int x = ax + xy[j][0];
9178       int y = ay + xy[j][1];
9179
9180       if (!IN_LEV_FIELD(x, y))
9181         continue;
9182
9183       if (IS_FREE(x, y) ||
9184           CAN_GROW_INTO(Tile[x][y]) ||
9185           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9186           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9187       {
9188         newax = x;
9189         neway = y;
9190         break;
9191       }
9192       else if (IS_PLAYER(x, y))
9193         waiting_for_player = TRUE;
9194     }
9195
9196     if (newax == ax && neway == ay)             // amoeba cannot grow
9197     {
9198       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9199       {
9200         Tile[ax][ay] = EL_AMOEBA_DEAD;
9201         TEST_DrawLevelField(ax, ay);
9202         AmoebaCnt[AmoebaNr[ax][ay]]--;
9203
9204         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9205         {
9206           if (element == EL_AMOEBA_FULL)
9207             AmoebaToDiamond(ax, ay);
9208           else if (element == EL_BD_AMOEBA)
9209             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9210         }
9211       }
9212       return;
9213     }
9214     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9215     {
9216       // amoeba gets larger by growing in some direction
9217
9218       int new_group_nr = AmoebaNr[ax][ay];
9219
9220 #ifdef DEBUG
9221   if (new_group_nr == 0)
9222   {
9223     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9224           newax, neway);
9225     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9226
9227     return;
9228   }
9229 #endif
9230
9231       AmoebaNr[newax][neway] = new_group_nr;
9232       AmoebaCnt[new_group_nr]++;
9233       AmoebaCnt2[new_group_nr]++;
9234
9235       // if amoeba touches other amoeba(s) after growing, unify them
9236       AmoebaMerge(newax, neway);
9237
9238       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9239       {
9240         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9241         return;
9242       }
9243     }
9244   }
9245
9246   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9247       (neway == lev_fieldy - 1 && newax != ax))
9248   {
9249     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9250     Store[newax][neway] = element;
9251   }
9252   else if (neway == ay || element == EL_EMC_DRIPPER)
9253   {
9254     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9255
9256     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9257   }
9258   else
9259   {
9260     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9261     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9262     Store[ax][ay] = EL_AMOEBA_DROP;
9263     ContinueMoving(ax, ay);
9264     return;
9265   }
9266
9267   TEST_DrawLevelField(newax, neway);
9268 }
9269
9270 static void Life(int ax, int ay)
9271 {
9272   int x1, y1, x2, y2;
9273   int life_time = 40;
9274   int element = Tile[ax][ay];
9275   int graphic = el2img(element);
9276   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9277                          level.biomaze);
9278   boolean changed = FALSE;
9279
9280   if (IS_ANIMATED(graphic))
9281     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9282
9283   if (Stop[ax][ay])
9284     return;
9285
9286   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9287     MovDelay[ax][ay] = life_time;
9288
9289   if (MovDelay[ax][ay])         // wait some time before next cycle
9290   {
9291     MovDelay[ax][ay]--;
9292     if (MovDelay[ax][ay])
9293       return;
9294   }
9295
9296   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9297   {
9298     int xx = ax+x1, yy = ay+y1;
9299     int old_element = Tile[xx][yy];
9300     int num_neighbours = 0;
9301
9302     if (!IN_LEV_FIELD(xx, yy))
9303       continue;
9304
9305     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9306     {
9307       int x = xx+x2, y = yy+y2;
9308
9309       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9310         continue;
9311
9312       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9313       boolean is_neighbour = FALSE;
9314
9315       if (level.use_life_bugs)
9316         is_neighbour =
9317           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9318            (IS_FREE(x, y)                             &&  Stop[x][y]));
9319       else
9320         is_neighbour =
9321           (Last[x][y] == element || is_player_cell);
9322
9323       if (is_neighbour)
9324         num_neighbours++;
9325     }
9326
9327     boolean is_free = FALSE;
9328
9329     if (level.use_life_bugs)
9330       is_free = (IS_FREE(xx, yy));
9331     else
9332       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9333
9334     if (xx == ax && yy == ay)           // field in the middle
9335     {
9336       if (num_neighbours < life_parameter[0] ||
9337           num_neighbours > life_parameter[1])
9338       {
9339         Tile[xx][yy] = EL_EMPTY;
9340         if (Tile[xx][yy] != old_element)
9341           TEST_DrawLevelField(xx, yy);
9342         Stop[xx][yy] = TRUE;
9343         changed = TRUE;
9344       }
9345     }
9346     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9347     {                                   // free border field
9348       if (num_neighbours >= life_parameter[2] &&
9349           num_neighbours <= life_parameter[3])
9350       {
9351         Tile[xx][yy] = element;
9352         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9353         if (Tile[xx][yy] != old_element)
9354           TEST_DrawLevelField(xx, yy);
9355         Stop[xx][yy] = TRUE;
9356         changed = TRUE;
9357       }
9358     }
9359   }
9360
9361   if (changed)
9362     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9363                    SND_GAME_OF_LIFE_GROWING);
9364 }
9365
9366 static void InitRobotWheel(int x, int y)
9367 {
9368   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9369 }
9370
9371 static void RunRobotWheel(int x, int y)
9372 {
9373   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9374 }
9375
9376 static void StopRobotWheel(int x, int y)
9377 {
9378   if (game.robot_wheel_x == x &&
9379       game.robot_wheel_y == y)
9380   {
9381     game.robot_wheel_x = -1;
9382     game.robot_wheel_y = -1;
9383     game.robot_wheel_active = FALSE;
9384   }
9385 }
9386
9387 static void InitTimegateWheel(int x, int y)
9388 {
9389   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9390 }
9391
9392 static void RunTimegateWheel(int x, int y)
9393 {
9394   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9395 }
9396
9397 static void InitMagicBallDelay(int x, int y)
9398 {
9399   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9400 }
9401
9402 static void ActivateMagicBall(int bx, int by)
9403 {
9404   int x, y;
9405
9406   if (level.ball_random)
9407   {
9408     int pos_border = RND(8);    // select one of the eight border elements
9409     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9410     int xx = pos_content % 3;
9411     int yy = pos_content / 3;
9412
9413     x = bx - 1 + xx;
9414     y = by - 1 + yy;
9415
9416     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9417       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9418   }
9419   else
9420   {
9421     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9422     {
9423       int xx = x - bx + 1;
9424       int yy = y - by + 1;
9425
9426       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9427         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9428     }
9429   }
9430
9431   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9432 }
9433
9434 static void CheckExit(int x, int y)
9435 {
9436   if (game.gems_still_needed > 0 ||
9437       game.sokoban_fields_still_needed > 0 ||
9438       game.sokoban_objects_still_needed > 0 ||
9439       game.lights_still_needed > 0)
9440   {
9441     int element = Tile[x][y];
9442     int graphic = el2img(element);
9443
9444     if (IS_ANIMATED(graphic))
9445       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9446
9447     return;
9448   }
9449
9450   // do not re-open exit door closed after last player
9451   if (game.all_players_gone)
9452     return;
9453
9454   Tile[x][y] = EL_EXIT_OPENING;
9455
9456   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9457 }
9458
9459 static void CheckExitEM(int x, int y)
9460 {
9461   if (game.gems_still_needed > 0 ||
9462       game.sokoban_fields_still_needed > 0 ||
9463       game.sokoban_objects_still_needed > 0 ||
9464       game.lights_still_needed > 0)
9465   {
9466     int element = Tile[x][y];
9467     int graphic = el2img(element);
9468
9469     if (IS_ANIMATED(graphic))
9470       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9471
9472     return;
9473   }
9474
9475   // do not re-open exit door closed after last player
9476   if (game.all_players_gone)
9477     return;
9478
9479   Tile[x][y] = EL_EM_EXIT_OPENING;
9480
9481   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9482 }
9483
9484 static void CheckExitSteel(int x, int y)
9485 {
9486   if (game.gems_still_needed > 0 ||
9487       game.sokoban_fields_still_needed > 0 ||
9488       game.sokoban_objects_still_needed > 0 ||
9489       game.lights_still_needed > 0)
9490   {
9491     int element = Tile[x][y];
9492     int graphic = el2img(element);
9493
9494     if (IS_ANIMATED(graphic))
9495       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9496
9497     return;
9498   }
9499
9500   // do not re-open exit door closed after last player
9501   if (game.all_players_gone)
9502     return;
9503
9504   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9505
9506   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9507 }
9508
9509 static void CheckExitSteelEM(int x, int y)
9510 {
9511   if (game.gems_still_needed > 0 ||
9512       game.sokoban_fields_still_needed > 0 ||
9513       game.sokoban_objects_still_needed > 0 ||
9514       game.lights_still_needed > 0)
9515   {
9516     int element = Tile[x][y];
9517     int graphic = el2img(element);
9518
9519     if (IS_ANIMATED(graphic))
9520       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9521
9522     return;
9523   }
9524
9525   // do not re-open exit door closed after last player
9526   if (game.all_players_gone)
9527     return;
9528
9529   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9530
9531   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9532 }
9533
9534 static void CheckExitSP(int x, int y)
9535 {
9536   if (game.gems_still_needed > 0)
9537   {
9538     int element = Tile[x][y];
9539     int graphic = el2img(element);
9540
9541     if (IS_ANIMATED(graphic))
9542       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9543
9544     return;
9545   }
9546
9547   // do not re-open exit door closed after last player
9548   if (game.all_players_gone)
9549     return;
9550
9551   Tile[x][y] = EL_SP_EXIT_OPENING;
9552
9553   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9554 }
9555
9556 static void CloseAllOpenTimegates(void)
9557 {
9558   int x, y;
9559
9560   SCAN_PLAYFIELD(x, y)
9561   {
9562     int element = Tile[x][y];
9563
9564     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9565     {
9566       Tile[x][y] = EL_TIMEGATE_CLOSING;
9567
9568       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9569     }
9570   }
9571 }
9572
9573 static void DrawTwinkleOnField(int x, int y)
9574 {
9575   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9576     return;
9577
9578   if (Tile[x][y] == EL_BD_DIAMOND)
9579     return;
9580
9581   if (MovDelay[x][y] == 0)      // next animation frame
9582     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9583
9584   if (MovDelay[x][y] != 0)      // wait some time before next frame
9585   {
9586     MovDelay[x][y]--;
9587
9588     DrawLevelElementAnimation(x, y, Tile[x][y]);
9589
9590     if (MovDelay[x][y] != 0)
9591     {
9592       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9593                                            10 - MovDelay[x][y]);
9594
9595       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9596     }
9597   }
9598 }
9599
9600 static void MauerWaechst(int x, int y)
9601 {
9602   int delay = 6;
9603
9604   if (!MovDelay[x][y])          // next animation frame
9605     MovDelay[x][y] = 3 * delay;
9606
9607   if (MovDelay[x][y])           // wait some time before next frame
9608   {
9609     MovDelay[x][y]--;
9610
9611     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9612     {
9613       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9614       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9615
9616       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9617     }
9618
9619     if (!MovDelay[x][y])
9620     {
9621       if (MovDir[x][y] == MV_LEFT)
9622       {
9623         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9624           TEST_DrawLevelField(x - 1, y);
9625       }
9626       else if (MovDir[x][y] == MV_RIGHT)
9627       {
9628         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9629           TEST_DrawLevelField(x + 1, y);
9630       }
9631       else if (MovDir[x][y] == MV_UP)
9632       {
9633         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9634           TEST_DrawLevelField(x, y - 1);
9635       }
9636       else
9637       {
9638         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9639           TEST_DrawLevelField(x, y + 1);
9640       }
9641
9642       Tile[x][y] = Store[x][y];
9643       Store[x][y] = 0;
9644       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9645       TEST_DrawLevelField(x, y);
9646     }
9647   }
9648 }
9649
9650 static void MauerAbleger(int ax, int ay)
9651 {
9652   int element = Tile[ax][ay];
9653   int graphic = el2img(element);
9654   boolean oben_frei = FALSE, unten_frei = FALSE;
9655   boolean links_frei = FALSE, rechts_frei = FALSE;
9656   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9657   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9658   boolean new_wall = FALSE;
9659
9660   if (IS_ANIMATED(graphic))
9661     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9662
9663   if (!MovDelay[ax][ay])        // start building new wall
9664     MovDelay[ax][ay] = 6;
9665
9666   if (MovDelay[ax][ay])         // wait some time before building new wall
9667   {
9668     MovDelay[ax][ay]--;
9669     if (MovDelay[ax][ay])
9670       return;
9671   }
9672
9673   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9674     oben_frei = TRUE;
9675   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9676     unten_frei = TRUE;
9677   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9678     links_frei = TRUE;
9679   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9680     rechts_frei = TRUE;
9681
9682   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9683       element == EL_EXPANDABLE_WALL_ANY)
9684   {
9685     if (oben_frei)
9686     {
9687       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9688       Store[ax][ay-1] = element;
9689       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9690       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9691         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9692                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9693       new_wall = TRUE;
9694     }
9695     if (unten_frei)
9696     {
9697       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9698       Store[ax][ay+1] = element;
9699       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9700       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9701         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9702                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9703       new_wall = TRUE;
9704     }
9705   }
9706
9707   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9708       element == EL_EXPANDABLE_WALL_ANY ||
9709       element == EL_EXPANDABLE_WALL ||
9710       element == EL_BD_EXPANDABLE_WALL)
9711   {
9712     if (links_frei)
9713     {
9714       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9715       Store[ax-1][ay] = element;
9716       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9717       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9718         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9719                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9720       new_wall = TRUE;
9721     }
9722
9723     if (rechts_frei)
9724     {
9725       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9726       Store[ax+1][ay] = element;
9727       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9728       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9729         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9730                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9731       new_wall = TRUE;
9732     }
9733   }
9734
9735   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9736     TEST_DrawLevelField(ax, ay);
9737
9738   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9739     oben_massiv = TRUE;
9740   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9741     unten_massiv = TRUE;
9742   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9743     links_massiv = TRUE;
9744   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9745     rechts_massiv = TRUE;
9746
9747   if (((oben_massiv && unten_massiv) ||
9748        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9749        element == EL_EXPANDABLE_WALL) &&
9750       ((links_massiv && rechts_massiv) ||
9751        element == EL_EXPANDABLE_WALL_VERTICAL))
9752     Tile[ax][ay] = EL_WALL;
9753
9754   if (new_wall)
9755     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9756 }
9757
9758 static void MauerAblegerStahl(int ax, int ay)
9759 {
9760   int element = Tile[ax][ay];
9761   int graphic = el2img(element);
9762   boolean oben_frei = FALSE, unten_frei = FALSE;
9763   boolean links_frei = FALSE, rechts_frei = FALSE;
9764   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9765   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9766   boolean new_wall = FALSE;
9767
9768   if (IS_ANIMATED(graphic))
9769     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9770
9771   if (!MovDelay[ax][ay])        // start building new wall
9772     MovDelay[ax][ay] = 6;
9773
9774   if (MovDelay[ax][ay])         // wait some time before building new wall
9775   {
9776     MovDelay[ax][ay]--;
9777     if (MovDelay[ax][ay])
9778       return;
9779   }
9780
9781   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9782     oben_frei = TRUE;
9783   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9784     unten_frei = TRUE;
9785   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9786     links_frei = TRUE;
9787   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9788     rechts_frei = TRUE;
9789
9790   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9791       element == EL_EXPANDABLE_STEELWALL_ANY)
9792   {
9793     if (oben_frei)
9794     {
9795       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9796       Store[ax][ay-1] = element;
9797       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9798       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9799         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9800                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9801       new_wall = TRUE;
9802     }
9803     if (unten_frei)
9804     {
9805       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9806       Store[ax][ay+1] = element;
9807       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9808       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9809         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9810                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9811       new_wall = TRUE;
9812     }
9813   }
9814
9815   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9816       element == EL_EXPANDABLE_STEELWALL_ANY)
9817   {
9818     if (links_frei)
9819     {
9820       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9821       Store[ax-1][ay] = element;
9822       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9823       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9824         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9825                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9826       new_wall = TRUE;
9827     }
9828
9829     if (rechts_frei)
9830     {
9831       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9832       Store[ax+1][ay] = element;
9833       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9834       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9835         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9836                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9837       new_wall = TRUE;
9838     }
9839   }
9840
9841   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9842     oben_massiv = TRUE;
9843   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9844     unten_massiv = TRUE;
9845   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9846     links_massiv = TRUE;
9847   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9848     rechts_massiv = TRUE;
9849
9850   if (((oben_massiv && unten_massiv) ||
9851        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9852       ((links_massiv && rechts_massiv) ||
9853        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9854     Tile[ax][ay] = EL_STEELWALL;
9855
9856   if (new_wall)
9857     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9858 }
9859
9860 static void CheckForDragon(int x, int y)
9861 {
9862   int i, j;
9863   boolean dragon_found = FALSE;
9864   static int xy[4][2] =
9865   {
9866     { 0, -1 },
9867     { -1, 0 },
9868     { +1, 0 },
9869     { 0, +1 }
9870   };
9871
9872   for (i = 0; i < NUM_DIRECTIONS; i++)
9873   {
9874     for (j = 0; j < 4; j++)
9875     {
9876       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9877
9878       if (IN_LEV_FIELD(xx, yy) &&
9879           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9880       {
9881         if (Tile[xx][yy] == EL_DRAGON)
9882           dragon_found = TRUE;
9883       }
9884       else
9885         break;
9886     }
9887   }
9888
9889   if (!dragon_found)
9890   {
9891     for (i = 0; i < NUM_DIRECTIONS; i++)
9892     {
9893       for (j = 0; j < 3; j++)
9894       {
9895         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9896   
9897         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9898         {
9899           Tile[xx][yy] = EL_EMPTY;
9900           TEST_DrawLevelField(xx, yy);
9901         }
9902         else
9903           break;
9904       }
9905     }
9906   }
9907 }
9908
9909 static void InitBuggyBase(int x, int y)
9910 {
9911   int element = Tile[x][y];
9912   int activating_delay = FRAMES_PER_SECOND / 4;
9913
9914   ChangeDelay[x][y] =
9915     (element == EL_SP_BUGGY_BASE ?
9916      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9917      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9918      activating_delay :
9919      element == EL_SP_BUGGY_BASE_ACTIVE ?
9920      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9921 }
9922
9923 static void WarnBuggyBase(int x, int y)
9924 {
9925   int i;
9926   static int xy[4][2] =
9927   {
9928     { 0, -1 },
9929     { -1, 0 },
9930     { +1, 0 },
9931     { 0, +1 }
9932   };
9933
9934   for (i = 0; i < NUM_DIRECTIONS; i++)
9935   {
9936     int xx = x + xy[i][0];
9937     int yy = y + xy[i][1];
9938
9939     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9940     {
9941       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9942
9943       break;
9944     }
9945   }
9946 }
9947
9948 static void InitTrap(int x, int y)
9949 {
9950   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9951 }
9952
9953 static void ActivateTrap(int x, int y)
9954 {
9955   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9956 }
9957
9958 static void ChangeActiveTrap(int x, int y)
9959 {
9960   int graphic = IMG_TRAP_ACTIVE;
9961
9962   // if new animation frame was drawn, correct crumbled sand border
9963   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9964     TEST_DrawLevelFieldCrumbled(x, y);
9965 }
9966
9967 static int getSpecialActionElement(int element, int number, int base_element)
9968 {
9969   return (element != EL_EMPTY ? element :
9970           number != -1 ? base_element + number - 1 :
9971           EL_EMPTY);
9972 }
9973
9974 static int getModifiedActionNumber(int value_old, int operator, int operand,
9975                                    int value_min, int value_max)
9976 {
9977   int value_new = (operator == CA_MODE_SET      ? operand :
9978                    operator == CA_MODE_ADD      ? value_old + operand :
9979                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9980                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9981                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9982                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9983                    value_old);
9984
9985   return (value_new < value_min ? value_min :
9986           value_new > value_max ? value_max :
9987           value_new);
9988 }
9989
9990 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9991 {
9992   struct ElementInfo *ei = &element_info[element];
9993   struct ElementChangeInfo *change = &ei->change_page[page];
9994   int target_element = change->target_element;
9995   int action_type = change->action_type;
9996   int action_mode = change->action_mode;
9997   int action_arg = change->action_arg;
9998   int action_element = change->action_element;
9999   int i;
10000
10001   if (!change->has_action)
10002     return;
10003
10004   // ---------- determine action paramater values -----------------------------
10005
10006   int level_time_value =
10007     (level.time > 0 ? TimeLeft :
10008      TimePlayed);
10009
10010   int action_arg_element_raw =
10011     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10012      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10013      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10014      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10015      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10016      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10017      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10018      EL_EMPTY);
10019   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10020
10021   int action_arg_direction =
10022     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10023      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10024      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10025      change->actual_trigger_side :
10026      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10027      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10028      MV_NONE);
10029
10030   int action_arg_number_min =
10031     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10032      CA_ARG_MIN);
10033
10034   int action_arg_number_max =
10035     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10036      action_type == CA_SET_LEVEL_GEMS ? 999 :
10037      action_type == CA_SET_LEVEL_TIME ? 9999 :
10038      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10039      action_type == CA_SET_CE_VALUE ? 9999 :
10040      action_type == CA_SET_CE_SCORE ? 9999 :
10041      CA_ARG_MAX);
10042
10043   int action_arg_number_reset =
10044     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10045      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10046      action_type == CA_SET_LEVEL_TIME ? level.time :
10047      action_type == CA_SET_LEVEL_SCORE ? 0 :
10048      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10049      action_type == CA_SET_CE_SCORE ? 0 :
10050      0);
10051
10052   int action_arg_number =
10053     (action_arg <= CA_ARG_MAX ? action_arg :
10054      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10055      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10056      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10057      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10058      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10059      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10060      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10061      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10062      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10063      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10064      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10065      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10066      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10067      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10068      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10069      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10070      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10071      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10072      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10073      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10074      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10075      -1);
10076
10077   int action_arg_number_old =
10078     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10079      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10080      action_type == CA_SET_LEVEL_SCORE ? game.score :
10081      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10082      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10083      0);
10084
10085   int action_arg_number_new =
10086     getModifiedActionNumber(action_arg_number_old,
10087                             action_mode, action_arg_number,
10088                             action_arg_number_min, action_arg_number_max);
10089
10090   int trigger_player_bits =
10091     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10092      change->actual_trigger_player_bits : change->trigger_player);
10093
10094   int action_arg_player_bits =
10095     (action_arg >= CA_ARG_PLAYER_1 &&
10096      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10097      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10098      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10099      PLAYER_BITS_ANY);
10100
10101   // ---------- execute action  -----------------------------------------------
10102
10103   switch (action_type)
10104   {
10105     case CA_NO_ACTION:
10106     {
10107       return;
10108     }
10109
10110     // ---------- level actions  ----------------------------------------------
10111
10112     case CA_RESTART_LEVEL:
10113     {
10114       game.restart_level = TRUE;
10115
10116       break;
10117     }
10118
10119     case CA_SHOW_ENVELOPE:
10120     {
10121       int element = getSpecialActionElement(action_arg_element,
10122                                             action_arg_number, EL_ENVELOPE_1);
10123
10124       if (IS_ENVELOPE(element))
10125         local_player->show_envelope = element;
10126
10127       break;
10128     }
10129
10130     case CA_SET_LEVEL_TIME:
10131     {
10132       if (level.time > 0)       // only modify limited time value
10133       {
10134         TimeLeft = action_arg_number_new;
10135
10136         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10137
10138         DisplayGameControlValues();
10139
10140         if (!TimeLeft && setup.time_limit)
10141           for (i = 0; i < MAX_PLAYERS; i++)
10142             KillPlayer(&stored_player[i]);
10143       }
10144
10145       break;
10146     }
10147
10148     case CA_SET_LEVEL_SCORE:
10149     {
10150       game.score = action_arg_number_new;
10151
10152       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10153
10154       DisplayGameControlValues();
10155
10156       break;
10157     }
10158
10159     case CA_SET_LEVEL_GEMS:
10160     {
10161       game.gems_still_needed = action_arg_number_new;
10162
10163       game.snapshot.collected_item = TRUE;
10164
10165       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10166
10167       DisplayGameControlValues();
10168
10169       break;
10170     }
10171
10172     case CA_SET_LEVEL_WIND:
10173     {
10174       game.wind_direction = action_arg_direction;
10175
10176       break;
10177     }
10178
10179     case CA_SET_LEVEL_RANDOM_SEED:
10180     {
10181       // ensure that setting a new random seed while playing is predictable
10182       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10183
10184       break;
10185     }
10186
10187     // ---------- player actions  ---------------------------------------------
10188
10189     case CA_MOVE_PLAYER:
10190     case CA_MOVE_PLAYER_NEW:
10191     {
10192       // automatically move to the next field in specified direction
10193       for (i = 0; i < MAX_PLAYERS; i++)
10194         if (trigger_player_bits & (1 << i))
10195           if (action_type == CA_MOVE_PLAYER ||
10196               stored_player[i].MovPos == 0)
10197             stored_player[i].programmed_action = action_arg_direction;
10198
10199       break;
10200     }
10201
10202     case CA_EXIT_PLAYER:
10203     {
10204       for (i = 0; i < MAX_PLAYERS; i++)
10205         if (action_arg_player_bits & (1 << i))
10206           ExitPlayer(&stored_player[i]);
10207
10208       if (game.players_still_needed == 0)
10209         LevelSolved();
10210
10211       break;
10212     }
10213
10214     case CA_KILL_PLAYER:
10215     {
10216       for (i = 0; i < MAX_PLAYERS; i++)
10217         if (action_arg_player_bits & (1 << i))
10218           KillPlayer(&stored_player[i]);
10219
10220       break;
10221     }
10222
10223     case CA_SET_PLAYER_KEYS:
10224     {
10225       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10226       int element = getSpecialActionElement(action_arg_element,
10227                                             action_arg_number, EL_KEY_1);
10228
10229       if (IS_KEY(element))
10230       {
10231         for (i = 0; i < MAX_PLAYERS; i++)
10232         {
10233           if (trigger_player_bits & (1 << i))
10234           {
10235             stored_player[i].key[KEY_NR(element)] = key_state;
10236
10237             DrawGameDoorValues();
10238           }
10239         }
10240       }
10241
10242       break;
10243     }
10244
10245     case CA_SET_PLAYER_SPEED:
10246     {
10247       for (i = 0; i < MAX_PLAYERS; i++)
10248       {
10249         if (trigger_player_bits & (1 << i))
10250         {
10251           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10252
10253           if (action_arg == CA_ARG_SPEED_FASTER &&
10254               stored_player[i].cannot_move)
10255           {
10256             action_arg_number = STEPSIZE_VERY_SLOW;
10257           }
10258           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10259                    action_arg == CA_ARG_SPEED_FASTER)
10260           {
10261             action_arg_number = 2;
10262             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10263                            CA_MODE_MULTIPLY);
10264           }
10265           else if (action_arg == CA_ARG_NUMBER_RESET)
10266           {
10267             action_arg_number = level.initial_player_stepsize[i];
10268           }
10269
10270           move_stepsize =
10271             getModifiedActionNumber(move_stepsize,
10272                                     action_mode,
10273                                     action_arg_number,
10274                                     action_arg_number_min,
10275                                     action_arg_number_max);
10276
10277           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10278         }
10279       }
10280
10281       break;
10282     }
10283
10284     case CA_SET_PLAYER_SHIELD:
10285     {
10286       for (i = 0; i < MAX_PLAYERS; i++)
10287       {
10288         if (trigger_player_bits & (1 << i))
10289         {
10290           if (action_arg == CA_ARG_SHIELD_OFF)
10291           {
10292             stored_player[i].shield_normal_time_left = 0;
10293             stored_player[i].shield_deadly_time_left = 0;
10294           }
10295           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10296           {
10297             stored_player[i].shield_normal_time_left = 999999;
10298           }
10299           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10300           {
10301             stored_player[i].shield_normal_time_left = 999999;
10302             stored_player[i].shield_deadly_time_left = 999999;
10303           }
10304         }
10305       }
10306
10307       break;
10308     }
10309
10310     case CA_SET_PLAYER_GRAVITY:
10311     {
10312       for (i = 0; i < MAX_PLAYERS; i++)
10313       {
10314         if (trigger_player_bits & (1 << i))
10315         {
10316           stored_player[i].gravity =
10317             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10318              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10319              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10320              stored_player[i].gravity);
10321         }
10322       }
10323
10324       break;
10325     }
10326
10327     case CA_SET_PLAYER_ARTWORK:
10328     {
10329       for (i = 0; i < MAX_PLAYERS; i++)
10330       {
10331         if (trigger_player_bits & (1 << i))
10332         {
10333           int artwork_element = action_arg_element;
10334
10335           if (action_arg == CA_ARG_ELEMENT_RESET)
10336             artwork_element =
10337               (level.use_artwork_element[i] ? level.artwork_element[i] :
10338                stored_player[i].element_nr);
10339
10340           if (stored_player[i].artwork_element != artwork_element)
10341             stored_player[i].Frame = 0;
10342
10343           stored_player[i].artwork_element = artwork_element;
10344
10345           SetPlayerWaiting(&stored_player[i], FALSE);
10346
10347           // set number of special actions for bored and sleeping animation
10348           stored_player[i].num_special_action_bored =
10349             get_num_special_action(artwork_element,
10350                                    ACTION_BORING_1, ACTION_BORING_LAST);
10351           stored_player[i].num_special_action_sleeping =
10352             get_num_special_action(artwork_element,
10353                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10354         }
10355       }
10356
10357       break;
10358     }
10359
10360     case CA_SET_PLAYER_INVENTORY:
10361     {
10362       for (i = 0; i < MAX_PLAYERS; i++)
10363       {
10364         struct PlayerInfo *player = &stored_player[i];
10365         int j, k;
10366
10367         if (trigger_player_bits & (1 << i))
10368         {
10369           int inventory_element = action_arg_element;
10370
10371           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10372               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10373               action_arg == CA_ARG_ELEMENT_ACTION)
10374           {
10375             int element = inventory_element;
10376             int collect_count = element_info[element].collect_count_initial;
10377
10378             if (!IS_CUSTOM_ELEMENT(element))
10379               collect_count = 1;
10380
10381             if (collect_count == 0)
10382               player->inventory_infinite_element = element;
10383             else
10384               for (k = 0; k < collect_count; k++)
10385                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10386                   player->inventory_element[player->inventory_size++] =
10387                     element;
10388           }
10389           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10390                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10391                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10392           {
10393             if (player->inventory_infinite_element != EL_UNDEFINED &&
10394                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10395                                      action_arg_element_raw))
10396               player->inventory_infinite_element = EL_UNDEFINED;
10397
10398             for (k = 0, j = 0; j < player->inventory_size; j++)
10399             {
10400               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10401                                         action_arg_element_raw))
10402                 player->inventory_element[k++] = player->inventory_element[j];
10403             }
10404
10405             player->inventory_size = k;
10406           }
10407           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10408           {
10409             if (player->inventory_size > 0)
10410             {
10411               for (j = 0; j < player->inventory_size - 1; j++)
10412                 player->inventory_element[j] = player->inventory_element[j + 1];
10413
10414               player->inventory_size--;
10415             }
10416           }
10417           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10418           {
10419             if (player->inventory_size > 0)
10420               player->inventory_size--;
10421           }
10422           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10423           {
10424             player->inventory_infinite_element = EL_UNDEFINED;
10425             player->inventory_size = 0;
10426           }
10427           else if (action_arg == CA_ARG_INVENTORY_RESET)
10428           {
10429             player->inventory_infinite_element = EL_UNDEFINED;
10430             player->inventory_size = 0;
10431
10432             if (level.use_initial_inventory[i])
10433             {
10434               for (j = 0; j < level.initial_inventory_size[i]; j++)
10435               {
10436                 int element = level.initial_inventory_content[i][j];
10437                 int collect_count = element_info[element].collect_count_initial;
10438
10439                 if (!IS_CUSTOM_ELEMENT(element))
10440                   collect_count = 1;
10441
10442                 if (collect_count == 0)
10443                   player->inventory_infinite_element = element;
10444                 else
10445                   for (k = 0; k < collect_count; k++)
10446                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10447                       player->inventory_element[player->inventory_size++] =
10448                         element;
10449               }
10450             }
10451           }
10452         }
10453       }
10454
10455       break;
10456     }
10457
10458     // ---------- CE actions  -------------------------------------------------
10459
10460     case CA_SET_CE_VALUE:
10461     {
10462       int last_ce_value = CustomValue[x][y];
10463
10464       CustomValue[x][y] = action_arg_number_new;
10465
10466       if (CustomValue[x][y] != last_ce_value)
10467       {
10468         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10469         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10470
10471         if (CustomValue[x][y] == 0)
10472         {
10473           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10474           ChangeCount[x][y] = 0;        // allow at least one more change
10475
10476           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10477           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10478         }
10479       }
10480
10481       break;
10482     }
10483
10484     case CA_SET_CE_SCORE:
10485     {
10486       int last_ce_score = ei->collect_score;
10487
10488       ei->collect_score = action_arg_number_new;
10489
10490       if (ei->collect_score != last_ce_score)
10491       {
10492         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10493         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10494
10495         if (ei->collect_score == 0)
10496         {
10497           int xx, yy;
10498
10499           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10500           ChangeCount[x][y] = 0;        // allow at least one more change
10501
10502           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10503           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10504
10505           /*
10506             This is a very special case that seems to be a mixture between
10507             CheckElementChange() and CheckTriggeredElementChange(): while
10508             the first one only affects single elements that are triggered
10509             directly, the second one affects multiple elements in the playfield
10510             that are triggered indirectly by another element. This is a third
10511             case: Changing the CE score always affects multiple identical CEs,
10512             so every affected CE must be checked, not only the single CE for
10513             which the CE score was changed in the first place (as every instance
10514             of that CE shares the same CE score, and therefore also can change)!
10515           */
10516           SCAN_PLAYFIELD(xx, yy)
10517           {
10518             if (Tile[xx][yy] == element)
10519               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10520                                  CE_SCORE_GETS_ZERO);
10521           }
10522         }
10523       }
10524
10525       break;
10526     }
10527
10528     case CA_SET_CE_ARTWORK:
10529     {
10530       int artwork_element = action_arg_element;
10531       boolean reset_frame = FALSE;
10532       int xx, yy;
10533
10534       if (action_arg == CA_ARG_ELEMENT_RESET)
10535         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10536                            element);
10537
10538       if (ei->gfx_element != artwork_element)
10539         reset_frame = TRUE;
10540
10541       ei->gfx_element = artwork_element;
10542
10543       SCAN_PLAYFIELD(xx, yy)
10544       {
10545         if (Tile[xx][yy] == element)
10546         {
10547           if (reset_frame)
10548           {
10549             ResetGfxAnimation(xx, yy);
10550             ResetRandomAnimationValue(xx, yy);
10551           }
10552
10553           TEST_DrawLevelField(xx, yy);
10554         }
10555       }
10556
10557       break;
10558     }
10559
10560     // ---------- engine actions  ---------------------------------------------
10561
10562     case CA_SET_ENGINE_SCAN_MODE:
10563     {
10564       InitPlayfieldScanMode(action_arg);
10565
10566       break;
10567     }
10568
10569     default:
10570       break;
10571   }
10572 }
10573
10574 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10575 {
10576   int old_element = Tile[x][y];
10577   int new_element = GetElementFromGroupElement(element);
10578   int previous_move_direction = MovDir[x][y];
10579   int last_ce_value = CustomValue[x][y];
10580   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10581   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10582   boolean add_player_onto_element = (new_element_is_player &&
10583                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10584                                      IS_WALKABLE(old_element));
10585
10586   if (!add_player_onto_element)
10587   {
10588     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10589       RemoveMovingField(x, y);
10590     else
10591       RemoveField(x, y);
10592
10593     Tile[x][y] = new_element;
10594
10595     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10596       MovDir[x][y] = previous_move_direction;
10597
10598     if (element_info[new_element].use_last_ce_value)
10599       CustomValue[x][y] = last_ce_value;
10600
10601     InitField_WithBug1(x, y, FALSE);
10602
10603     new_element = Tile[x][y];   // element may have changed
10604
10605     ResetGfxAnimation(x, y);
10606     ResetRandomAnimationValue(x, y);
10607
10608     TEST_DrawLevelField(x, y);
10609
10610     if (GFX_CRUMBLED(new_element))
10611       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10612   }
10613
10614   // check if element under the player changes from accessible to unaccessible
10615   // (needed for special case of dropping element which then changes)
10616   // (must be checked after creating new element for walkable group elements)
10617   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10618       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10619   {
10620     Bang(x, y);
10621
10622     return;
10623   }
10624
10625   // "ChangeCount" not set yet to allow "entered by player" change one time
10626   if (new_element_is_player)
10627     RelocatePlayer(x, y, new_element);
10628
10629   if (is_change)
10630     ChangeCount[x][y]++;        // count number of changes in the same frame
10631
10632   TestIfBadThingTouchesPlayer(x, y);
10633   TestIfPlayerTouchesCustomElement(x, y);
10634   TestIfElementTouchesCustomElement(x, y);
10635 }
10636
10637 static void CreateField(int x, int y, int element)
10638 {
10639   CreateFieldExt(x, y, element, FALSE);
10640 }
10641
10642 static void CreateElementFromChange(int x, int y, int element)
10643 {
10644   element = GET_VALID_RUNTIME_ELEMENT(element);
10645
10646   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10647   {
10648     int old_element = Tile[x][y];
10649
10650     // prevent changed element from moving in same engine frame
10651     // unless both old and new element can either fall or move
10652     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10653         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10654       Stop[x][y] = TRUE;
10655   }
10656
10657   CreateFieldExt(x, y, element, TRUE);
10658 }
10659
10660 static boolean ChangeElement(int x, int y, int element, int page)
10661 {
10662   struct ElementInfo *ei = &element_info[element];
10663   struct ElementChangeInfo *change = &ei->change_page[page];
10664   int ce_value = CustomValue[x][y];
10665   int ce_score = ei->collect_score;
10666   int target_element;
10667   int old_element = Tile[x][y];
10668
10669   // always use default change event to prevent running into a loop
10670   if (ChangeEvent[x][y] == -1)
10671     ChangeEvent[x][y] = CE_DELAY;
10672
10673   if (ChangeEvent[x][y] == CE_DELAY)
10674   {
10675     // reset actual trigger element, trigger player and action element
10676     change->actual_trigger_element = EL_EMPTY;
10677     change->actual_trigger_player = EL_EMPTY;
10678     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10679     change->actual_trigger_side = CH_SIDE_NONE;
10680     change->actual_trigger_ce_value = 0;
10681     change->actual_trigger_ce_score = 0;
10682   }
10683
10684   // do not change elements more than a specified maximum number of changes
10685   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10686     return FALSE;
10687
10688   ChangeCount[x][y]++;          // count number of changes in the same frame
10689
10690   if (change->explode)
10691   {
10692     Bang(x, y);
10693
10694     return TRUE;
10695   }
10696
10697   if (change->use_target_content)
10698   {
10699     boolean complete_replace = TRUE;
10700     boolean can_replace[3][3];
10701     int xx, yy;
10702
10703     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10704     {
10705       boolean is_empty;
10706       boolean is_walkable;
10707       boolean is_diggable;
10708       boolean is_collectible;
10709       boolean is_removable;
10710       boolean is_destructible;
10711       int ex = x + xx - 1;
10712       int ey = y + yy - 1;
10713       int content_element = change->target_content.e[xx][yy];
10714       int e;
10715
10716       can_replace[xx][yy] = TRUE;
10717
10718       if (ex == x && ey == y)   // do not check changing element itself
10719         continue;
10720
10721       if (content_element == EL_EMPTY_SPACE)
10722       {
10723         can_replace[xx][yy] = FALSE;    // do not replace border with space
10724
10725         continue;
10726       }
10727
10728       if (!IN_LEV_FIELD(ex, ey))
10729       {
10730         can_replace[xx][yy] = FALSE;
10731         complete_replace = FALSE;
10732
10733         continue;
10734       }
10735
10736       e = Tile[ex][ey];
10737
10738       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10739         e = MovingOrBlocked2Element(ex, ey);
10740
10741       is_empty = (IS_FREE(ex, ey) ||
10742                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10743
10744       is_walkable     = (is_empty || IS_WALKABLE(e));
10745       is_diggable     = (is_empty || IS_DIGGABLE(e));
10746       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10747       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10748       is_removable    = (is_diggable || is_collectible);
10749
10750       can_replace[xx][yy] =
10751         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10752           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10753           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10754           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10755           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10756           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10757          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10758
10759       if (!can_replace[xx][yy])
10760         complete_replace = FALSE;
10761     }
10762
10763     if (!change->only_if_complete || complete_replace)
10764     {
10765       boolean something_has_changed = FALSE;
10766
10767       if (change->only_if_complete && change->use_random_replace &&
10768           RND(100) < change->random_percentage)
10769         return FALSE;
10770
10771       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10772       {
10773         int ex = x + xx - 1;
10774         int ey = y + yy - 1;
10775         int content_element;
10776
10777         if (can_replace[xx][yy] && (!change->use_random_replace ||
10778                                     RND(100) < change->random_percentage))
10779         {
10780           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10781             RemoveMovingField(ex, ey);
10782
10783           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10784
10785           content_element = change->target_content.e[xx][yy];
10786           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10787                                               ce_value, ce_score);
10788
10789           CreateElementFromChange(ex, ey, target_element);
10790
10791           something_has_changed = TRUE;
10792
10793           // for symmetry reasons, freeze newly created border elements
10794           if (ex != x || ey != y)
10795             Stop[ex][ey] = TRUE;        // no more moving in this frame
10796         }
10797       }
10798
10799       if (something_has_changed)
10800       {
10801         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10802         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10803       }
10804     }
10805   }
10806   else
10807   {
10808     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10809                                         ce_value, ce_score);
10810
10811     if (element == EL_DIAGONAL_GROWING ||
10812         element == EL_DIAGONAL_SHRINKING)
10813     {
10814       target_element = Store[x][y];
10815
10816       Store[x][y] = EL_EMPTY;
10817     }
10818
10819     CreateElementFromChange(x, y, target_element);
10820
10821     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10822     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10823   }
10824
10825   // this uses direct change before indirect change
10826   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10827
10828   return TRUE;
10829 }
10830
10831 static void HandleElementChange(int x, int y, int page)
10832 {
10833   int element = MovingOrBlocked2Element(x, y);
10834   struct ElementInfo *ei = &element_info[element];
10835   struct ElementChangeInfo *change = &ei->change_page[page];
10836   boolean handle_action_before_change = FALSE;
10837
10838 #ifdef DEBUG
10839   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10840       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10841   {
10842     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10843           x, y, element, element_info[element].token_name);
10844     Debug("game:playing:HandleElementChange", "This should never happen!");
10845   }
10846 #endif
10847
10848   // this can happen with classic bombs on walkable, changing elements
10849   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10850   {
10851     return;
10852   }
10853
10854   if (ChangeDelay[x][y] == 0)           // initialize element change
10855   {
10856     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10857
10858     if (change->can_change)
10859     {
10860       // !!! not clear why graphic animation should be reset at all here !!!
10861       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10862       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10863
10864       /*
10865         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10866
10867         When using an animation frame delay of 1 (this only happens with
10868         "sp_zonk.moving.left/right" in the classic graphics), the default
10869         (non-moving) animation shows wrong animation frames (while the
10870         moving animation, like "sp_zonk.moving.left/right", is correct,
10871         so this graphical bug never shows up with the classic graphics).
10872         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10873         be drawn instead of the correct frames 0,1,2,3. This is caused by
10874         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10875         an element change: First when the change delay ("ChangeDelay[][]")
10876         counter has reached zero after decrementing, then a second time in
10877         the next frame (after "GfxFrame[][]" was already incremented) when
10878         "ChangeDelay[][]" is reset to the initial delay value again.
10879
10880         This causes frame 0 to be drawn twice, while the last frame won't
10881         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10882
10883         As some animations may already be cleverly designed around this bug
10884         (at least the "Snake Bite" snake tail animation does this), it cannot
10885         simply be fixed here without breaking such existing animations.
10886         Unfortunately, it cannot easily be detected if a graphics set was
10887         designed "before" or "after" the bug was fixed. As a workaround,
10888         a new graphics set option "game.graphics_engine_version" was added
10889         to be able to specify the game's major release version for which the
10890         graphics set was designed, which can then be used to decide if the
10891         bugfix should be used (version 4 and above) or not (version 3 or
10892         below, or if no version was specified at all, as with old sets).
10893
10894         (The wrong/fixed animation frames can be tested with the test level set
10895         "test_gfxframe" and level "000", which contains a specially prepared
10896         custom element at level position (x/y) == (11/9) which uses the zonk
10897         animation mentioned above. Using "game.graphics_engine_version: 4"
10898         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10899         This can also be seen from the debug output for this test element.)
10900       */
10901
10902       // when a custom element is about to change (for example by change delay),
10903       // do not reset graphic animation when the custom element is moving
10904       if (game.graphics_engine_version < 4 &&
10905           !IS_MOVING(x, y))
10906       {
10907         ResetGfxAnimation(x, y);
10908         ResetRandomAnimationValue(x, y);
10909       }
10910
10911       if (change->pre_change_function)
10912         change->pre_change_function(x, y);
10913     }
10914   }
10915
10916   ChangeDelay[x][y]--;
10917
10918   if (ChangeDelay[x][y] != 0)           // continue element change
10919   {
10920     if (change->can_change)
10921     {
10922       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10923
10924       if (IS_ANIMATED(graphic))
10925         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10926
10927       if (change->change_function)
10928         change->change_function(x, y);
10929     }
10930   }
10931   else                                  // finish element change
10932   {
10933     if (ChangePage[x][y] != -1)         // remember page from delayed change
10934     {
10935       page = ChangePage[x][y];
10936       ChangePage[x][y] = -1;
10937
10938       change = &ei->change_page[page];
10939     }
10940
10941     if (IS_MOVING(x, y))                // never change a running system ;-)
10942     {
10943       ChangeDelay[x][y] = 1;            // try change after next move step
10944       ChangePage[x][y] = page;          // remember page to use for change
10945
10946       return;
10947     }
10948
10949     // special case: set new level random seed before changing element
10950     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10951       handle_action_before_change = TRUE;
10952
10953     if (change->has_action && handle_action_before_change)
10954       ExecuteCustomElementAction(x, y, element, page);
10955
10956     if (change->can_change)
10957     {
10958       if (ChangeElement(x, y, element, page))
10959       {
10960         if (change->post_change_function)
10961           change->post_change_function(x, y);
10962       }
10963     }
10964
10965     if (change->has_action && !handle_action_before_change)
10966       ExecuteCustomElementAction(x, y, element, page);
10967   }
10968 }
10969
10970 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10971                                               int trigger_element,
10972                                               int trigger_event,
10973                                               int trigger_player,
10974                                               int trigger_side,
10975                                               int trigger_page)
10976 {
10977   boolean change_done_any = FALSE;
10978   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10979   int i;
10980
10981   if (!(trigger_events[trigger_element][trigger_event]))
10982     return FALSE;
10983
10984   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10985
10986   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10987   {
10988     int element = EL_CUSTOM_START + i;
10989     boolean change_done = FALSE;
10990     int p;
10991
10992     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10993         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10994       continue;
10995
10996     for (p = 0; p < element_info[element].num_change_pages; p++)
10997     {
10998       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10999
11000       if (change->can_change_or_has_action &&
11001           change->has_event[trigger_event] &&
11002           change->trigger_side & trigger_side &&
11003           change->trigger_player & trigger_player &&
11004           change->trigger_page & trigger_page_bits &&
11005           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11006       {
11007         change->actual_trigger_element = trigger_element;
11008         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11009         change->actual_trigger_player_bits = trigger_player;
11010         change->actual_trigger_side = trigger_side;
11011         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11012         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11013
11014         if ((change->can_change && !change_done) || change->has_action)
11015         {
11016           int x, y;
11017
11018           SCAN_PLAYFIELD(x, y)
11019           {
11020             if (Tile[x][y] == element)
11021             {
11022               if (change->can_change && !change_done)
11023               {
11024                 // if element already changed in this frame, not only prevent
11025                 // another element change (checked in ChangeElement()), but
11026                 // also prevent additional element actions for this element
11027
11028                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11029                     !level.use_action_after_change_bug)
11030                   continue;
11031
11032                 ChangeDelay[x][y] = 1;
11033                 ChangeEvent[x][y] = trigger_event;
11034
11035                 HandleElementChange(x, y, p);
11036               }
11037               else if (change->has_action)
11038               {
11039                 // if element already changed in this frame, not only prevent
11040                 // another element change (checked in ChangeElement()), but
11041                 // also prevent additional element actions for this element
11042
11043                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11044                     !level.use_action_after_change_bug)
11045                   continue;
11046
11047                 ExecuteCustomElementAction(x, y, element, p);
11048                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11049               }
11050             }
11051           }
11052
11053           if (change->can_change)
11054           {
11055             change_done = TRUE;
11056             change_done_any = TRUE;
11057           }
11058         }
11059       }
11060     }
11061   }
11062
11063   RECURSION_LOOP_DETECTION_END();
11064
11065   return change_done_any;
11066 }
11067
11068 static boolean CheckElementChangeExt(int x, int y,
11069                                      int element,
11070                                      int trigger_element,
11071                                      int trigger_event,
11072                                      int trigger_player,
11073                                      int trigger_side)
11074 {
11075   boolean change_done = FALSE;
11076   int p;
11077
11078   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11079       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11080     return FALSE;
11081
11082   if (Tile[x][y] == EL_BLOCKED)
11083   {
11084     Blocked2Moving(x, y, &x, &y);
11085     element = Tile[x][y];
11086   }
11087
11088   // check if element has already changed or is about to change after moving
11089   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11090        Tile[x][y] != element) ||
11091
11092       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11093        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11094         ChangePage[x][y] != -1)))
11095     return FALSE;
11096
11097   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11098
11099   for (p = 0; p < element_info[element].num_change_pages; p++)
11100   {
11101     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11102
11103     /* check trigger element for all events where the element that is checked
11104        for changing interacts with a directly adjacent element -- this is
11105        different to element changes that affect other elements to change on the
11106        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11107     boolean check_trigger_element =
11108       (trigger_event == CE_TOUCHING_X ||
11109        trigger_event == CE_HITTING_X ||
11110        trigger_event == CE_HIT_BY_X ||
11111        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11112
11113     if (change->can_change_or_has_action &&
11114         change->has_event[trigger_event] &&
11115         change->trigger_side & trigger_side &&
11116         change->trigger_player & trigger_player &&
11117         (!check_trigger_element ||
11118          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11119     {
11120       change->actual_trigger_element = trigger_element;
11121       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11122       change->actual_trigger_player_bits = trigger_player;
11123       change->actual_trigger_side = trigger_side;
11124       change->actual_trigger_ce_value = CustomValue[x][y];
11125       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11126
11127       // special case: trigger element not at (x,y) position for some events
11128       if (check_trigger_element)
11129       {
11130         static struct
11131         {
11132           int dx, dy;
11133         } move_xy[] =
11134           {
11135             {  0,  0 },
11136             { -1,  0 },
11137             { +1,  0 },
11138             {  0,  0 },
11139             {  0, -1 },
11140             {  0,  0 }, { 0, 0 }, { 0, 0 },
11141             {  0, +1 }
11142           };
11143
11144         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11145         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11146
11147         change->actual_trigger_ce_value = CustomValue[xx][yy];
11148         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11149       }
11150
11151       if (change->can_change && !change_done)
11152       {
11153         ChangeDelay[x][y] = 1;
11154         ChangeEvent[x][y] = trigger_event;
11155
11156         HandleElementChange(x, y, p);
11157
11158         change_done = TRUE;
11159       }
11160       else if (change->has_action)
11161       {
11162         ExecuteCustomElementAction(x, y, element, p);
11163         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11164       }
11165     }
11166   }
11167
11168   RECURSION_LOOP_DETECTION_END();
11169
11170   return change_done;
11171 }
11172
11173 static void PlayPlayerSound(struct PlayerInfo *player)
11174 {
11175   int jx = player->jx, jy = player->jy;
11176   int sound_element = player->artwork_element;
11177   int last_action = player->last_action_waiting;
11178   int action = player->action_waiting;
11179
11180   if (player->is_waiting)
11181   {
11182     if (action != last_action)
11183       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11184     else
11185       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11186   }
11187   else
11188   {
11189     if (action != last_action)
11190       StopSound(element_info[sound_element].sound[last_action]);
11191
11192     if (last_action == ACTION_SLEEPING)
11193       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11194   }
11195 }
11196
11197 static void PlayAllPlayersSound(void)
11198 {
11199   int i;
11200
11201   for (i = 0; i < MAX_PLAYERS; i++)
11202     if (stored_player[i].active)
11203       PlayPlayerSound(&stored_player[i]);
11204 }
11205
11206 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11207 {
11208   boolean last_waiting = player->is_waiting;
11209   int move_dir = player->MovDir;
11210
11211   player->dir_waiting = move_dir;
11212   player->last_action_waiting = player->action_waiting;
11213
11214   if (is_waiting)
11215   {
11216     if (!last_waiting)          // not waiting -> waiting
11217     {
11218       player->is_waiting = TRUE;
11219
11220       player->frame_counter_bored =
11221         FrameCounter +
11222         game.player_boring_delay_fixed +
11223         GetSimpleRandom(game.player_boring_delay_random);
11224       player->frame_counter_sleeping =
11225         FrameCounter +
11226         game.player_sleeping_delay_fixed +
11227         GetSimpleRandom(game.player_sleeping_delay_random);
11228
11229       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11230     }
11231
11232     if (game.player_sleeping_delay_fixed +
11233         game.player_sleeping_delay_random > 0 &&
11234         player->anim_delay_counter == 0 &&
11235         player->post_delay_counter == 0 &&
11236         FrameCounter >= player->frame_counter_sleeping)
11237       player->is_sleeping = TRUE;
11238     else if (game.player_boring_delay_fixed +
11239              game.player_boring_delay_random > 0 &&
11240              FrameCounter >= player->frame_counter_bored)
11241       player->is_bored = TRUE;
11242
11243     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11244                               player->is_bored ? ACTION_BORING :
11245                               ACTION_WAITING);
11246
11247     if (player->is_sleeping && player->use_murphy)
11248     {
11249       // special case for sleeping Murphy when leaning against non-free tile
11250
11251       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11252           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11253            !IS_MOVING(player->jx - 1, player->jy)))
11254         move_dir = MV_LEFT;
11255       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11256                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11257                 !IS_MOVING(player->jx + 1, player->jy)))
11258         move_dir = MV_RIGHT;
11259       else
11260         player->is_sleeping = FALSE;
11261
11262       player->dir_waiting = move_dir;
11263     }
11264
11265     if (player->is_sleeping)
11266     {
11267       if (player->num_special_action_sleeping > 0)
11268       {
11269         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11270         {
11271           int last_special_action = player->special_action_sleeping;
11272           int num_special_action = player->num_special_action_sleeping;
11273           int special_action =
11274             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11275              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11276              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11277              last_special_action + 1 : ACTION_SLEEPING);
11278           int special_graphic =
11279             el_act_dir2img(player->artwork_element, special_action, move_dir);
11280
11281           player->anim_delay_counter =
11282             graphic_info[special_graphic].anim_delay_fixed +
11283             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11284           player->post_delay_counter =
11285             graphic_info[special_graphic].post_delay_fixed +
11286             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11287
11288           player->special_action_sleeping = special_action;
11289         }
11290
11291         if (player->anim_delay_counter > 0)
11292         {
11293           player->action_waiting = player->special_action_sleeping;
11294           player->anim_delay_counter--;
11295         }
11296         else if (player->post_delay_counter > 0)
11297         {
11298           player->post_delay_counter--;
11299         }
11300       }
11301     }
11302     else if (player->is_bored)
11303     {
11304       if (player->num_special_action_bored > 0)
11305       {
11306         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11307         {
11308           int special_action =
11309             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11310           int special_graphic =
11311             el_act_dir2img(player->artwork_element, special_action, move_dir);
11312
11313           player->anim_delay_counter =
11314             graphic_info[special_graphic].anim_delay_fixed +
11315             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11316           player->post_delay_counter =
11317             graphic_info[special_graphic].post_delay_fixed +
11318             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11319
11320           player->special_action_bored = special_action;
11321         }
11322
11323         if (player->anim_delay_counter > 0)
11324         {
11325           player->action_waiting = player->special_action_bored;
11326           player->anim_delay_counter--;
11327         }
11328         else if (player->post_delay_counter > 0)
11329         {
11330           player->post_delay_counter--;
11331         }
11332       }
11333     }
11334   }
11335   else if (last_waiting)        // waiting -> not waiting
11336   {
11337     player->is_waiting = FALSE;
11338     player->is_bored = FALSE;
11339     player->is_sleeping = FALSE;
11340
11341     player->frame_counter_bored = -1;
11342     player->frame_counter_sleeping = -1;
11343
11344     player->anim_delay_counter = 0;
11345     player->post_delay_counter = 0;
11346
11347     player->dir_waiting = player->MovDir;
11348     player->action_waiting = ACTION_DEFAULT;
11349
11350     player->special_action_bored = ACTION_DEFAULT;
11351     player->special_action_sleeping = ACTION_DEFAULT;
11352   }
11353 }
11354
11355 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11356 {
11357   if ((!player->is_moving  && player->was_moving) ||
11358       (player->MovPos == 0 && player->was_moving) ||
11359       (player->is_snapping && !player->was_snapping) ||
11360       (player->is_dropping && !player->was_dropping))
11361   {
11362     if (!CheckSaveEngineSnapshotToList())
11363       return;
11364
11365     player->was_moving = FALSE;
11366     player->was_snapping = TRUE;
11367     player->was_dropping = TRUE;
11368   }
11369   else
11370   {
11371     if (player->is_moving)
11372       player->was_moving = TRUE;
11373
11374     if (!player->is_snapping)
11375       player->was_snapping = FALSE;
11376
11377     if (!player->is_dropping)
11378       player->was_dropping = FALSE;
11379   }
11380
11381   static struct MouseActionInfo mouse_action_last = { 0 };
11382   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11383   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11384
11385   if (new_released)
11386     CheckSaveEngineSnapshotToList();
11387
11388   mouse_action_last = mouse_action;
11389 }
11390
11391 static void CheckSingleStepMode(struct PlayerInfo *player)
11392 {
11393   if (tape.single_step && tape.recording && !tape.pausing)
11394   {
11395     // as it is called "single step mode", just return to pause mode when the
11396     // player stopped moving after one tile (or never starts moving at all)
11397     // (reverse logic needed here in case single step mode used in team mode)
11398     if (player->is_moving ||
11399         player->is_pushing ||
11400         player->is_dropping_pressed ||
11401         player->effective_mouse_action.button)
11402       game.enter_single_step_mode = FALSE;
11403   }
11404
11405   CheckSaveEngineSnapshot(player);
11406 }
11407
11408 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11409 {
11410   int left      = player_action & JOY_LEFT;
11411   int right     = player_action & JOY_RIGHT;
11412   int up        = player_action & JOY_UP;
11413   int down      = player_action & JOY_DOWN;
11414   int button1   = player_action & JOY_BUTTON_1;
11415   int button2   = player_action & JOY_BUTTON_2;
11416   int dx        = (left ? -1 : right ? 1 : 0);
11417   int dy        = (up   ? -1 : down  ? 1 : 0);
11418
11419   if (!player->active || tape.pausing)
11420     return 0;
11421
11422   if (player_action)
11423   {
11424     if (button1)
11425       SnapField(player, dx, dy);
11426     else
11427     {
11428       if (button2)
11429         DropElement(player);
11430
11431       MovePlayer(player, dx, dy);
11432     }
11433
11434     CheckSingleStepMode(player);
11435
11436     SetPlayerWaiting(player, FALSE);
11437
11438     return player_action;
11439   }
11440   else
11441   {
11442     // no actions for this player (no input at player's configured device)
11443
11444     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11445     SnapField(player, 0, 0);
11446     CheckGravityMovementWhenNotMoving(player);
11447
11448     if (player->MovPos == 0)
11449       SetPlayerWaiting(player, TRUE);
11450
11451     if (player->MovPos == 0)    // needed for tape.playing
11452       player->is_moving = FALSE;
11453
11454     player->is_dropping = FALSE;
11455     player->is_dropping_pressed = FALSE;
11456     player->drop_pressed_delay = 0;
11457
11458     CheckSingleStepMode(player);
11459
11460     return 0;
11461   }
11462 }
11463
11464 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11465                                          byte *tape_action)
11466 {
11467   if (!tape.use_mouse_actions)
11468     return;
11469
11470   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11471   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11472   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11473 }
11474
11475 static void SetTapeActionFromMouseAction(byte *tape_action,
11476                                          struct MouseActionInfo *mouse_action)
11477 {
11478   if (!tape.use_mouse_actions)
11479     return;
11480
11481   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11482   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11483   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11484 }
11485
11486 static void CheckLevelSolved(void)
11487 {
11488   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11489   {
11490     if (game_em.level_solved &&
11491         !game_em.game_over)                             // game won
11492     {
11493       LevelSolved();
11494
11495       game_em.game_over = TRUE;
11496
11497       game.all_players_gone = TRUE;
11498     }
11499
11500     if (game_em.game_over)                              // game lost
11501       game.all_players_gone = TRUE;
11502   }
11503   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11504   {
11505     if (game_sp.level_solved &&
11506         !game_sp.game_over)                             // game won
11507     {
11508       LevelSolved();
11509
11510       game_sp.game_over = TRUE;
11511
11512       game.all_players_gone = TRUE;
11513     }
11514
11515     if (game_sp.game_over)                              // game lost
11516       game.all_players_gone = TRUE;
11517   }
11518   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11519   {
11520     if (game_mm.level_solved &&
11521         !game_mm.game_over)                             // game won
11522     {
11523       LevelSolved();
11524
11525       game_mm.game_over = TRUE;
11526
11527       game.all_players_gone = TRUE;
11528     }
11529
11530     if (game_mm.game_over)                              // game lost
11531       game.all_players_gone = TRUE;
11532   }
11533 }
11534
11535 static void CheckLevelTime(void)
11536 {
11537   int i;
11538
11539   if (TimeFrames >= FRAMES_PER_SECOND)
11540   {
11541     TimeFrames = 0;
11542     TapeTime++;
11543
11544     for (i = 0; i < MAX_PLAYERS; i++)
11545     {
11546       struct PlayerInfo *player = &stored_player[i];
11547
11548       if (SHIELD_ON(player))
11549       {
11550         player->shield_normal_time_left--;
11551
11552         if (player->shield_deadly_time_left > 0)
11553           player->shield_deadly_time_left--;
11554       }
11555     }
11556
11557     if (!game.LevelSolved && !level.use_step_counter)
11558     {
11559       TimePlayed++;
11560
11561       if (TimeLeft > 0)
11562       {
11563         TimeLeft--;
11564
11565         if (TimeLeft <= 10 && setup.time_limit)
11566           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11567
11568         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11569            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11570
11571         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11572
11573         if (!TimeLeft && setup.time_limit)
11574         {
11575           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11576             game_em.lev->killed_out_of_time = TRUE;
11577           else
11578             for (i = 0; i < MAX_PLAYERS; i++)
11579               KillPlayer(&stored_player[i]);
11580         }
11581       }
11582       else if (game.no_time_limit && !game.all_players_gone)
11583       {
11584         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11585       }
11586
11587       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11588     }
11589
11590     if (tape.recording || tape.playing)
11591       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11592   }
11593
11594   if (tape.recording || tape.playing)
11595     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11596
11597   UpdateAndDisplayGameControlValues();
11598 }
11599
11600 void AdvanceFrameAndPlayerCounters(int player_nr)
11601 {
11602   int i;
11603
11604   // advance frame counters (global frame counter and time frame counter)
11605   FrameCounter++;
11606   TimeFrames++;
11607
11608   // advance player counters (counters for move delay, move animation etc.)
11609   for (i = 0; i < MAX_PLAYERS; i++)
11610   {
11611     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11612     int move_delay_value = stored_player[i].move_delay_value;
11613     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11614
11615     if (!advance_player_counters)       // not all players may be affected
11616       continue;
11617
11618     if (move_frames == 0)       // less than one move per game frame
11619     {
11620       int stepsize = TILEX / move_delay_value;
11621       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11622       int count = (stored_player[i].is_moving ?
11623                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11624
11625       if (count % delay == 0)
11626         move_frames = 1;
11627     }
11628
11629     stored_player[i].Frame += move_frames;
11630
11631     if (stored_player[i].MovPos != 0)
11632       stored_player[i].StepFrame += move_frames;
11633
11634     if (stored_player[i].move_delay > 0)
11635       stored_player[i].move_delay--;
11636
11637     // due to bugs in previous versions, counter must count up, not down
11638     if (stored_player[i].push_delay != -1)
11639       stored_player[i].push_delay++;
11640
11641     if (stored_player[i].drop_delay > 0)
11642       stored_player[i].drop_delay--;
11643
11644     if (stored_player[i].is_dropping_pressed)
11645       stored_player[i].drop_pressed_delay++;
11646   }
11647 }
11648
11649 void StartGameActions(boolean init_network_game, boolean record_tape,
11650                       int random_seed)
11651 {
11652   unsigned int new_random_seed = InitRND(random_seed);
11653
11654   if (record_tape)
11655     TapeStartRecording(new_random_seed);
11656
11657   if (init_network_game)
11658   {
11659     SendToServer_LevelFile();
11660     SendToServer_StartPlaying();
11661
11662     return;
11663   }
11664
11665   InitGame();
11666 }
11667
11668 static void GameActionsExt(void)
11669 {
11670 #if 0
11671   static unsigned int game_frame_delay = 0;
11672 #endif
11673   unsigned int game_frame_delay_value;
11674   byte *recorded_player_action;
11675   byte summarized_player_action = 0;
11676   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11677   int i;
11678
11679   // detect endless loops, caused by custom element programming
11680   if (recursion_loop_detected && recursion_loop_depth == 0)
11681   {
11682     char *message = getStringCat3("Internal Error! Element ",
11683                                   EL_NAME(recursion_loop_element),
11684                                   " caused endless loop! Quit the game?");
11685
11686     Warn("element '%s' caused endless loop in game engine",
11687          EL_NAME(recursion_loop_element));
11688
11689     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11690
11691     recursion_loop_detected = FALSE;    // if game should be continued
11692
11693     free(message);
11694
11695     return;
11696   }
11697
11698   if (game.restart_level)
11699     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11700
11701   CheckLevelSolved();
11702
11703   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11704     GameWon();
11705
11706   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11707     TapeStop();
11708
11709   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11710     return;
11711
11712   game_frame_delay_value =
11713     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11714
11715   if (tape.playing && tape.warp_forward && !tape.pausing)
11716     game_frame_delay_value = 0;
11717
11718   SetVideoFrameDelay(game_frame_delay_value);
11719
11720   // (de)activate virtual buttons depending on current game status
11721   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11722   {
11723     if (game.all_players_gone)  // if no players there to be controlled anymore
11724       SetOverlayActive(FALSE);
11725     else if (!tape.playing)     // if game continues after tape stopped playing
11726       SetOverlayActive(TRUE);
11727   }
11728
11729 #if 0
11730 #if 0
11731   // ---------- main game synchronization point ----------
11732
11733   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11734
11735   Debug("game:playing:skip", "skip == %d", skip);
11736
11737 #else
11738   // ---------- main game synchronization point ----------
11739
11740   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11741 #endif
11742 #endif
11743
11744   if (network_playing && !network_player_action_received)
11745   {
11746     // try to get network player actions in time
11747
11748     // last chance to get network player actions without main loop delay
11749     HandleNetworking();
11750
11751     // game was quit by network peer
11752     if (game_status != GAME_MODE_PLAYING)
11753       return;
11754
11755     // check if network player actions still missing and game still running
11756     if (!network_player_action_received && !checkGameEnded())
11757       return;           // failed to get network player actions in time
11758
11759     // do not yet reset "network_player_action_received" (for tape.pausing)
11760   }
11761
11762   if (tape.pausing)
11763     return;
11764
11765   // at this point we know that we really continue executing the game
11766
11767   network_player_action_received = FALSE;
11768
11769   // when playing tape, read previously recorded player input from tape data
11770   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11771
11772   local_player->effective_mouse_action = local_player->mouse_action;
11773
11774   if (recorded_player_action != NULL)
11775     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11776                                  recorded_player_action);
11777
11778   // TapePlayAction() may return NULL when toggling to "pause before death"
11779   if (tape.pausing)
11780     return;
11781
11782   if (tape.set_centered_player)
11783   {
11784     game.centered_player_nr_next = tape.centered_player_nr_next;
11785     game.set_centered_player = TRUE;
11786   }
11787
11788   for (i = 0; i < MAX_PLAYERS; i++)
11789   {
11790     summarized_player_action |= stored_player[i].action;
11791
11792     if (!network_playing && (game.team_mode || tape.playing))
11793       stored_player[i].effective_action = stored_player[i].action;
11794   }
11795
11796   if (network_playing && !checkGameEnded())
11797     SendToServer_MovePlayer(summarized_player_action);
11798
11799   // summarize all actions at local players mapped input device position
11800   // (this allows using different input devices in single player mode)
11801   if (!network.enabled && !game.team_mode)
11802     stored_player[map_player_action[local_player->index_nr]].effective_action =
11803       summarized_player_action;
11804
11805   // summarize all actions at centered player in local team mode
11806   if (tape.recording &&
11807       setup.team_mode && !network.enabled &&
11808       setup.input_on_focus &&
11809       game.centered_player_nr != -1)
11810   {
11811     for (i = 0; i < MAX_PLAYERS; i++)
11812       stored_player[map_player_action[i]].effective_action =
11813         (i == game.centered_player_nr ? summarized_player_action : 0);
11814   }
11815
11816   if (recorded_player_action != NULL)
11817     for (i = 0; i < MAX_PLAYERS; i++)
11818       stored_player[i].effective_action = recorded_player_action[i];
11819
11820   for (i = 0; i < MAX_PLAYERS; i++)
11821   {
11822     tape_action[i] = stored_player[i].effective_action;
11823
11824     /* (this may happen in the RND game engine if a player was not present on
11825        the playfield on level start, but appeared later from a custom element */
11826     if (setup.team_mode &&
11827         tape.recording &&
11828         tape_action[i] &&
11829         !tape.player_participates[i])
11830       tape.player_participates[i] = TRUE;
11831   }
11832
11833   SetTapeActionFromMouseAction(tape_action,
11834                                &local_player->effective_mouse_action);
11835
11836   // only record actions from input devices, but not programmed actions
11837   if (tape.recording)
11838     TapeRecordAction(tape_action);
11839
11840   // remember if game was played (especially after tape stopped playing)
11841   if (!tape.playing && summarized_player_action)
11842     game.GamePlayed = TRUE;
11843
11844 #if USE_NEW_PLAYER_ASSIGNMENTS
11845   // !!! also map player actions in single player mode !!!
11846   // if (game.team_mode)
11847   if (1)
11848   {
11849     byte mapped_action[MAX_PLAYERS];
11850
11851 #if DEBUG_PLAYER_ACTIONS
11852     for (i = 0; i < MAX_PLAYERS; i++)
11853       DebugContinued("", "%d, ", stored_player[i].effective_action);
11854 #endif
11855
11856     for (i = 0; i < MAX_PLAYERS; i++)
11857       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11858
11859     for (i = 0; i < MAX_PLAYERS; i++)
11860       stored_player[i].effective_action = mapped_action[i];
11861
11862 #if DEBUG_PLAYER_ACTIONS
11863     DebugContinued("", "=> ");
11864     for (i = 0; i < MAX_PLAYERS; i++)
11865       DebugContinued("", "%d, ", stored_player[i].effective_action);
11866     DebugContinued("game:playing:player", "\n");
11867 #endif
11868   }
11869 #if DEBUG_PLAYER_ACTIONS
11870   else
11871   {
11872     for (i = 0; i < MAX_PLAYERS; i++)
11873       DebugContinued("", "%d, ", stored_player[i].effective_action);
11874     DebugContinued("game:playing:player", "\n");
11875   }
11876 #endif
11877 #endif
11878
11879   for (i = 0; i < MAX_PLAYERS; i++)
11880   {
11881     // allow engine snapshot in case of changed movement attempt
11882     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11883         (stored_player[i].effective_action & KEY_MOTION))
11884       game.snapshot.changed_action = TRUE;
11885
11886     // allow engine snapshot in case of snapping/dropping attempt
11887     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11888         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11889       game.snapshot.changed_action = TRUE;
11890
11891     game.snapshot.last_action[i] = stored_player[i].effective_action;
11892   }
11893
11894   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11895   {
11896     GameActions_EM_Main();
11897   }
11898   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11899   {
11900     GameActions_SP_Main();
11901   }
11902   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11903   {
11904     GameActions_MM_Main();
11905   }
11906   else
11907   {
11908     GameActions_RND_Main();
11909   }
11910
11911   BlitScreenToBitmap(backbuffer);
11912
11913   CheckLevelSolved();
11914   CheckLevelTime();
11915
11916   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11917
11918   if (global.show_frames_per_second)
11919   {
11920     static unsigned int fps_counter = 0;
11921     static int fps_frames = 0;
11922     unsigned int fps_delay_ms = Counter() - fps_counter;
11923
11924     fps_frames++;
11925
11926     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11927     {
11928       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11929
11930       fps_frames = 0;
11931       fps_counter = Counter();
11932
11933       // always draw FPS to screen after FPS value was updated
11934       redraw_mask |= REDRAW_FPS;
11935     }
11936
11937     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11938     if (GetDrawDeactivationMask() == REDRAW_NONE)
11939       redraw_mask |= REDRAW_FPS;
11940   }
11941 }
11942
11943 static void GameActions_CheckSaveEngineSnapshot(void)
11944 {
11945   if (!game.snapshot.save_snapshot)
11946     return;
11947
11948   // clear flag for saving snapshot _before_ saving snapshot
11949   game.snapshot.save_snapshot = FALSE;
11950
11951   SaveEngineSnapshotToList();
11952 }
11953
11954 void GameActions(void)
11955 {
11956   GameActionsExt();
11957
11958   GameActions_CheckSaveEngineSnapshot();
11959 }
11960
11961 void GameActions_EM_Main(void)
11962 {
11963   byte effective_action[MAX_PLAYERS];
11964   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11965   int i;
11966
11967   for (i = 0; i < MAX_PLAYERS; i++)
11968     effective_action[i] = stored_player[i].effective_action;
11969
11970   GameActions_EM(effective_action, warp_mode);
11971 }
11972
11973 void GameActions_SP_Main(void)
11974 {
11975   byte effective_action[MAX_PLAYERS];
11976   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11977   int i;
11978
11979   for (i = 0; i < MAX_PLAYERS; i++)
11980     effective_action[i] = stored_player[i].effective_action;
11981
11982   GameActions_SP(effective_action, warp_mode);
11983
11984   for (i = 0; i < MAX_PLAYERS; i++)
11985   {
11986     if (stored_player[i].force_dropping)
11987       stored_player[i].action |= KEY_BUTTON_DROP;
11988
11989     stored_player[i].force_dropping = FALSE;
11990   }
11991 }
11992
11993 void GameActions_MM_Main(void)
11994 {
11995   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11996
11997   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11998 }
11999
12000 void GameActions_RND_Main(void)
12001 {
12002   GameActions_RND();
12003 }
12004
12005 void GameActions_RND(void)
12006 {
12007   static struct MouseActionInfo mouse_action_last = { 0 };
12008   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12009   int magic_wall_x = 0, magic_wall_y = 0;
12010   int i, x, y, element, graphic, last_gfx_frame;
12011
12012   InitPlayfieldScanModeVars();
12013
12014   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12015   {
12016     SCAN_PLAYFIELD(x, y)
12017     {
12018       ChangeCount[x][y] = 0;
12019       ChangeEvent[x][y] = -1;
12020     }
12021   }
12022
12023   if (game.set_centered_player)
12024   {
12025     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12026
12027     // switching to "all players" only possible if all players fit to screen
12028     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12029     {
12030       game.centered_player_nr_next = game.centered_player_nr;
12031       game.set_centered_player = FALSE;
12032     }
12033
12034     // do not switch focus to non-existing (or non-active) player
12035     if (game.centered_player_nr_next >= 0 &&
12036         !stored_player[game.centered_player_nr_next].active)
12037     {
12038       game.centered_player_nr_next = game.centered_player_nr;
12039       game.set_centered_player = FALSE;
12040     }
12041   }
12042
12043   if (game.set_centered_player &&
12044       ScreenMovPos == 0)        // screen currently aligned at tile position
12045   {
12046     int sx, sy;
12047
12048     if (game.centered_player_nr_next == -1)
12049     {
12050       setScreenCenteredToAllPlayers(&sx, &sy);
12051     }
12052     else
12053     {
12054       sx = stored_player[game.centered_player_nr_next].jx;
12055       sy = stored_player[game.centered_player_nr_next].jy;
12056     }
12057
12058     game.centered_player_nr = game.centered_player_nr_next;
12059     game.set_centered_player = FALSE;
12060
12061     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12062     DrawGameDoorValues();
12063   }
12064
12065   // check single step mode (set flag and clear again if any player is active)
12066   game.enter_single_step_mode =
12067     (tape.single_step && tape.recording && !tape.pausing);
12068
12069   for (i = 0; i < MAX_PLAYERS; i++)
12070   {
12071     int actual_player_action = stored_player[i].effective_action;
12072
12073 #if 1
12074     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12075        - rnd_equinox_tetrachloride 048
12076        - rnd_equinox_tetrachloride_ii 096
12077        - rnd_emanuel_schmieg 002
12078        - doctor_sloan_ww 001, 020
12079     */
12080     if (stored_player[i].MovPos == 0)
12081       CheckGravityMovement(&stored_player[i]);
12082 #endif
12083
12084     // overwrite programmed action with tape action
12085     if (stored_player[i].programmed_action)
12086       actual_player_action = stored_player[i].programmed_action;
12087
12088     PlayerActions(&stored_player[i], actual_player_action);
12089
12090     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12091   }
12092
12093   // single step pause mode may already have been toggled by "ScrollPlayer()"
12094   if (game.enter_single_step_mode && !tape.pausing)
12095     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12096
12097   ScrollScreen(NULL, SCROLL_GO_ON);
12098
12099   /* for backwards compatibility, the following code emulates a fixed bug that
12100      occured when pushing elements (causing elements that just made their last
12101      pushing step to already (if possible) make their first falling step in the
12102      same game frame, which is bad); this code is also needed to use the famous
12103      "spring push bug" which is used in older levels and might be wanted to be
12104      used also in newer levels, but in this case the buggy pushing code is only
12105      affecting the "spring" element and no other elements */
12106
12107   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12108   {
12109     for (i = 0; i < MAX_PLAYERS; i++)
12110     {
12111       struct PlayerInfo *player = &stored_player[i];
12112       int x = player->jx;
12113       int y = player->jy;
12114
12115       if (player->active && player->is_pushing && player->is_moving &&
12116           IS_MOVING(x, y) &&
12117           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12118            Tile[x][y] == EL_SPRING))
12119       {
12120         ContinueMoving(x, y);
12121
12122         // continue moving after pushing (this is actually a bug)
12123         if (!IS_MOVING(x, y))
12124           Stop[x][y] = FALSE;
12125       }
12126     }
12127   }
12128
12129   SCAN_PLAYFIELD(x, y)
12130   {
12131     Last[x][y] = Tile[x][y];
12132
12133     ChangeCount[x][y] = 0;
12134     ChangeEvent[x][y] = -1;
12135
12136     // this must be handled before main playfield loop
12137     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12138     {
12139       MovDelay[x][y]--;
12140       if (MovDelay[x][y] <= 0)
12141         RemoveField(x, y);
12142     }
12143
12144     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12145     {
12146       MovDelay[x][y]--;
12147       if (MovDelay[x][y] <= 0)
12148       {
12149         int element = Store[x][y];
12150         int move_direction = MovDir[x][y];
12151         int player_index_bit = Store2[x][y];
12152
12153         Store[x][y] = 0;
12154         Store2[x][y] = 0;
12155
12156         RemoveField(x, y);
12157         TEST_DrawLevelField(x, y);
12158
12159         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12160       }
12161     }
12162
12163 #if DEBUG
12164     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12165     {
12166       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12167             x, y);
12168       Debug("game:playing:GameActions_RND", "This should never happen!");
12169
12170       ChangePage[x][y] = -1;
12171     }
12172 #endif
12173
12174     Stop[x][y] = FALSE;
12175     if (WasJustMoving[x][y] > 0)
12176       WasJustMoving[x][y]--;
12177     if (WasJustFalling[x][y] > 0)
12178       WasJustFalling[x][y]--;
12179     if (CheckCollision[x][y] > 0)
12180       CheckCollision[x][y]--;
12181     if (CheckImpact[x][y] > 0)
12182       CheckImpact[x][y]--;
12183
12184     GfxFrame[x][y]++;
12185
12186     /* reset finished pushing action (not done in ContinueMoving() to allow
12187        continuous pushing animation for elements with zero push delay) */
12188     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12189     {
12190       ResetGfxAnimation(x, y);
12191       TEST_DrawLevelField(x, y);
12192     }
12193
12194 #if DEBUG
12195     if (IS_BLOCKED(x, y))
12196     {
12197       int oldx, oldy;
12198
12199       Blocked2Moving(x, y, &oldx, &oldy);
12200       if (!IS_MOVING(oldx, oldy))
12201       {
12202         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12203         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12204         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12205         Debug("game:playing:GameActions_RND", "This should never happen!");
12206       }
12207     }
12208 #endif
12209   }
12210
12211   if (mouse_action.button)
12212   {
12213     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12214     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12215
12216     x = mouse_action.lx;
12217     y = mouse_action.ly;
12218     element = Tile[x][y];
12219
12220     if (new_button)
12221     {
12222       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12223       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12224                                          ch_button);
12225     }
12226
12227     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12228     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12229                                        ch_button);
12230   }
12231
12232   SCAN_PLAYFIELD(x, y)
12233   {
12234     element = Tile[x][y];
12235     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12236     last_gfx_frame = GfxFrame[x][y];
12237
12238     ResetGfxFrame(x, y);
12239
12240     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12241       DrawLevelGraphicAnimation(x, y, graphic);
12242
12243     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12244         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12245       ResetRandomAnimationValue(x, y);
12246
12247     SetRandomAnimationValue(x, y);
12248
12249     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12250
12251     if (IS_INACTIVE(element))
12252     {
12253       if (IS_ANIMATED(graphic))
12254         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12255
12256       continue;
12257     }
12258
12259     // this may take place after moving, so 'element' may have changed
12260     if (IS_CHANGING(x, y) &&
12261         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12262     {
12263       int page = element_info[element].event_page_nr[CE_DELAY];
12264
12265       HandleElementChange(x, y, page);
12266
12267       element = Tile[x][y];
12268       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12269     }
12270
12271     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12272     {
12273       StartMoving(x, y);
12274
12275       element = Tile[x][y];
12276       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12277
12278       if (IS_ANIMATED(graphic) &&
12279           !IS_MOVING(x, y) &&
12280           !Stop[x][y])
12281         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12282
12283       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12284         TEST_DrawTwinkleOnField(x, y);
12285     }
12286     else if (element == EL_ACID)
12287     {
12288       if (!Stop[x][y])
12289         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12290     }
12291     else if ((element == EL_EXIT_OPEN ||
12292               element == EL_EM_EXIT_OPEN ||
12293               element == EL_SP_EXIT_OPEN ||
12294               element == EL_STEEL_EXIT_OPEN ||
12295               element == EL_EM_STEEL_EXIT_OPEN ||
12296               element == EL_SP_TERMINAL ||
12297               element == EL_SP_TERMINAL_ACTIVE ||
12298               element == EL_EXTRA_TIME ||
12299               element == EL_SHIELD_NORMAL ||
12300               element == EL_SHIELD_DEADLY) &&
12301              IS_ANIMATED(graphic))
12302       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12303     else if (IS_MOVING(x, y))
12304       ContinueMoving(x, y);
12305     else if (IS_ACTIVE_BOMB(element))
12306       CheckDynamite(x, y);
12307     else if (element == EL_AMOEBA_GROWING)
12308       AmoebaGrowing(x, y);
12309     else if (element == EL_AMOEBA_SHRINKING)
12310       AmoebaShrinking(x, y);
12311
12312 #if !USE_NEW_AMOEBA_CODE
12313     else if (IS_AMOEBALIVE(element))
12314       AmoebaReproduce(x, y);
12315 #endif
12316
12317     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12318       Life(x, y);
12319     else if (element == EL_EXIT_CLOSED)
12320       CheckExit(x, y);
12321     else if (element == EL_EM_EXIT_CLOSED)
12322       CheckExitEM(x, y);
12323     else if (element == EL_STEEL_EXIT_CLOSED)
12324       CheckExitSteel(x, y);
12325     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12326       CheckExitSteelEM(x, y);
12327     else if (element == EL_SP_EXIT_CLOSED)
12328       CheckExitSP(x, y);
12329     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12330              element == EL_EXPANDABLE_STEELWALL_GROWING)
12331       MauerWaechst(x, y);
12332     else if (element == EL_EXPANDABLE_WALL ||
12333              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12334              element == EL_EXPANDABLE_WALL_VERTICAL ||
12335              element == EL_EXPANDABLE_WALL_ANY ||
12336              element == EL_BD_EXPANDABLE_WALL)
12337       MauerAbleger(x, y);
12338     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12339              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12340              element == EL_EXPANDABLE_STEELWALL_ANY)
12341       MauerAblegerStahl(x, y);
12342     else if (element == EL_FLAMES)
12343       CheckForDragon(x, y);
12344     else if (element == EL_EXPLOSION)
12345       ; // drawing of correct explosion animation is handled separately
12346     else if (element == EL_ELEMENT_SNAPPING ||
12347              element == EL_DIAGONAL_SHRINKING ||
12348              element == EL_DIAGONAL_GROWING)
12349     {
12350       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12351
12352       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12353     }
12354     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12355       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12356
12357     if (IS_BELT_ACTIVE(element))
12358       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12359
12360     if (game.magic_wall_active)
12361     {
12362       int jx = local_player->jx, jy = local_player->jy;
12363
12364       // play the element sound at the position nearest to the player
12365       if ((element == EL_MAGIC_WALL_FULL ||
12366            element == EL_MAGIC_WALL_ACTIVE ||
12367            element == EL_MAGIC_WALL_EMPTYING ||
12368            element == EL_BD_MAGIC_WALL_FULL ||
12369            element == EL_BD_MAGIC_WALL_ACTIVE ||
12370            element == EL_BD_MAGIC_WALL_EMPTYING ||
12371            element == EL_DC_MAGIC_WALL_FULL ||
12372            element == EL_DC_MAGIC_WALL_ACTIVE ||
12373            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12374           ABS(x - jx) + ABS(y - jy) <
12375           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12376       {
12377         magic_wall_x = x;
12378         magic_wall_y = y;
12379       }
12380     }
12381   }
12382
12383 #if USE_NEW_AMOEBA_CODE
12384   // new experimental amoeba growth stuff
12385   if (!(FrameCounter % 8))
12386   {
12387     static unsigned int random = 1684108901;
12388
12389     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12390     {
12391       x = RND(lev_fieldx);
12392       y = RND(lev_fieldy);
12393       element = Tile[x][y];
12394
12395       if (!IS_PLAYER(x,y) &&
12396           (element == EL_EMPTY ||
12397            CAN_GROW_INTO(element) ||
12398            element == EL_QUICKSAND_EMPTY ||
12399            element == EL_QUICKSAND_FAST_EMPTY ||
12400            element == EL_ACID_SPLASH_LEFT ||
12401            element == EL_ACID_SPLASH_RIGHT))
12402       {
12403         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12404             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12405             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12406             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12407           Tile[x][y] = EL_AMOEBA_DROP;
12408       }
12409
12410       random = random * 129 + 1;
12411     }
12412   }
12413 #endif
12414
12415   game.explosions_delayed = FALSE;
12416
12417   SCAN_PLAYFIELD(x, y)
12418   {
12419     element = Tile[x][y];
12420
12421     if (ExplodeField[x][y])
12422       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12423     else if (element == EL_EXPLOSION)
12424       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12425
12426     ExplodeField[x][y] = EX_TYPE_NONE;
12427   }
12428
12429   game.explosions_delayed = TRUE;
12430
12431   if (game.magic_wall_active)
12432   {
12433     if (!(game.magic_wall_time_left % 4))
12434     {
12435       int element = Tile[magic_wall_x][magic_wall_y];
12436
12437       if (element == EL_BD_MAGIC_WALL_FULL ||
12438           element == EL_BD_MAGIC_WALL_ACTIVE ||
12439           element == EL_BD_MAGIC_WALL_EMPTYING)
12440         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12441       else if (element == EL_DC_MAGIC_WALL_FULL ||
12442                element == EL_DC_MAGIC_WALL_ACTIVE ||
12443                element == EL_DC_MAGIC_WALL_EMPTYING)
12444         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12445       else
12446         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12447     }
12448
12449     if (game.magic_wall_time_left > 0)
12450     {
12451       game.magic_wall_time_left--;
12452
12453       if (!game.magic_wall_time_left)
12454       {
12455         SCAN_PLAYFIELD(x, y)
12456         {
12457           element = Tile[x][y];
12458
12459           if (element == EL_MAGIC_WALL_ACTIVE ||
12460               element == EL_MAGIC_WALL_FULL)
12461           {
12462             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12463             TEST_DrawLevelField(x, y);
12464           }
12465           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12466                    element == EL_BD_MAGIC_WALL_FULL)
12467           {
12468             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12469             TEST_DrawLevelField(x, y);
12470           }
12471           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12472                    element == EL_DC_MAGIC_WALL_FULL)
12473           {
12474             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12475             TEST_DrawLevelField(x, y);
12476           }
12477         }
12478
12479         game.magic_wall_active = FALSE;
12480       }
12481     }
12482   }
12483
12484   if (game.light_time_left > 0)
12485   {
12486     game.light_time_left--;
12487
12488     if (game.light_time_left == 0)
12489       RedrawAllLightSwitchesAndInvisibleElements();
12490   }
12491
12492   if (game.timegate_time_left > 0)
12493   {
12494     game.timegate_time_left--;
12495
12496     if (game.timegate_time_left == 0)
12497       CloseAllOpenTimegates();
12498   }
12499
12500   if (game.lenses_time_left > 0)
12501   {
12502     game.lenses_time_left--;
12503
12504     if (game.lenses_time_left == 0)
12505       RedrawAllInvisibleElementsForLenses();
12506   }
12507
12508   if (game.magnify_time_left > 0)
12509   {
12510     game.magnify_time_left--;
12511
12512     if (game.magnify_time_left == 0)
12513       RedrawAllInvisibleElementsForMagnifier();
12514   }
12515
12516   for (i = 0; i < MAX_PLAYERS; i++)
12517   {
12518     struct PlayerInfo *player = &stored_player[i];
12519
12520     if (SHIELD_ON(player))
12521     {
12522       if (player->shield_deadly_time_left)
12523         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12524       else if (player->shield_normal_time_left)
12525         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12526     }
12527   }
12528
12529 #if USE_DELAYED_GFX_REDRAW
12530   SCAN_PLAYFIELD(x, y)
12531   {
12532     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12533     {
12534       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12535          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12536
12537       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12538         DrawLevelField(x, y);
12539
12540       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12541         DrawLevelFieldCrumbled(x, y);
12542
12543       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12544         DrawLevelFieldCrumbledNeighbours(x, y);
12545
12546       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12547         DrawTwinkleOnField(x, y);
12548     }
12549
12550     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12551   }
12552 #endif
12553
12554   DrawAllPlayers();
12555   PlayAllPlayersSound();
12556
12557   for (i = 0; i < MAX_PLAYERS; i++)
12558   {
12559     struct PlayerInfo *player = &stored_player[i];
12560
12561     if (player->show_envelope != 0 && (!player->active ||
12562                                        player->MovPos == 0))
12563     {
12564       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12565
12566       player->show_envelope = 0;
12567     }
12568   }
12569
12570   // use random number generator in every frame to make it less predictable
12571   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12572     RND(1);
12573
12574   mouse_action_last = mouse_action;
12575 }
12576
12577 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12578 {
12579   int min_x = x, min_y = y, max_x = x, max_y = y;
12580   int scr_fieldx = getScreenFieldSizeX();
12581   int scr_fieldy = getScreenFieldSizeY();
12582   int i;
12583
12584   for (i = 0; i < MAX_PLAYERS; i++)
12585   {
12586     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12587
12588     if (!stored_player[i].active || &stored_player[i] == player)
12589       continue;
12590
12591     min_x = MIN(min_x, jx);
12592     min_y = MIN(min_y, jy);
12593     max_x = MAX(max_x, jx);
12594     max_y = MAX(max_y, jy);
12595   }
12596
12597   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12598 }
12599
12600 static boolean AllPlayersInVisibleScreen(void)
12601 {
12602   int i;
12603
12604   for (i = 0; i < MAX_PLAYERS; i++)
12605   {
12606     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12607
12608     if (!stored_player[i].active)
12609       continue;
12610
12611     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12612       return FALSE;
12613   }
12614
12615   return TRUE;
12616 }
12617
12618 void ScrollLevel(int dx, int dy)
12619 {
12620   int scroll_offset = 2 * TILEX_VAR;
12621   int x, y;
12622
12623   BlitBitmap(drawto_field, drawto_field,
12624              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12625              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12626              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12627              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12628              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12629              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12630
12631   if (dx != 0)
12632   {
12633     x = (dx == 1 ? BX1 : BX2);
12634     for (y = BY1; y <= BY2; y++)
12635       DrawScreenField(x, y);
12636   }
12637
12638   if (dy != 0)
12639   {
12640     y = (dy == 1 ? BY1 : BY2);
12641     for (x = BX1; x <= BX2; x++)
12642       DrawScreenField(x, y);
12643   }
12644
12645   redraw_mask |= REDRAW_FIELD;
12646 }
12647
12648 static boolean canFallDown(struct PlayerInfo *player)
12649 {
12650   int jx = player->jx, jy = player->jy;
12651
12652   return (IN_LEV_FIELD(jx, jy + 1) &&
12653           (IS_FREE(jx, jy + 1) ||
12654            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12655           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12656           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12657 }
12658
12659 static boolean canPassField(int x, int y, int move_dir)
12660 {
12661   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12662   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12663   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12664   int nextx = x + dx;
12665   int nexty = y + dy;
12666   int element = Tile[x][y];
12667
12668   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12669           !CAN_MOVE(element) &&
12670           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12671           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12672           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12673 }
12674
12675 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12676 {
12677   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12678   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12679   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12680   int newx = x + dx;
12681   int newy = y + dy;
12682
12683   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12684           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12685           (IS_DIGGABLE(Tile[newx][newy]) ||
12686            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12687            canPassField(newx, newy, move_dir)));
12688 }
12689
12690 static void CheckGravityMovement(struct PlayerInfo *player)
12691 {
12692   if (player->gravity && !player->programmed_action)
12693   {
12694     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12695     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12696     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12697     int jx = player->jx, jy = player->jy;
12698     boolean player_is_moving_to_valid_field =
12699       (!player_is_snapping &&
12700        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12701         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12702     boolean player_can_fall_down = canFallDown(player);
12703
12704     if (player_can_fall_down &&
12705         !player_is_moving_to_valid_field)
12706       player->programmed_action = MV_DOWN;
12707   }
12708 }
12709
12710 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12711 {
12712   return CheckGravityMovement(player);
12713
12714   if (player->gravity && !player->programmed_action)
12715   {
12716     int jx = player->jx, jy = player->jy;
12717     boolean field_under_player_is_free =
12718       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12719     boolean player_is_standing_on_valid_field =
12720       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12721        (IS_WALKABLE(Tile[jx][jy]) &&
12722         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12723
12724     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12725       player->programmed_action = MV_DOWN;
12726   }
12727 }
12728
12729 /*
12730   MovePlayerOneStep()
12731   -----------------------------------------------------------------------------
12732   dx, dy:               direction (non-diagonal) to try to move the player to
12733   real_dx, real_dy:     direction as read from input device (can be diagonal)
12734 */
12735
12736 boolean MovePlayerOneStep(struct PlayerInfo *player,
12737                           int dx, int dy, int real_dx, int real_dy)
12738 {
12739   int jx = player->jx, jy = player->jy;
12740   int new_jx = jx + dx, new_jy = jy + dy;
12741   int can_move;
12742   boolean player_can_move = !player->cannot_move;
12743
12744   if (!player->active || (!dx && !dy))
12745     return MP_NO_ACTION;
12746
12747   player->MovDir = (dx < 0 ? MV_LEFT :
12748                     dx > 0 ? MV_RIGHT :
12749                     dy < 0 ? MV_UP :
12750                     dy > 0 ? MV_DOWN :  MV_NONE);
12751
12752   if (!IN_LEV_FIELD(new_jx, new_jy))
12753     return MP_NO_ACTION;
12754
12755   if (!player_can_move)
12756   {
12757     if (player->MovPos == 0)
12758     {
12759       player->is_moving = FALSE;
12760       player->is_digging = FALSE;
12761       player->is_collecting = FALSE;
12762       player->is_snapping = FALSE;
12763       player->is_pushing = FALSE;
12764     }
12765   }
12766
12767   if (!network.enabled && game.centered_player_nr == -1 &&
12768       !AllPlayersInSight(player, new_jx, new_jy))
12769     return MP_NO_ACTION;
12770
12771   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12772   if (can_move != MP_MOVING)
12773     return can_move;
12774
12775   // check if DigField() has caused relocation of the player
12776   if (player->jx != jx || player->jy != jy)
12777     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12778
12779   StorePlayer[jx][jy] = 0;
12780   player->last_jx = jx;
12781   player->last_jy = jy;
12782   player->jx = new_jx;
12783   player->jy = new_jy;
12784   StorePlayer[new_jx][new_jy] = player->element_nr;
12785
12786   if (player->move_delay_value_next != -1)
12787   {
12788     player->move_delay_value = player->move_delay_value_next;
12789     player->move_delay_value_next = -1;
12790   }
12791
12792   player->MovPos =
12793     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12794
12795   player->step_counter++;
12796
12797   PlayerVisit[jx][jy] = FrameCounter;
12798
12799   player->is_moving = TRUE;
12800
12801 #if 1
12802   // should better be called in MovePlayer(), but this breaks some tapes
12803   ScrollPlayer(player, SCROLL_INIT);
12804 #endif
12805
12806   return MP_MOVING;
12807 }
12808
12809 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12810 {
12811   int jx = player->jx, jy = player->jy;
12812   int old_jx = jx, old_jy = jy;
12813   int moved = MP_NO_ACTION;
12814
12815   if (!player->active)
12816     return FALSE;
12817
12818   if (!dx && !dy)
12819   {
12820     if (player->MovPos == 0)
12821     {
12822       player->is_moving = FALSE;
12823       player->is_digging = FALSE;
12824       player->is_collecting = FALSE;
12825       player->is_snapping = FALSE;
12826       player->is_pushing = FALSE;
12827     }
12828
12829     return FALSE;
12830   }
12831
12832   if (player->move_delay > 0)
12833     return FALSE;
12834
12835   player->move_delay = -1;              // set to "uninitialized" value
12836
12837   // store if player is automatically moved to next field
12838   player->is_auto_moving = (player->programmed_action != MV_NONE);
12839
12840   // remove the last programmed player action
12841   player->programmed_action = 0;
12842
12843   if (player->MovPos)
12844   {
12845     // should only happen if pre-1.2 tape recordings are played
12846     // this is only for backward compatibility
12847
12848     int original_move_delay_value = player->move_delay_value;
12849
12850 #if DEBUG
12851     Debug("game:playing:MovePlayer",
12852           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12853           tape.counter);
12854 #endif
12855
12856     // scroll remaining steps with finest movement resolution
12857     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12858
12859     while (player->MovPos)
12860     {
12861       ScrollPlayer(player, SCROLL_GO_ON);
12862       ScrollScreen(NULL, SCROLL_GO_ON);
12863
12864       AdvanceFrameAndPlayerCounters(player->index_nr);
12865
12866       DrawAllPlayers();
12867       BackToFront_WithFrameDelay(0);
12868     }
12869
12870     player->move_delay_value = original_move_delay_value;
12871   }
12872
12873   player->is_active = FALSE;
12874
12875   if (player->last_move_dir & MV_HORIZONTAL)
12876   {
12877     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12878       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12879   }
12880   else
12881   {
12882     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12883       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12884   }
12885
12886   if (!moved && !player->is_active)
12887   {
12888     player->is_moving = FALSE;
12889     player->is_digging = FALSE;
12890     player->is_collecting = FALSE;
12891     player->is_snapping = FALSE;
12892     player->is_pushing = FALSE;
12893   }
12894
12895   jx = player->jx;
12896   jy = player->jy;
12897
12898   if (moved & MP_MOVING && !ScreenMovPos &&
12899       (player->index_nr == game.centered_player_nr ||
12900        game.centered_player_nr == -1))
12901   {
12902     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12903
12904     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12905     {
12906       // actual player has left the screen -- scroll in that direction
12907       if (jx != old_jx)         // player has moved horizontally
12908         scroll_x += (jx - old_jx);
12909       else                      // player has moved vertically
12910         scroll_y += (jy - old_jy);
12911     }
12912     else
12913     {
12914       int offset_raw = game.scroll_delay_value;
12915
12916       if (jx != old_jx)         // player has moved horizontally
12917       {
12918         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12919         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12920         int new_scroll_x = jx - MIDPOSX + offset_x;
12921
12922         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12923             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12924           scroll_x = new_scroll_x;
12925
12926         // don't scroll over playfield boundaries
12927         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12928
12929         // don't scroll more than one field at a time
12930         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12931
12932         // don't scroll against the player's moving direction
12933         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12934             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12935           scroll_x = old_scroll_x;
12936       }
12937       else                      // player has moved vertically
12938       {
12939         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12940         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12941         int new_scroll_y = jy - MIDPOSY + offset_y;
12942
12943         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12944             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12945           scroll_y = new_scroll_y;
12946
12947         // don't scroll over playfield boundaries
12948         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12949
12950         // don't scroll more than one field at a time
12951         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12952
12953         // don't scroll against the player's moving direction
12954         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12955             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12956           scroll_y = old_scroll_y;
12957       }
12958     }
12959
12960     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12961     {
12962       if (!network.enabled && game.centered_player_nr == -1 &&
12963           !AllPlayersInVisibleScreen())
12964       {
12965         scroll_x = old_scroll_x;
12966         scroll_y = old_scroll_y;
12967       }
12968       else
12969       {
12970         ScrollScreen(player, SCROLL_INIT);
12971         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12972       }
12973     }
12974   }
12975
12976   player->StepFrame = 0;
12977
12978   if (moved & MP_MOVING)
12979   {
12980     if (old_jx != jx && old_jy == jy)
12981       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12982     else if (old_jx == jx && old_jy != jy)
12983       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12984
12985     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12986
12987     player->last_move_dir = player->MovDir;
12988     player->is_moving = TRUE;
12989     player->is_snapping = FALSE;
12990     player->is_switching = FALSE;
12991     player->is_dropping = FALSE;
12992     player->is_dropping_pressed = FALSE;
12993     player->drop_pressed_delay = 0;
12994
12995 #if 0
12996     // should better be called here than above, but this breaks some tapes
12997     ScrollPlayer(player, SCROLL_INIT);
12998 #endif
12999   }
13000   else
13001   {
13002     CheckGravityMovementWhenNotMoving(player);
13003
13004     player->is_moving = FALSE;
13005
13006     /* at this point, the player is allowed to move, but cannot move right now
13007        (e.g. because of something blocking the way) -- ensure that the player
13008        is also allowed to move in the next frame (in old versions before 3.1.1,
13009        the player was forced to wait again for eight frames before next try) */
13010
13011     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13012       player->move_delay = 0;   // allow direct movement in the next frame
13013   }
13014
13015   if (player->move_delay == -1)         // not yet initialized by DigField()
13016     player->move_delay = player->move_delay_value;
13017
13018   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13019   {
13020     TestIfPlayerTouchesBadThing(jx, jy);
13021     TestIfPlayerTouchesCustomElement(jx, jy);
13022   }
13023
13024   if (!player->active)
13025     RemovePlayer(player);
13026
13027   return moved;
13028 }
13029
13030 void ScrollPlayer(struct PlayerInfo *player, int mode)
13031 {
13032   int jx = player->jx, jy = player->jy;
13033   int last_jx = player->last_jx, last_jy = player->last_jy;
13034   int move_stepsize = TILEX / player->move_delay_value;
13035
13036   if (!player->active)
13037     return;
13038
13039   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13040     return;
13041
13042   if (mode == SCROLL_INIT)
13043   {
13044     player->actual_frame_counter = FrameCounter;
13045     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13046
13047     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13048         Tile[last_jx][last_jy] == EL_EMPTY)
13049     {
13050       int last_field_block_delay = 0;   // start with no blocking at all
13051       int block_delay_adjustment = player->block_delay_adjustment;
13052
13053       // if player blocks last field, add delay for exactly one move
13054       if (player->block_last_field)
13055       {
13056         last_field_block_delay += player->move_delay_value;
13057
13058         // when blocking enabled, prevent moving up despite gravity
13059         if (player->gravity && player->MovDir == MV_UP)
13060           block_delay_adjustment = -1;
13061       }
13062
13063       // add block delay adjustment (also possible when not blocking)
13064       last_field_block_delay += block_delay_adjustment;
13065
13066       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13067       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13068     }
13069
13070     if (player->MovPos != 0)    // player has not yet reached destination
13071       return;
13072   }
13073   else if (!FrameReached(&player->actual_frame_counter, 1))
13074     return;
13075
13076   if (player->MovPos != 0)
13077   {
13078     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13079     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13080
13081     // before DrawPlayer() to draw correct player graphic for this case
13082     if (player->MovPos == 0)
13083       CheckGravityMovement(player);
13084   }
13085
13086   if (player->MovPos == 0)      // player reached destination field
13087   {
13088     if (player->move_delay_reset_counter > 0)
13089     {
13090       player->move_delay_reset_counter--;
13091
13092       if (player->move_delay_reset_counter == 0)
13093       {
13094         // continue with normal speed after quickly moving through gate
13095         HALVE_PLAYER_SPEED(player);
13096
13097         // be able to make the next move without delay
13098         player->move_delay = 0;
13099       }
13100     }
13101
13102     player->last_jx = jx;
13103     player->last_jy = jy;
13104
13105     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13106         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13107         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13108         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13109         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13110         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13111         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13112         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13113     {
13114       ExitPlayer(player);
13115
13116       if (game.players_still_needed == 0 &&
13117           (game.friends_still_needed == 0 ||
13118            IS_SP_ELEMENT(Tile[jx][jy])))
13119         LevelSolved();
13120     }
13121
13122     // this breaks one level: "machine", level 000
13123     {
13124       int move_direction = player->MovDir;
13125       int enter_side = MV_DIR_OPPOSITE(move_direction);
13126       int leave_side = move_direction;
13127       int old_jx = last_jx;
13128       int old_jy = last_jy;
13129       int old_element = Tile[old_jx][old_jy];
13130       int new_element = Tile[jx][jy];
13131
13132       if (IS_CUSTOM_ELEMENT(old_element))
13133         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13134                                    CE_LEFT_BY_PLAYER,
13135                                    player->index_bit, leave_side);
13136
13137       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13138                                           CE_PLAYER_LEAVES_X,
13139                                           player->index_bit, leave_side);
13140
13141       if (IS_CUSTOM_ELEMENT(new_element))
13142         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13143                                    player->index_bit, enter_side);
13144
13145       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13146                                           CE_PLAYER_ENTERS_X,
13147                                           player->index_bit, enter_side);
13148
13149       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13150                                         CE_MOVE_OF_X, move_direction);
13151     }
13152
13153     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13154     {
13155       TestIfPlayerTouchesBadThing(jx, jy);
13156       TestIfPlayerTouchesCustomElement(jx, jy);
13157
13158       /* needed because pushed element has not yet reached its destination,
13159          so it would trigger a change event at its previous field location */
13160       if (!player->is_pushing)
13161         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13162
13163       if (level.finish_dig_collect &&
13164           (player->is_digging || player->is_collecting))
13165       {
13166         int last_element = player->last_removed_element;
13167         int move_direction = player->MovDir;
13168         int enter_side = MV_DIR_OPPOSITE(move_direction);
13169         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13170                             CE_PLAYER_COLLECTS_X);
13171
13172         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13173                                             player->index_bit, enter_side);
13174
13175         player->last_removed_element = EL_UNDEFINED;
13176       }
13177
13178       if (!player->active)
13179         RemovePlayer(player);
13180     }
13181
13182     if (level.use_step_counter)
13183     {
13184       int i;
13185
13186       TimePlayed++;
13187
13188       if (TimeLeft > 0)
13189       {
13190         TimeLeft--;
13191
13192         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13193           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13194
13195         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13196
13197         DisplayGameControlValues();
13198
13199         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13200           for (i = 0; i < MAX_PLAYERS; i++)
13201             KillPlayer(&stored_player[i]);
13202       }
13203       else if (game.no_time_limit && !game.all_players_gone)
13204       {
13205         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13206
13207         DisplayGameControlValues();
13208       }
13209     }
13210
13211     if (tape.single_step && tape.recording && !tape.pausing &&
13212         !player->programmed_action)
13213       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13214
13215     if (!player->programmed_action)
13216       CheckSaveEngineSnapshot(player);
13217   }
13218 }
13219
13220 void ScrollScreen(struct PlayerInfo *player, int mode)
13221 {
13222   static unsigned int screen_frame_counter = 0;
13223
13224   if (mode == SCROLL_INIT)
13225   {
13226     // set scrolling step size according to actual player's moving speed
13227     ScrollStepSize = TILEX / player->move_delay_value;
13228
13229     screen_frame_counter = FrameCounter;
13230     ScreenMovDir = player->MovDir;
13231     ScreenMovPos = player->MovPos;
13232     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13233     return;
13234   }
13235   else if (!FrameReached(&screen_frame_counter, 1))
13236     return;
13237
13238   if (ScreenMovPos)
13239   {
13240     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13241     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13242     redraw_mask |= REDRAW_FIELD;
13243   }
13244   else
13245     ScreenMovDir = MV_NONE;
13246 }
13247
13248 void TestIfPlayerTouchesCustomElement(int x, int y)
13249 {
13250   static int xy[4][2] =
13251   {
13252     { 0, -1 },
13253     { -1, 0 },
13254     { +1, 0 },
13255     { 0, +1 }
13256   };
13257   static int trigger_sides[4][2] =
13258   {
13259     // center side       border side
13260     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13261     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13262     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13263     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13264   };
13265   static int touch_dir[4] =
13266   {
13267     MV_LEFT | MV_RIGHT,
13268     MV_UP   | MV_DOWN,
13269     MV_UP   | MV_DOWN,
13270     MV_LEFT | MV_RIGHT
13271   };
13272   int center_element = Tile[x][y];      // should always be non-moving!
13273   int i;
13274
13275   for (i = 0; i < NUM_DIRECTIONS; i++)
13276   {
13277     int xx = x + xy[i][0];
13278     int yy = y + xy[i][1];
13279     int center_side = trigger_sides[i][0];
13280     int border_side = trigger_sides[i][1];
13281     int border_element;
13282
13283     if (!IN_LEV_FIELD(xx, yy))
13284       continue;
13285
13286     if (IS_PLAYER(x, y))                // player found at center element
13287     {
13288       struct PlayerInfo *player = PLAYERINFO(x, y);
13289
13290       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13291         border_element = Tile[xx][yy];          // may be moving!
13292       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13293         border_element = Tile[xx][yy];
13294       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13295         border_element = MovingOrBlocked2Element(xx, yy);
13296       else
13297         continue;               // center and border element do not touch
13298
13299       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13300                                  player->index_bit, border_side);
13301       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13302                                           CE_PLAYER_TOUCHES_X,
13303                                           player->index_bit, border_side);
13304
13305       {
13306         /* use player element that is initially defined in the level playfield,
13307            not the player element that corresponds to the runtime player number
13308            (example: a level that contains EL_PLAYER_3 as the only player would
13309            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13310         int player_element = PLAYERINFO(x, y)->initial_element;
13311
13312         CheckElementChangeBySide(xx, yy, border_element, player_element,
13313                                  CE_TOUCHING_X, border_side);
13314       }
13315     }
13316     else if (IS_PLAYER(xx, yy))         // player found at border element
13317     {
13318       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13319
13320       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13321       {
13322         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13323           continue;             // center and border element do not touch
13324       }
13325
13326       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13327                                  player->index_bit, center_side);
13328       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13329                                           CE_PLAYER_TOUCHES_X,
13330                                           player->index_bit, center_side);
13331
13332       {
13333         /* use player element that is initially defined in the level playfield,
13334            not the player element that corresponds to the runtime player number
13335            (example: a level that contains EL_PLAYER_3 as the only player would
13336            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13337         int player_element = PLAYERINFO(xx, yy)->initial_element;
13338
13339         CheckElementChangeBySide(x, y, center_element, player_element,
13340                                  CE_TOUCHING_X, center_side);
13341       }
13342
13343       break;
13344     }
13345   }
13346 }
13347
13348 void TestIfElementTouchesCustomElement(int x, int y)
13349 {
13350   static int xy[4][2] =
13351   {
13352     { 0, -1 },
13353     { -1, 0 },
13354     { +1, 0 },
13355     { 0, +1 }
13356   };
13357   static int trigger_sides[4][2] =
13358   {
13359     // center side      border side
13360     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13361     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13362     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13363     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13364   };
13365   static int touch_dir[4] =
13366   {
13367     MV_LEFT | MV_RIGHT,
13368     MV_UP   | MV_DOWN,
13369     MV_UP   | MV_DOWN,
13370     MV_LEFT | MV_RIGHT
13371   };
13372   boolean change_center_element = FALSE;
13373   int center_element = Tile[x][y];      // should always be non-moving!
13374   int border_element_old[NUM_DIRECTIONS];
13375   int i;
13376
13377   for (i = 0; i < NUM_DIRECTIONS; i++)
13378   {
13379     int xx = x + xy[i][0];
13380     int yy = y + xy[i][1];
13381     int border_element;
13382
13383     border_element_old[i] = -1;
13384
13385     if (!IN_LEV_FIELD(xx, yy))
13386       continue;
13387
13388     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13389       border_element = Tile[xx][yy];    // may be moving!
13390     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13391       border_element = Tile[xx][yy];
13392     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13393       border_element = MovingOrBlocked2Element(xx, yy);
13394     else
13395       continue;                 // center and border element do not touch
13396
13397     border_element_old[i] = border_element;
13398   }
13399
13400   for (i = 0; i < NUM_DIRECTIONS; i++)
13401   {
13402     int xx = x + xy[i][0];
13403     int yy = y + xy[i][1];
13404     int center_side = trigger_sides[i][0];
13405     int border_element = border_element_old[i];
13406
13407     if (border_element == -1)
13408       continue;
13409
13410     // check for change of border element
13411     CheckElementChangeBySide(xx, yy, border_element, center_element,
13412                              CE_TOUCHING_X, center_side);
13413
13414     // (center element cannot be player, so we dont have to check this here)
13415   }
13416
13417   for (i = 0; i < NUM_DIRECTIONS; i++)
13418   {
13419     int xx = x + xy[i][0];
13420     int yy = y + xy[i][1];
13421     int border_side = trigger_sides[i][1];
13422     int border_element = border_element_old[i];
13423
13424     if (border_element == -1)
13425       continue;
13426
13427     // check for change of center element (but change it only once)
13428     if (!change_center_element)
13429       change_center_element =
13430         CheckElementChangeBySide(x, y, center_element, border_element,
13431                                  CE_TOUCHING_X, border_side);
13432
13433     if (IS_PLAYER(xx, yy))
13434     {
13435       /* use player element that is initially defined in the level playfield,
13436          not the player element that corresponds to the runtime player number
13437          (example: a level that contains EL_PLAYER_3 as the only player would
13438          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13439       int player_element = PLAYERINFO(xx, yy)->initial_element;
13440
13441       CheckElementChangeBySide(x, y, center_element, player_element,
13442                                CE_TOUCHING_X, border_side);
13443     }
13444   }
13445 }
13446
13447 void TestIfElementHitsCustomElement(int x, int y, int direction)
13448 {
13449   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13450   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13451   int hitx = x + dx, hity = y + dy;
13452   int hitting_element = Tile[x][y];
13453   int touched_element;
13454
13455   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13456     return;
13457
13458   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13459                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13460
13461   if (IN_LEV_FIELD(hitx, hity))
13462   {
13463     int opposite_direction = MV_DIR_OPPOSITE(direction);
13464     int hitting_side = direction;
13465     int touched_side = opposite_direction;
13466     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13467                           MovDir[hitx][hity] != direction ||
13468                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13469
13470     object_hit = TRUE;
13471
13472     if (object_hit)
13473     {
13474       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13475                                CE_HITTING_X, touched_side);
13476
13477       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13478                                CE_HIT_BY_X, hitting_side);
13479
13480       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13481                                CE_HIT_BY_SOMETHING, opposite_direction);
13482
13483       if (IS_PLAYER(hitx, hity))
13484       {
13485         /* use player element that is initially defined in the level playfield,
13486            not the player element that corresponds to the runtime player number
13487            (example: a level that contains EL_PLAYER_3 as the only player would
13488            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13489         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13490
13491         CheckElementChangeBySide(x, y, hitting_element, player_element,
13492                                  CE_HITTING_X, touched_side);
13493       }
13494     }
13495   }
13496
13497   // "hitting something" is also true when hitting the playfield border
13498   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13499                            CE_HITTING_SOMETHING, direction);
13500 }
13501
13502 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13503 {
13504   int i, kill_x = -1, kill_y = -1;
13505
13506   int bad_element = -1;
13507   static int test_xy[4][2] =
13508   {
13509     { 0, -1 },
13510     { -1, 0 },
13511     { +1, 0 },
13512     { 0, +1 }
13513   };
13514   static int test_dir[4] =
13515   {
13516     MV_UP,
13517     MV_LEFT,
13518     MV_RIGHT,
13519     MV_DOWN
13520   };
13521
13522   for (i = 0; i < NUM_DIRECTIONS; i++)
13523   {
13524     int test_x, test_y, test_move_dir, test_element;
13525
13526     test_x = good_x + test_xy[i][0];
13527     test_y = good_y + test_xy[i][1];
13528
13529     if (!IN_LEV_FIELD(test_x, test_y))
13530       continue;
13531
13532     test_move_dir =
13533       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13534
13535     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13536
13537     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13538        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13539     */
13540     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13541         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13542     {
13543       kill_x = test_x;
13544       kill_y = test_y;
13545       bad_element = test_element;
13546
13547       break;
13548     }
13549   }
13550
13551   if (kill_x != -1 || kill_y != -1)
13552   {
13553     if (IS_PLAYER(good_x, good_y))
13554     {
13555       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13556
13557       if (player->shield_deadly_time_left > 0 &&
13558           !IS_INDESTRUCTIBLE(bad_element))
13559         Bang(kill_x, kill_y);
13560       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13561         KillPlayer(player);
13562     }
13563     else
13564       Bang(good_x, good_y);
13565   }
13566 }
13567
13568 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13569 {
13570   int i, kill_x = -1, kill_y = -1;
13571   int bad_element = Tile[bad_x][bad_y];
13572   static int test_xy[4][2] =
13573   {
13574     { 0, -1 },
13575     { -1, 0 },
13576     { +1, 0 },
13577     { 0, +1 }
13578   };
13579   static int touch_dir[4] =
13580   {
13581     MV_LEFT | MV_RIGHT,
13582     MV_UP   | MV_DOWN,
13583     MV_UP   | MV_DOWN,
13584     MV_LEFT | MV_RIGHT
13585   };
13586   static int test_dir[4] =
13587   {
13588     MV_UP,
13589     MV_LEFT,
13590     MV_RIGHT,
13591     MV_DOWN
13592   };
13593
13594   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13595     return;
13596
13597   for (i = 0; i < NUM_DIRECTIONS; i++)
13598   {
13599     int test_x, test_y, test_move_dir, test_element;
13600
13601     test_x = bad_x + test_xy[i][0];
13602     test_y = bad_y + test_xy[i][1];
13603
13604     if (!IN_LEV_FIELD(test_x, test_y))
13605       continue;
13606
13607     test_move_dir =
13608       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13609
13610     test_element = Tile[test_x][test_y];
13611
13612     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13613        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13614     */
13615     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13616         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13617     {
13618       // good thing is player or penguin that does not move away
13619       if (IS_PLAYER(test_x, test_y))
13620       {
13621         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13622
13623         if (bad_element == EL_ROBOT && player->is_moving)
13624           continue;     // robot does not kill player if he is moving
13625
13626         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13627         {
13628           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13629             continue;           // center and border element do not touch
13630         }
13631
13632         kill_x = test_x;
13633         kill_y = test_y;
13634
13635         break;
13636       }
13637       else if (test_element == EL_PENGUIN)
13638       {
13639         kill_x = test_x;
13640         kill_y = test_y;
13641
13642         break;
13643       }
13644     }
13645   }
13646
13647   if (kill_x != -1 || kill_y != -1)
13648   {
13649     if (IS_PLAYER(kill_x, kill_y))
13650     {
13651       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13652
13653       if (player->shield_deadly_time_left > 0 &&
13654           !IS_INDESTRUCTIBLE(bad_element))
13655         Bang(bad_x, bad_y);
13656       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13657         KillPlayer(player);
13658     }
13659     else
13660       Bang(kill_x, kill_y);
13661   }
13662 }
13663
13664 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13665 {
13666   int bad_element = Tile[bad_x][bad_y];
13667   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13668   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13669   int test_x = bad_x + dx, test_y = bad_y + dy;
13670   int test_move_dir, test_element;
13671   int kill_x = -1, kill_y = -1;
13672
13673   if (!IN_LEV_FIELD(test_x, test_y))
13674     return;
13675
13676   test_move_dir =
13677     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13678
13679   test_element = Tile[test_x][test_y];
13680
13681   if (test_move_dir != bad_move_dir)
13682   {
13683     // good thing can be player or penguin that does not move away
13684     if (IS_PLAYER(test_x, test_y))
13685     {
13686       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13687
13688       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13689          player as being hit when he is moving towards the bad thing, because
13690          the "get hit by" condition would be lost after the player stops) */
13691       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13692         return;         // player moves away from bad thing
13693
13694       kill_x = test_x;
13695       kill_y = test_y;
13696     }
13697     else if (test_element == EL_PENGUIN)
13698     {
13699       kill_x = test_x;
13700       kill_y = test_y;
13701     }
13702   }
13703
13704   if (kill_x != -1 || kill_y != -1)
13705   {
13706     if (IS_PLAYER(kill_x, kill_y))
13707     {
13708       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13709
13710       if (player->shield_deadly_time_left > 0 &&
13711           !IS_INDESTRUCTIBLE(bad_element))
13712         Bang(bad_x, bad_y);
13713       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13714         KillPlayer(player);
13715     }
13716     else
13717       Bang(kill_x, kill_y);
13718   }
13719 }
13720
13721 void TestIfPlayerTouchesBadThing(int x, int y)
13722 {
13723   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13724 }
13725
13726 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13727 {
13728   TestIfGoodThingHitsBadThing(x, y, move_dir);
13729 }
13730
13731 void TestIfBadThingTouchesPlayer(int x, int y)
13732 {
13733   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13734 }
13735
13736 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13737 {
13738   TestIfBadThingHitsGoodThing(x, y, move_dir);
13739 }
13740
13741 void TestIfFriendTouchesBadThing(int x, int y)
13742 {
13743   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13744 }
13745
13746 void TestIfBadThingTouchesFriend(int x, int y)
13747 {
13748   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13749 }
13750
13751 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13752 {
13753   int i, kill_x = bad_x, kill_y = bad_y;
13754   static int xy[4][2] =
13755   {
13756     { 0, -1 },
13757     { -1, 0 },
13758     { +1, 0 },
13759     { 0, +1 }
13760   };
13761
13762   for (i = 0; i < NUM_DIRECTIONS; i++)
13763   {
13764     int x, y, element;
13765
13766     x = bad_x + xy[i][0];
13767     y = bad_y + xy[i][1];
13768     if (!IN_LEV_FIELD(x, y))
13769       continue;
13770
13771     element = Tile[x][y];
13772     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13773         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13774     {
13775       kill_x = x;
13776       kill_y = y;
13777       break;
13778     }
13779   }
13780
13781   if (kill_x != bad_x || kill_y != bad_y)
13782     Bang(bad_x, bad_y);
13783 }
13784
13785 void KillPlayer(struct PlayerInfo *player)
13786 {
13787   int jx = player->jx, jy = player->jy;
13788
13789   if (!player->active)
13790     return;
13791
13792 #if 0
13793   Debug("game:playing:KillPlayer",
13794         "0: killed == %d, active == %d, reanimated == %d",
13795         player->killed, player->active, player->reanimated);
13796 #endif
13797
13798   /* the following code was introduced to prevent an infinite loop when calling
13799      -> Bang()
13800      -> CheckTriggeredElementChangeExt()
13801      -> ExecuteCustomElementAction()
13802      -> KillPlayer()
13803      -> (infinitely repeating the above sequence of function calls)
13804      which occurs when killing the player while having a CE with the setting
13805      "kill player X when explosion of <player X>"; the solution using a new
13806      field "player->killed" was chosen for backwards compatibility, although
13807      clever use of the fields "player->active" etc. would probably also work */
13808 #if 1
13809   if (player->killed)
13810     return;
13811 #endif
13812
13813   player->killed = TRUE;
13814
13815   // remove accessible field at the player's position
13816   Tile[jx][jy] = EL_EMPTY;
13817
13818   // deactivate shield (else Bang()/Explode() would not work right)
13819   player->shield_normal_time_left = 0;
13820   player->shield_deadly_time_left = 0;
13821
13822 #if 0
13823   Debug("game:playing:KillPlayer",
13824         "1: killed == %d, active == %d, reanimated == %d",
13825         player->killed, player->active, player->reanimated);
13826 #endif
13827
13828   Bang(jx, jy);
13829
13830 #if 0
13831   Debug("game:playing:KillPlayer",
13832         "2: killed == %d, active == %d, reanimated == %d",
13833         player->killed, player->active, player->reanimated);
13834 #endif
13835
13836   if (player->reanimated)       // killed player may have been reanimated
13837     player->killed = player->reanimated = FALSE;
13838   else
13839     BuryPlayer(player);
13840 }
13841
13842 static void KillPlayerUnlessEnemyProtected(int x, int y)
13843 {
13844   if (!PLAYER_ENEMY_PROTECTED(x, y))
13845     KillPlayer(PLAYERINFO(x, y));
13846 }
13847
13848 static void KillPlayerUnlessExplosionProtected(int x, int y)
13849 {
13850   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13851     KillPlayer(PLAYERINFO(x, y));
13852 }
13853
13854 void BuryPlayer(struct PlayerInfo *player)
13855 {
13856   int jx = player->jx, jy = player->jy;
13857
13858   if (!player->active)
13859     return;
13860
13861   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13862   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13863
13864   RemovePlayer(player);
13865
13866   player->buried = TRUE;
13867
13868   if (game.all_players_gone)
13869     game.GameOver = TRUE;
13870 }
13871
13872 void RemovePlayer(struct PlayerInfo *player)
13873 {
13874   int jx = player->jx, jy = player->jy;
13875   int i, found = FALSE;
13876
13877   player->present = FALSE;
13878   player->active = FALSE;
13879
13880   // required for some CE actions (even if the player is not active anymore)
13881   player->MovPos = 0;
13882
13883   if (!ExplodeField[jx][jy])
13884     StorePlayer[jx][jy] = 0;
13885
13886   if (player->is_moving)
13887     TEST_DrawLevelField(player->last_jx, player->last_jy);
13888
13889   for (i = 0; i < MAX_PLAYERS; i++)
13890     if (stored_player[i].active)
13891       found = TRUE;
13892
13893   if (!found)
13894   {
13895     game.all_players_gone = TRUE;
13896     game.GameOver = TRUE;
13897   }
13898
13899   game.exit_x = game.robot_wheel_x = jx;
13900   game.exit_y = game.robot_wheel_y = jy;
13901 }
13902
13903 void ExitPlayer(struct PlayerInfo *player)
13904 {
13905   DrawPlayer(player);   // needed here only to cleanup last field
13906   RemovePlayer(player);
13907
13908   if (game.players_still_needed > 0)
13909     game.players_still_needed--;
13910 }
13911
13912 static void SetFieldForSnapping(int x, int y, int element, int direction,
13913                                 int player_index_bit)
13914 {
13915   struct ElementInfo *ei = &element_info[element];
13916   int direction_bit = MV_DIR_TO_BIT(direction);
13917   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13918   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13919                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13920
13921   Tile[x][y] = EL_ELEMENT_SNAPPING;
13922   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13923   MovDir[x][y] = direction;
13924   Store[x][y] = element;
13925   Store2[x][y] = player_index_bit;
13926
13927   ResetGfxAnimation(x, y);
13928
13929   GfxElement[x][y] = element;
13930   GfxAction[x][y] = action;
13931   GfxDir[x][y] = direction;
13932   GfxFrame[x][y] = -1;
13933 }
13934
13935 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13936                                    int player_index_bit)
13937 {
13938   TestIfElementTouchesCustomElement(x, y);      // for empty space
13939
13940   if (level.finish_dig_collect)
13941   {
13942     int dig_side = MV_DIR_OPPOSITE(direction);
13943
13944     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13945                                         player_index_bit, dig_side);
13946   }
13947 }
13948
13949 /*
13950   =============================================================================
13951   checkDiagonalPushing()
13952   -----------------------------------------------------------------------------
13953   check if diagonal input device direction results in pushing of object
13954   (by checking if the alternative direction is walkable, diggable, ...)
13955   =============================================================================
13956 */
13957
13958 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13959                                     int x, int y, int real_dx, int real_dy)
13960 {
13961   int jx, jy, dx, dy, xx, yy;
13962
13963   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13964     return TRUE;
13965
13966   // diagonal direction: check alternative direction
13967   jx = player->jx;
13968   jy = player->jy;
13969   dx = x - jx;
13970   dy = y - jy;
13971   xx = jx + (dx == 0 ? real_dx : 0);
13972   yy = jy + (dy == 0 ? real_dy : 0);
13973
13974   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13975 }
13976
13977 /*
13978   =============================================================================
13979   DigField()
13980   -----------------------------------------------------------------------------
13981   x, y:                 field next to player (non-diagonal) to try to dig to
13982   real_dx, real_dy:     direction as read from input device (can be diagonal)
13983   =============================================================================
13984 */
13985
13986 static int DigField(struct PlayerInfo *player,
13987                     int oldx, int oldy, int x, int y,
13988                     int real_dx, int real_dy, int mode)
13989 {
13990   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13991   boolean player_was_pushing = player->is_pushing;
13992   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13993   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13994   int jx = oldx, jy = oldy;
13995   int dx = x - jx, dy = y - jy;
13996   int nextx = x + dx, nexty = y + dy;
13997   int move_direction = (dx == -1 ? MV_LEFT  :
13998                         dx == +1 ? MV_RIGHT :
13999                         dy == -1 ? MV_UP    :
14000                         dy == +1 ? MV_DOWN  : MV_NONE);
14001   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14002   int dig_side = MV_DIR_OPPOSITE(move_direction);
14003   int old_element = Tile[jx][jy];
14004   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14005   int collect_count;
14006
14007   if (is_player)                // function can also be called by EL_PENGUIN
14008   {
14009     if (player->MovPos == 0)
14010     {
14011       player->is_digging = FALSE;
14012       player->is_collecting = FALSE;
14013     }
14014
14015     if (player->MovPos == 0)    // last pushing move finished
14016       player->is_pushing = FALSE;
14017
14018     if (mode == DF_NO_PUSH)     // player just stopped pushing
14019     {
14020       player->is_switching = FALSE;
14021       player->push_delay = -1;
14022
14023       return MP_NO_ACTION;
14024     }
14025   }
14026
14027   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14028     old_element = Back[jx][jy];
14029
14030   // in case of element dropped at player position, check background
14031   else if (Back[jx][jy] != EL_EMPTY &&
14032            game.engine_version >= VERSION_IDENT(2,2,0,0))
14033     old_element = Back[jx][jy];
14034
14035   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14036     return MP_NO_ACTION;        // field has no opening in this direction
14037
14038   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14039     return MP_NO_ACTION;        // field has no opening in this direction
14040
14041   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14042   {
14043     SplashAcid(x, y);
14044
14045     Tile[jx][jy] = player->artwork_element;
14046     InitMovingField(jx, jy, MV_DOWN);
14047     Store[jx][jy] = EL_ACID;
14048     ContinueMoving(jx, jy);
14049     BuryPlayer(player);
14050
14051     return MP_DONT_RUN_INTO;
14052   }
14053
14054   if (player_can_move && DONT_RUN_INTO(element))
14055   {
14056     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14057
14058     return MP_DONT_RUN_INTO;
14059   }
14060
14061   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14062     return MP_NO_ACTION;
14063
14064   collect_count = element_info[element].collect_count_initial;
14065
14066   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14067     return MP_NO_ACTION;
14068
14069   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14070     player_can_move = player_can_move_or_snap;
14071
14072   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14073       game.engine_version >= VERSION_IDENT(2,2,0,0))
14074   {
14075     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14076                                player->index_bit, dig_side);
14077     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14078                                         player->index_bit, dig_side);
14079
14080     if (element == EL_DC_LANDMINE)
14081       Bang(x, y);
14082
14083     if (Tile[x][y] != element)          // field changed by snapping
14084       return MP_ACTION;
14085
14086     return MP_NO_ACTION;
14087   }
14088
14089   if (player->gravity && is_player && !player->is_auto_moving &&
14090       canFallDown(player) && move_direction != MV_DOWN &&
14091       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14092     return MP_NO_ACTION;        // player cannot walk here due to gravity
14093
14094   if (player_can_move &&
14095       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14096   {
14097     int sound_element = SND_ELEMENT(element);
14098     int sound_action = ACTION_WALKING;
14099
14100     if (IS_RND_GATE(element))
14101     {
14102       if (!player->key[RND_GATE_NR(element)])
14103         return MP_NO_ACTION;
14104     }
14105     else if (IS_RND_GATE_GRAY(element))
14106     {
14107       if (!player->key[RND_GATE_GRAY_NR(element)])
14108         return MP_NO_ACTION;
14109     }
14110     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14111     {
14112       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14113         return MP_NO_ACTION;
14114     }
14115     else if (element == EL_EXIT_OPEN ||
14116              element == EL_EM_EXIT_OPEN ||
14117              element == EL_EM_EXIT_OPENING ||
14118              element == EL_STEEL_EXIT_OPEN ||
14119              element == EL_EM_STEEL_EXIT_OPEN ||
14120              element == EL_EM_STEEL_EXIT_OPENING ||
14121              element == EL_SP_EXIT_OPEN ||
14122              element == EL_SP_EXIT_OPENING)
14123     {
14124       sound_action = ACTION_PASSING;    // player is passing exit
14125     }
14126     else if (element == EL_EMPTY)
14127     {
14128       sound_action = ACTION_MOVING;             // nothing to walk on
14129     }
14130
14131     // play sound from background or player, whatever is available
14132     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14133       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14134     else
14135       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14136   }
14137   else if (player_can_move &&
14138            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14139   {
14140     if (!ACCESS_FROM(element, opposite_direction))
14141       return MP_NO_ACTION;      // field not accessible from this direction
14142
14143     if (CAN_MOVE(element))      // only fixed elements can be passed!
14144       return MP_NO_ACTION;
14145
14146     if (IS_EM_GATE(element))
14147     {
14148       if (!player->key[EM_GATE_NR(element)])
14149         return MP_NO_ACTION;
14150     }
14151     else if (IS_EM_GATE_GRAY(element))
14152     {
14153       if (!player->key[EM_GATE_GRAY_NR(element)])
14154         return MP_NO_ACTION;
14155     }
14156     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14157     {
14158       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14159         return MP_NO_ACTION;
14160     }
14161     else if (IS_EMC_GATE(element))
14162     {
14163       if (!player->key[EMC_GATE_NR(element)])
14164         return MP_NO_ACTION;
14165     }
14166     else if (IS_EMC_GATE_GRAY(element))
14167     {
14168       if (!player->key[EMC_GATE_GRAY_NR(element)])
14169         return MP_NO_ACTION;
14170     }
14171     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14172     {
14173       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14174         return MP_NO_ACTION;
14175     }
14176     else if (element == EL_DC_GATE_WHITE ||
14177              element == EL_DC_GATE_WHITE_GRAY ||
14178              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14179     {
14180       if (player->num_white_keys == 0)
14181         return MP_NO_ACTION;
14182
14183       player->num_white_keys--;
14184     }
14185     else if (IS_SP_PORT(element))
14186     {
14187       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14188           element == EL_SP_GRAVITY_PORT_RIGHT ||
14189           element == EL_SP_GRAVITY_PORT_UP ||
14190           element == EL_SP_GRAVITY_PORT_DOWN)
14191         player->gravity = !player->gravity;
14192       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14193                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14194                element == EL_SP_GRAVITY_ON_PORT_UP ||
14195                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14196         player->gravity = TRUE;
14197       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14198                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14199                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14200                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14201         player->gravity = FALSE;
14202     }
14203
14204     // automatically move to the next field with double speed
14205     player->programmed_action = move_direction;
14206
14207     if (player->move_delay_reset_counter == 0)
14208     {
14209       player->move_delay_reset_counter = 2;     // two double speed steps
14210
14211       DOUBLE_PLAYER_SPEED(player);
14212     }
14213
14214     PlayLevelSoundAction(x, y, ACTION_PASSING);
14215   }
14216   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14217   {
14218     RemoveField(x, y);
14219
14220     if (mode != DF_SNAP)
14221     {
14222       GfxElement[x][y] = GFX_ELEMENT(element);
14223       player->is_digging = TRUE;
14224     }
14225
14226     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14227
14228     // use old behaviour for old levels (digging)
14229     if (!level.finish_dig_collect)
14230     {
14231       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14232                                           player->index_bit, dig_side);
14233
14234       // if digging triggered player relocation, finish digging tile
14235       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14236         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14237     }
14238
14239     if (mode == DF_SNAP)
14240     {
14241       if (level.block_snap_field)
14242         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14243       else
14244         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14245
14246       // use old behaviour for old levels (snapping)
14247       if (!level.finish_dig_collect)
14248         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14249                                             player->index_bit, dig_side);
14250     }
14251   }
14252   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14253   {
14254     RemoveField(x, y);
14255
14256     if (is_player && mode != DF_SNAP)
14257     {
14258       GfxElement[x][y] = element;
14259       player->is_collecting = TRUE;
14260     }
14261
14262     if (element == EL_SPEED_PILL)
14263     {
14264       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14265     }
14266     else if (element == EL_EXTRA_TIME && level.time > 0)
14267     {
14268       TimeLeft += level.extra_time;
14269
14270       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14271
14272       DisplayGameControlValues();
14273     }
14274     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14275     {
14276       player->shield_normal_time_left += level.shield_normal_time;
14277       if (element == EL_SHIELD_DEADLY)
14278         player->shield_deadly_time_left += level.shield_deadly_time;
14279     }
14280     else if (element == EL_DYNAMITE ||
14281              element == EL_EM_DYNAMITE ||
14282              element == EL_SP_DISK_RED)
14283     {
14284       if (player->inventory_size < MAX_INVENTORY_SIZE)
14285         player->inventory_element[player->inventory_size++] = element;
14286
14287       DrawGameDoorValues();
14288     }
14289     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14290     {
14291       player->dynabomb_count++;
14292       player->dynabombs_left++;
14293     }
14294     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14295     {
14296       player->dynabomb_size++;
14297     }
14298     else if (element == EL_DYNABOMB_INCREASE_POWER)
14299     {
14300       player->dynabomb_xl = TRUE;
14301     }
14302     else if (IS_KEY(element))
14303     {
14304       player->key[KEY_NR(element)] = TRUE;
14305
14306       DrawGameDoorValues();
14307     }
14308     else if (element == EL_DC_KEY_WHITE)
14309     {
14310       player->num_white_keys++;
14311
14312       // display white keys?
14313       // DrawGameDoorValues();
14314     }
14315     else if (IS_ENVELOPE(element))
14316     {
14317       player->show_envelope = element;
14318     }
14319     else if (element == EL_EMC_LENSES)
14320     {
14321       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14322
14323       RedrawAllInvisibleElementsForLenses();
14324     }
14325     else if (element == EL_EMC_MAGNIFIER)
14326     {
14327       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14328
14329       RedrawAllInvisibleElementsForMagnifier();
14330     }
14331     else if (IS_DROPPABLE(element) ||
14332              IS_THROWABLE(element))     // can be collected and dropped
14333     {
14334       int i;
14335
14336       if (collect_count == 0)
14337         player->inventory_infinite_element = element;
14338       else
14339         for (i = 0; i < collect_count; i++)
14340           if (player->inventory_size < MAX_INVENTORY_SIZE)
14341             player->inventory_element[player->inventory_size++] = element;
14342
14343       DrawGameDoorValues();
14344     }
14345     else if (collect_count > 0)
14346     {
14347       game.gems_still_needed -= collect_count;
14348       if (game.gems_still_needed < 0)
14349         game.gems_still_needed = 0;
14350
14351       game.snapshot.collected_item = TRUE;
14352
14353       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14354
14355       DisplayGameControlValues();
14356     }
14357
14358     RaiseScoreElement(element);
14359     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14360
14361     // use old behaviour for old levels (collecting)
14362     if (!level.finish_dig_collect && is_player)
14363     {
14364       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14365                                           player->index_bit, dig_side);
14366
14367       // if collecting triggered player relocation, finish collecting tile
14368       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14369         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14370     }
14371
14372     if (mode == DF_SNAP)
14373     {
14374       if (level.block_snap_field)
14375         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14376       else
14377         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14378
14379       // use old behaviour for old levels (snapping)
14380       if (!level.finish_dig_collect)
14381         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14382                                             player->index_bit, dig_side);
14383     }
14384   }
14385   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14386   {
14387     if (mode == DF_SNAP && element != EL_BD_ROCK)
14388       return MP_NO_ACTION;
14389
14390     if (CAN_FALL(element) && dy)
14391       return MP_NO_ACTION;
14392
14393     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14394         !(element == EL_SPRING && level.use_spring_bug))
14395       return MP_NO_ACTION;
14396
14397     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14398         ((move_direction & MV_VERTICAL &&
14399           ((element_info[element].move_pattern & MV_LEFT &&
14400             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14401            (element_info[element].move_pattern & MV_RIGHT &&
14402             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14403          (move_direction & MV_HORIZONTAL &&
14404           ((element_info[element].move_pattern & MV_UP &&
14405             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14406            (element_info[element].move_pattern & MV_DOWN &&
14407             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14408       return MP_NO_ACTION;
14409
14410     // do not push elements already moving away faster than player
14411     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14412         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14413       return MP_NO_ACTION;
14414
14415     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14416     {
14417       if (player->push_delay_value == -1 || !player_was_pushing)
14418         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14419     }
14420     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14421     {
14422       if (player->push_delay_value == -1)
14423         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14424     }
14425     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14426     {
14427       if (!player->is_pushing)
14428         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14429     }
14430
14431     player->is_pushing = TRUE;
14432     player->is_active = TRUE;
14433
14434     if (!(IN_LEV_FIELD(nextx, nexty) &&
14435           (IS_FREE(nextx, nexty) ||
14436            (IS_SB_ELEMENT(element) &&
14437             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14438            (IS_CUSTOM_ELEMENT(element) &&
14439             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14440       return MP_NO_ACTION;
14441
14442     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14443       return MP_NO_ACTION;
14444
14445     if (player->push_delay == -1)       // new pushing; restart delay
14446       player->push_delay = 0;
14447
14448     if (player->push_delay < player->push_delay_value &&
14449         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14450         element != EL_SPRING && element != EL_BALLOON)
14451     {
14452       // make sure that there is no move delay before next try to push
14453       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14454         player->move_delay = 0;
14455
14456       return MP_NO_ACTION;
14457     }
14458
14459     if (IS_CUSTOM_ELEMENT(element) &&
14460         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14461     {
14462       if (!DigFieldByCE(nextx, nexty, element))
14463         return MP_NO_ACTION;
14464     }
14465
14466     if (IS_SB_ELEMENT(element))
14467     {
14468       boolean sokoban_task_solved = FALSE;
14469
14470       if (element == EL_SOKOBAN_FIELD_FULL)
14471       {
14472         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14473
14474         IncrementSokobanFieldsNeeded();
14475         IncrementSokobanObjectsNeeded();
14476       }
14477
14478       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14479       {
14480         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14481
14482         DecrementSokobanFieldsNeeded();
14483         DecrementSokobanObjectsNeeded();
14484
14485         // sokoban object was pushed from empty field to sokoban field
14486         if (Back[x][y] == EL_EMPTY)
14487           sokoban_task_solved = TRUE;
14488       }
14489
14490       Tile[x][y] = EL_SOKOBAN_OBJECT;
14491
14492       if (Back[x][y] == Back[nextx][nexty])
14493         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14494       else if (Back[x][y] != 0)
14495         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14496                                     ACTION_EMPTYING);
14497       else
14498         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14499                                     ACTION_FILLING);
14500
14501       if (sokoban_task_solved &&
14502           game.sokoban_fields_still_needed == 0 &&
14503           game.sokoban_objects_still_needed == 0 &&
14504           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14505       {
14506         game.players_still_needed = 0;
14507
14508         LevelSolved();
14509
14510         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14511       }
14512     }
14513     else
14514       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14515
14516     InitMovingField(x, y, move_direction);
14517     GfxAction[x][y] = ACTION_PUSHING;
14518
14519     if (mode == DF_SNAP)
14520       ContinueMoving(x, y);
14521     else
14522       MovPos[x][y] = (dx != 0 ? dx : dy);
14523
14524     Pushed[x][y] = TRUE;
14525     Pushed[nextx][nexty] = TRUE;
14526
14527     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14528       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14529     else
14530       player->push_delay_value = -1;    // get new value later
14531
14532     // check for element change _after_ element has been pushed
14533     if (game.use_change_when_pushing_bug)
14534     {
14535       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14536                                  player->index_bit, dig_side);
14537       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14538                                           player->index_bit, dig_side);
14539     }
14540   }
14541   else if (IS_SWITCHABLE(element))
14542   {
14543     if (PLAYER_SWITCHING(player, x, y))
14544     {
14545       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14546                                           player->index_bit, dig_side);
14547
14548       return MP_ACTION;
14549     }
14550
14551     player->is_switching = TRUE;
14552     player->switch_x = x;
14553     player->switch_y = y;
14554
14555     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14556
14557     if (element == EL_ROBOT_WHEEL)
14558     {
14559       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14560
14561       game.robot_wheel_x = x;
14562       game.robot_wheel_y = y;
14563       game.robot_wheel_active = TRUE;
14564
14565       TEST_DrawLevelField(x, y);
14566     }
14567     else if (element == EL_SP_TERMINAL)
14568     {
14569       int xx, yy;
14570
14571       SCAN_PLAYFIELD(xx, yy)
14572       {
14573         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14574         {
14575           Bang(xx, yy);
14576         }
14577         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14578         {
14579           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14580
14581           ResetGfxAnimation(xx, yy);
14582           TEST_DrawLevelField(xx, yy);
14583         }
14584       }
14585     }
14586     else if (IS_BELT_SWITCH(element))
14587     {
14588       ToggleBeltSwitch(x, y);
14589     }
14590     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14591              element == EL_SWITCHGATE_SWITCH_DOWN ||
14592              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14593              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14594     {
14595       ToggleSwitchgateSwitch(x, y);
14596     }
14597     else if (element == EL_LIGHT_SWITCH ||
14598              element == EL_LIGHT_SWITCH_ACTIVE)
14599     {
14600       ToggleLightSwitch(x, y);
14601     }
14602     else if (element == EL_TIMEGATE_SWITCH ||
14603              element == EL_DC_TIMEGATE_SWITCH)
14604     {
14605       ActivateTimegateSwitch(x, y);
14606     }
14607     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14608              element == EL_BALLOON_SWITCH_RIGHT ||
14609              element == EL_BALLOON_SWITCH_UP    ||
14610              element == EL_BALLOON_SWITCH_DOWN  ||
14611              element == EL_BALLOON_SWITCH_NONE  ||
14612              element == EL_BALLOON_SWITCH_ANY)
14613     {
14614       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14615                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14616                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14617                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14618                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14619                              move_direction);
14620     }
14621     else if (element == EL_LAMP)
14622     {
14623       Tile[x][y] = EL_LAMP_ACTIVE;
14624       game.lights_still_needed--;
14625
14626       ResetGfxAnimation(x, y);
14627       TEST_DrawLevelField(x, y);
14628     }
14629     else if (element == EL_TIME_ORB_FULL)
14630     {
14631       Tile[x][y] = EL_TIME_ORB_EMPTY;
14632
14633       if (level.time > 0 || level.use_time_orb_bug)
14634       {
14635         TimeLeft += level.time_orb_time;
14636         game.no_time_limit = FALSE;
14637
14638         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14639
14640         DisplayGameControlValues();
14641       }
14642
14643       ResetGfxAnimation(x, y);
14644       TEST_DrawLevelField(x, y);
14645     }
14646     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14647              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14648     {
14649       int xx, yy;
14650
14651       game.ball_active = !game.ball_active;
14652
14653       SCAN_PLAYFIELD(xx, yy)
14654       {
14655         int e = Tile[xx][yy];
14656
14657         if (game.ball_active)
14658         {
14659           if (e == EL_EMC_MAGIC_BALL)
14660             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14661           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14662             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14663         }
14664         else
14665         {
14666           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14667             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14668           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14669             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14670         }
14671       }
14672     }
14673
14674     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14675                                         player->index_bit, dig_side);
14676
14677     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14678                                         player->index_bit, dig_side);
14679
14680     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14681                                         player->index_bit, dig_side);
14682
14683     return MP_ACTION;
14684   }
14685   else
14686   {
14687     if (!PLAYER_SWITCHING(player, x, y))
14688     {
14689       player->is_switching = TRUE;
14690       player->switch_x = x;
14691       player->switch_y = y;
14692
14693       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14694                                  player->index_bit, dig_side);
14695       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14696                                           player->index_bit, dig_side);
14697
14698       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14699                                  player->index_bit, dig_side);
14700       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14701                                           player->index_bit, dig_side);
14702     }
14703
14704     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14705                                player->index_bit, dig_side);
14706     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14707                                         player->index_bit, dig_side);
14708
14709     return MP_NO_ACTION;
14710   }
14711
14712   player->push_delay = -1;
14713
14714   if (is_player)                // function can also be called by EL_PENGUIN
14715   {
14716     if (Tile[x][y] != element)          // really digged/collected something
14717     {
14718       player->is_collecting = !player->is_digging;
14719       player->is_active = TRUE;
14720
14721       player->last_removed_element = element;
14722     }
14723   }
14724
14725   return MP_MOVING;
14726 }
14727
14728 static boolean DigFieldByCE(int x, int y, int digging_element)
14729 {
14730   int element = Tile[x][y];
14731
14732   if (!IS_FREE(x, y))
14733   {
14734     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14735                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14736                   ACTION_BREAKING);
14737
14738     // no element can dig solid indestructible elements
14739     if (IS_INDESTRUCTIBLE(element) &&
14740         !IS_DIGGABLE(element) &&
14741         !IS_COLLECTIBLE(element))
14742       return FALSE;
14743
14744     if (AmoebaNr[x][y] &&
14745         (element == EL_AMOEBA_FULL ||
14746          element == EL_BD_AMOEBA ||
14747          element == EL_AMOEBA_GROWING))
14748     {
14749       AmoebaCnt[AmoebaNr[x][y]]--;
14750       AmoebaCnt2[AmoebaNr[x][y]]--;
14751     }
14752
14753     if (IS_MOVING(x, y))
14754       RemoveMovingField(x, y);
14755     else
14756     {
14757       RemoveField(x, y);
14758       TEST_DrawLevelField(x, y);
14759     }
14760
14761     // if digged element was about to explode, prevent the explosion
14762     ExplodeField[x][y] = EX_TYPE_NONE;
14763
14764     PlayLevelSoundAction(x, y, action);
14765   }
14766
14767   Store[x][y] = EL_EMPTY;
14768
14769   // this makes it possible to leave the removed element again
14770   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14771     Store[x][y] = element;
14772
14773   return TRUE;
14774 }
14775
14776 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14777 {
14778   int jx = player->jx, jy = player->jy;
14779   int x = jx + dx, y = jy + dy;
14780   int snap_direction = (dx == -1 ? MV_LEFT  :
14781                         dx == +1 ? MV_RIGHT :
14782                         dy == -1 ? MV_UP    :
14783                         dy == +1 ? MV_DOWN  : MV_NONE);
14784   boolean can_continue_snapping = (level.continuous_snapping &&
14785                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14786
14787   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14788     return FALSE;
14789
14790   if (!player->active || !IN_LEV_FIELD(x, y))
14791     return FALSE;
14792
14793   if (dx && dy)
14794     return FALSE;
14795
14796   if (!dx && !dy)
14797   {
14798     if (player->MovPos == 0)
14799       player->is_pushing = FALSE;
14800
14801     player->is_snapping = FALSE;
14802
14803     if (player->MovPos == 0)
14804     {
14805       player->is_moving = FALSE;
14806       player->is_digging = FALSE;
14807       player->is_collecting = FALSE;
14808     }
14809
14810     return FALSE;
14811   }
14812
14813   // prevent snapping with already pressed snap key when not allowed
14814   if (player->is_snapping && !can_continue_snapping)
14815     return FALSE;
14816
14817   player->MovDir = snap_direction;
14818
14819   if (player->MovPos == 0)
14820   {
14821     player->is_moving = FALSE;
14822     player->is_digging = FALSE;
14823     player->is_collecting = FALSE;
14824   }
14825
14826   player->is_dropping = FALSE;
14827   player->is_dropping_pressed = FALSE;
14828   player->drop_pressed_delay = 0;
14829
14830   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14831     return FALSE;
14832
14833   player->is_snapping = TRUE;
14834   player->is_active = TRUE;
14835
14836   if (player->MovPos == 0)
14837   {
14838     player->is_moving = FALSE;
14839     player->is_digging = FALSE;
14840     player->is_collecting = FALSE;
14841   }
14842
14843   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14844     TEST_DrawLevelField(player->last_jx, player->last_jy);
14845
14846   TEST_DrawLevelField(x, y);
14847
14848   return TRUE;
14849 }
14850
14851 static boolean DropElement(struct PlayerInfo *player)
14852 {
14853   int old_element, new_element;
14854   int dropx = player->jx, dropy = player->jy;
14855   int drop_direction = player->MovDir;
14856   int drop_side = drop_direction;
14857   int drop_element = get_next_dropped_element(player);
14858
14859   /* do not drop an element on top of another element; when holding drop key
14860      pressed without moving, dropped element must move away before the next
14861      element can be dropped (this is especially important if the next element
14862      is dynamite, which can be placed on background for historical reasons) */
14863   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14864     return MP_ACTION;
14865
14866   if (IS_THROWABLE(drop_element))
14867   {
14868     dropx += GET_DX_FROM_DIR(drop_direction);
14869     dropy += GET_DY_FROM_DIR(drop_direction);
14870
14871     if (!IN_LEV_FIELD(dropx, dropy))
14872       return FALSE;
14873   }
14874
14875   old_element = Tile[dropx][dropy];     // old element at dropping position
14876   new_element = drop_element;           // default: no change when dropping
14877
14878   // check if player is active, not moving and ready to drop
14879   if (!player->active || player->MovPos || player->drop_delay > 0)
14880     return FALSE;
14881
14882   // check if player has anything that can be dropped
14883   if (new_element == EL_UNDEFINED)
14884     return FALSE;
14885
14886   // only set if player has anything that can be dropped
14887   player->is_dropping_pressed = TRUE;
14888
14889   // check if drop key was pressed long enough for EM style dynamite
14890   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14891     return FALSE;
14892
14893   // check if anything can be dropped at the current position
14894   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14895     return FALSE;
14896
14897   // collected custom elements can only be dropped on empty fields
14898   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14899     return FALSE;
14900
14901   if (old_element != EL_EMPTY)
14902     Back[dropx][dropy] = old_element;   // store old element on this field
14903
14904   ResetGfxAnimation(dropx, dropy);
14905   ResetRandomAnimationValue(dropx, dropy);
14906
14907   if (player->inventory_size > 0 ||
14908       player->inventory_infinite_element != EL_UNDEFINED)
14909   {
14910     if (player->inventory_size > 0)
14911     {
14912       player->inventory_size--;
14913
14914       DrawGameDoorValues();
14915
14916       if (new_element == EL_DYNAMITE)
14917         new_element = EL_DYNAMITE_ACTIVE;
14918       else if (new_element == EL_EM_DYNAMITE)
14919         new_element = EL_EM_DYNAMITE_ACTIVE;
14920       else if (new_element == EL_SP_DISK_RED)
14921         new_element = EL_SP_DISK_RED_ACTIVE;
14922     }
14923
14924     Tile[dropx][dropy] = new_element;
14925
14926     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14927       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14928                           el2img(Tile[dropx][dropy]), 0);
14929
14930     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14931
14932     // needed if previous element just changed to "empty" in the last frame
14933     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14934
14935     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14936                                player->index_bit, drop_side);
14937     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14938                                         CE_PLAYER_DROPS_X,
14939                                         player->index_bit, drop_side);
14940
14941     TestIfElementTouchesCustomElement(dropx, dropy);
14942   }
14943   else          // player is dropping a dyna bomb
14944   {
14945     player->dynabombs_left--;
14946
14947     Tile[dropx][dropy] = new_element;
14948
14949     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14950       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14951                           el2img(Tile[dropx][dropy]), 0);
14952
14953     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14954   }
14955
14956   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14957     InitField_WithBug1(dropx, dropy, FALSE);
14958
14959   new_element = Tile[dropx][dropy];     // element might have changed
14960
14961   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14962       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14963   {
14964     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14965       MovDir[dropx][dropy] = drop_direction;
14966
14967     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14968
14969     // do not cause impact style collision by dropping elements that can fall
14970     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14971   }
14972
14973   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14974   player->is_dropping = TRUE;
14975
14976   player->drop_pressed_delay = 0;
14977   player->is_dropping_pressed = FALSE;
14978
14979   player->drop_x = dropx;
14980   player->drop_y = dropy;
14981
14982   return TRUE;
14983 }
14984
14985 // ----------------------------------------------------------------------------
14986 // game sound playing functions
14987 // ----------------------------------------------------------------------------
14988
14989 static int *loop_sound_frame = NULL;
14990 static int *loop_sound_volume = NULL;
14991
14992 void InitPlayLevelSound(void)
14993 {
14994   int num_sounds = getSoundListSize();
14995
14996   checked_free(loop_sound_frame);
14997   checked_free(loop_sound_volume);
14998
14999   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15000   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15001 }
15002
15003 static void PlayLevelSound(int x, int y, int nr)
15004 {
15005   int sx = SCREENX(x), sy = SCREENY(y);
15006   int volume, stereo_position;
15007   int max_distance = 8;
15008   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15009
15010   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15011       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15012     return;
15013
15014   if (!IN_LEV_FIELD(x, y) ||
15015       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15016       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15017     return;
15018
15019   volume = SOUND_MAX_VOLUME;
15020
15021   if (!IN_SCR_FIELD(sx, sy))
15022   {
15023     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15024     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15025
15026     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15027   }
15028
15029   stereo_position = (SOUND_MAX_LEFT +
15030                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15031                      (SCR_FIELDX + 2 * max_distance));
15032
15033   if (IS_LOOP_SOUND(nr))
15034   {
15035     /* This assures that quieter loop sounds do not overwrite louder ones,
15036        while restarting sound volume comparison with each new game frame. */
15037
15038     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15039       return;
15040
15041     loop_sound_volume[nr] = volume;
15042     loop_sound_frame[nr] = FrameCounter;
15043   }
15044
15045   PlaySoundExt(nr, volume, stereo_position, type);
15046 }
15047
15048 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15049 {
15050   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15051                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15052                  y < LEVELY(BY1) ? LEVELY(BY1) :
15053                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15054                  sound_action);
15055 }
15056
15057 static void PlayLevelSoundAction(int x, int y, int action)
15058 {
15059   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15060 }
15061
15062 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15063 {
15064   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15065
15066   if (sound_effect != SND_UNDEFINED)
15067     PlayLevelSound(x, y, sound_effect);
15068 }
15069
15070 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15071                                               int action)
15072 {
15073   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15074
15075   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15076     PlayLevelSound(x, y, sound_effect);
15077 }
15078
15079 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15080 {
15081   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15082
15083   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15084     PlayLevelSound(x, y, sound_effect);
15085 }
15086
15087 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15088 {
15089   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15090
15091   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15092     StopSound(sound_effect);
15093 }
15094
15095 static int getLevelMusicNr(void)
15096 {
15097   if (levelset.music[level_nr] != MUS_UNDEFINED)
15098     return levelset.music[level_nr];            // from config file
15099   else
15100     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15101 }
15102
15103 static void FadeLevelSounds(void)
15104 {
15105   FadeSounds();
15106 }
15107
15108 static void FadeLevelMusic(void)
15109 {
15110   int music_nr = getLevelMusicNr();
15111   char *curr_music = getCurrentlyPlayingMusicFilename();
15112   char *next_music = getMusicInfoEntryFilename(music_nr);
15113
15114   if (!strEqual(curr_music, next_music))
15115     FadeMusic();
15116 }
15117
15118 void FadeLevelSoundsAndMusic(void)
15119 {
15120   FadeLevelSounds();
15121   FadeLevelMusic();
15122 }
15123
15124 static void PlayLevelMusic(void)
15125 {
15126   int music_nr = getLevelMusicNr();
15127   char *curr_music = getCurrentlyPlayingMusicFilename();
15128   char *next_music = getMusicInfoEntryFilename(music_nr);
15129
15130   if (!strEqual(curr_music, next_music))
15131     PlayMusicLoop(music_nr);
15132 }
15133
15134 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15135 {
15136   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15137   int offset = 0;
15138   int x = xx - offset;
15139   int y = yy - offset;
15140
15141   switch (sample)
15142   {
15143     case SOUND_blank:
15144       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15145       break;
15146
15147     case SOUND_roll:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15149       break;
15150
15151     case SOUND_stone:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15153       break;
15154
15155     case SOUND_nut:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15157       break;
15158
15159     case SOUND_crack:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15161       break;
15162
15163     case SOUND_bug:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15165       break;
15166
15167     case SOUND_tank:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15169       break;
15170
15171     case SOUND_android_clone:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15173       break;
15174
15175     case SOUND_android_move:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15177       break;
15178
15179     case SOUND_spring:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15181       break;
15182
15183     case SOUND_slurp:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15185       break;
15186
15187     case SOUND_eater:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15189       break;
15190
15191     case SOUND_eater_eat:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15193       break;
15194
15195     case SOUND_alien:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15197       break;
15198
15199     case SOUND_collect:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15201       break;
15202
15203     case SOUND_diamond:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15205       break;
15206
15207     case SOUND_squash:
15208       // !!! CHECK THIS !!!
15209 #if 1
15210       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15211 #else
15212       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15213 #endif
15214       break;
15215
15216     case SOUND_wonderfall:
15217       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15218       break;
15219
15220     case SOUND_drip:
15221       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15222       break;
15223
15224     case SOUND_push:
15225       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15226       break;
15227
15228     case SOUND_dirt:
15229       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15230       break;
15231
15232     case SOUND_acid:
15233       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15234       break;
15235
15236     case SOUND_ball:
15237       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15238       break;
15239
15240     case SOUND_slide:
15241       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15242       break;
15243
15244     case SOUND_wonder:
15245       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15246       break;
15247
15248     case SOUND_door:
15249       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15250       break;
15251
15252     case SOUND_exit_open:
15253       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15254       break;
15255
15256     case SOUND_exit_leave:
15257       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15258       break;
15259
15260     case SOUND_dynamite:
15261       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15262       break;
15263
15264     case SOUND_tick:
15265       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15266       break;
15267
15268     case SOUND_press:
15269       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15270       break;
15271
15272     case SOUND_wheel:
15273       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15274       break;
15275
15276     case SOUND_boom:
15277       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15278       break;
15279
15280     case SOUND_die:
15281       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15282       break;
15283
15284     case SOUND_time:
15285       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15286       break;
15287
15288     default:
15289       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15290       break;
15291   }
15292 }
15293
15294 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15295 {
15296   int element = map_element_SP_to_RND(element_sp);
15297   int action = map_action_SP_to_RND(action_sp);
15298   int offset = (setup.sp_show_border_elements ? 0 : 1);
15299   int x = xx - offset;
15300   int y = yy - offset;
15301
15302   PlayLevelSoundElementAction(x, y, element, action);
15303 }
15304
15305 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15306 {
15307   int element = map_element_MM_to_RND(element_mm);
15308   int action = map_action_MM_to_RND(action_mm);
15309   int offset = 0;
15310   int x = xx - offset;
15311   int y = yy - offset;
15312
15313   if (!IS_MM_ELEMENT(element))
15314     element = EL_MM_DEFAULT;
15315
15316   PlayLevelSoundElementAction(x, y, element, action);
15317 }
15318
15319 void PlaySound_MM(int sound_mm)
15320 {
15321   int sound = map_sound_MM_to_RND(sound_mm);
15322
15323   if (sound == SND_UNDEFINED)
15324     return;
15325
15326   PlaySound(sound);
15327 }
15328
15329 void PlaySoundLoop_MM(int sound_mm)
15330 {
15331   int sound = map_sound_MM_to_RND(sound_mm);
15332
15333   if (sound == SND_UNDEFINED)
15334     return;
15335
15336   PlaySoundLoop(sound);
15337 }
15338
15339 void StopSound_MM(int sound_mm)
15340 {
15341   int sound = map_sound_MM_to_RND(sound_mm);
15342
15343   if (sound == SND_UNDEFINED)
15344     return;
15345
15346   StopSound(sound);
15347 }
15348
15349 void RaiseScore(int value)
15350 {
15351   game.score += value;
15352
15353   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15354
15355   DisplayGameControlValues();
15356 }
15357
15358 void RaiseScoreElement(int element)
15359 {
15360   switch (element)
15361   {
15362     case EL_EMERALD:
15363     case EL_BD_DIAMOND:
15364     case EL_EMERALD_YELLOW:
15365     case EL_EMERALD_RED:
15366     case EL_EMERALD_PURPLE:
15367     case EL_SP_INFOTRON:
15368       RaiseScore(level.score[SC_EMERALD]);
15369       break;
15370     case EL_DIAMOND:
15371       RaiseScore(level.score[SC_DIAMOND]);
15372       break;
15373     case EL_CRYSTAL:
15374       RaiseScore(level.score[SC_CRYSTAL]);
15375       break;
15376     case EL_PEARL:
15377       RaiseScore(level.score[SC_PEARL]);
15378       break;
15379     case EL_BUG:
15380     case EL_BD_BUTTERFLY:
15381     case EL_SP_ELECTRON:
15382       RaiseScore(level.score[SC_BUG]);
15383       break;
15384     case EL_SPACESHIP:
15385     case EL_BD_FIREFLY:
15386     case EL_SP_SNIKSNAK:
15387       RaiseScore(level.score[SC_SPACESHIP]);
15388       break;
15389     case EL_YAMYAM:
15390     case EL_DARK_YAMYAM:
15391       RaiseScore(level.score[SC_YAMYAM]);
15392       break;
15393     case EL_ROBOT:
15394       RaiseScore(level.score[SC_ROBOT]);
15395       break;
15396     case EL_PACMAN:
15397       RaiseScore(level.score[SC_PACMAN]);
15398       break;
15399     case EL_NUT:
15400       RaiseScore(level.score[SC_NUT]);
15401       break;
15402     case EL_DYNAMITE:
15403     case EL_EM_DYNAMITE:
15404     case EL_SP_DISK_RED:
15405     case EL_DYNABOMB_INCREASE_NUMBER:
15406     case EL_DYNABOMB_INCREASE_SIZE:
15407     case EL_DYNABOMB_INCREASE_POWER:
15408       RaiseScore(level.score[SC_DYNAMITE]);
15409       break;
15410     case EL_SHIELD_NORMAL:
15411     case EL_SHIELD_DEADLY:
15412       RaiseScore(level.score[SC_SHIELD]);
15413       break;
15414     case EL_EXTRA_TIME:
15415       RaiseScore(level.extra_time_score);
15416       break;
15417     case EL_KEY_1:
15418     case EL_KEY_2:
15419     case EL_KEY_3:
15420     case EL_KEY_4:
15421     case EL_EM_KEY_1:
15422     case EL_EM_KEY_2:
15423     case EL_EM_KEY_3:
15424     case EL_EM_KEY_4:
15425     case EL_EMC_KEY_5:
15426     case EL_EMC_KEY_6:
15427     case EL_EMC_KEY_7:
15428     case EL_EMC_KEY_8:
15429     case EL_DC_KEY_WHITE:
15430       RaiseScore(level.score[SC_KEY]);
15431       break;
15432     default:
15433       RaiseScore(element_info[element].collect_score);
15434       break;
15435   }
15436 }
15437
15438 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15439 {
15440   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15441   {
15442     if (!quick_quit)
15443     {
15444       // prevent short reactivation of overlay buttons while closing door
15445       SetOverlayActive(FALSE);
15446
15447       // door may still be open due to skipped or envelope style request
15448       CloseDoor(DOOR_CLOSE_1);
15449     }
15450
15451     if (network.enabled)
15452       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15453     else
15454     {
15455       if (quick_quit)
15456         FadeSkipNextFadeIn();
15457
15458       SetGameStatus(GAME_MODE_MAIN);
15459
15460       DrawMainMenu();
15461     }
15462   }
15463   else          // continue playing the game
15464   {
15465     if (tape.playing && tape.deactivate_display)
15466       TapeDeactivateDisplayOff(TRUE);
15467
15468     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15469
15470     if (tape.playing && tape.deactivate_display)
15471       TapeDeactivateDisplayOn();
15472   }
15473 }
15474
15475 void RequestQuitGame(boolean escape_key_pressed)
15476 {
15477   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15478   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15479                         level_editor_test_game);
15480   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15481                           quick_quit);
15482
15483   RequestQuitGameExt(skip_request, quick_quit,
15484                      "Do you really want to quit the game?");
15485 }
15486
15487 void RequestRestartGame(char *message)
15488 {
15489   game.restart_game_message = NULL;
15490
15491   boolean has_started_game = hasStartedNetworkGame();
15492   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15493
15494   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15495   {
15496     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15497   }
15498   else
15499   {
15500     // needed in case of envelope request to close game panel
15501     CloseDoor(DOOR_CLOSE_1);
15502
15503     SetGameStatus(GAME_MODE_MAIN);
15504
15505     DrawMainMenu();
15506   }
15507 }
15508
15509 void CheckGameOver(void)
15510 {
15511   static boolean last_game_over = FALSE;
15512   static int game_over_delay = 0;
15513   int game_over_delay_value = 50;
15514   boolean game_over = checkGameFailed();
15515
15516   // do not handle game over if request dialog is already active
15517   if (game.request_active)
15518     return;
15519
15520   // do not ask to play again if game was never actually played
15521   if (!game.GamePlayed)
15522     return;
15523
15524   if (!game_over)
15525   {
15526     last_game_over = FALSE;
15527     game_over_delay = game_over_delay_value;
15528
15529     return;
15530   }
15531
15532   if (game_over_delay > 0)
15533   {
15534     game_over_delay--;
15535
15536     return;
15537   }
15538
15539   if (last_game_over != game_over)
15540     game.restart_game_message = (hasStartedNetworkGame() ?
15541                                  "Game over! Play it again?" :
15542                                  "Game over!");
15543
15544   last_game_over = game_over;
15545 }
15546
15547 boolean checkGameSolved(void)
15548 {
15549   // set for all game engines if level was solved
15550   return game.LevelSolved_GameEnd;
15551 }
15552
15553 boolean checkGameFailed(void)
15554 {
15555   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15556     return (game_em.game_over && !game_em.level_solved);
15557   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15558     return (game_sp.game_over && !game_sp.level_solved);
15559   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15560     return (game_mm.game_over && !game_mm.level_solved);
15561   else                          // GAME_ENGINE_TYPE_RND
15562     return (game.GameOver && !game.LevelSolved);
15563 }
15564
15565 boolean checkGameEnded(void)
15566 {
15567   return (checkGameSolved() || checkGameFailed());
15568 }
15569
15570
15571 // ----------------------------------------------------------------------------
15572 // random generator functions
15573 // ----------------------------------------------------------------------------
15574
15575 unsigned int InitEngineRandom_RND(int seed)
15576 {
15577   game.num_random_calls = 0;
15578
15579   return InitEngineRandom(seed);
15580 }
15581
15582 unsigned int RND(int max)
15583 {
15584   if (max > 0)
15585   {
15586     game.num_random_calls++;
15587
15588     return GetEngineRandom(max);
15589   }
15590
15591   return 0;
15592 }
15593
15594
15595 // ----------------------------------------------------------------------------
15596 // game engine snapshot handling functions
15597 // ----------------------------------------------------------------------------
15598
15599 struct EngineSnapshotInfo
15600 {
15601   // runtime values for custom element collect score
15602   int collect_score[NUM_CUSTOM_ELEMENTS];
15603
15604   // runtime values for group element choice position
15605   int choice_pos[NUM_GROUP_ELEMENTS];
15606
15607   // runtime values for belt position animations
15608   int belt_graphic[4][NUM_BELT_PARTS];
15609   int belt_anim_mode[4][NUM_BELT_PARTS];
15610 };
15611
15612 static struct EngineSnapshotInfo engine_snapshot_rnd;
15613 static char *snapshot_level_identifier = NULL;
15614 static int snapshot_level_nr = -1;
15615
15616 static void SaveEngineSnapshotValues_RND(void)
15617 {
15618   static int belt_base_active_element[4] =
15619   {
15620     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15621     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15622     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15623     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15624   };
15625   int i, j;
15626
15627   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15628   {
15629     int element = EL_CUSTOM_START + i;
15630
15631     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15632   }
15633
15634   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15635   {
15636     int element = EL_GROUP_START + i;
15637
15638     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15639   }
15640
15641   for (i = 0; i < 4; i++)
15642   {
15643     for (j = 0; j < NUM_BELT_PARTS; j++)
15644     {
15645       int element = belt_base_active_element[i] + j;
15646       int graphic = el2img(element);
15647       int anim_mode = graphic_info[graphic].anim_mode;
15648
15649       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15650       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15651     }
15652   }
15653 }
15654
15655 static void LoadEngineSnapshotValues_RND(void)
15656 {
15657   unsigned int num_random_calls = game.num_random_calls;
15658   int i, j;
15659
15660   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15661   {
15662     int element = EL_CUSTOM_START + i;
15663
15664     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15665   }
15666
15667   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15668   {
15669     int element = EL_GROUP_START + i;
15670
15671     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15672   }
15673
15674   for (i = 0; i < 4; i++)
15675   {
15676     for (j = 0; j < NUM_BELT_PARTS; j++)
15677     {
15678       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15679       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15680
15681       graphic_info[graphic].anim_mode = anim_mode;
15682     }
15683   }
15684
15685   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15686   {
15687     InitRND(tape.random_seed);
15688     for (i = 0; i < num_random_calls; i++)
15689       RND(1);
15690   }
15691
15692   if (game.num_random_calls != num_random_calls)
15693   {
15694     Error("number of random calls out of sync");
15695     Error("number of random calls should be %d", num_random_calls);
15696     Error("number of random calls is %d", game.num_random_calls);
15697
15698     Fail("this should not happen -- please debug");
15699   }
15700 }
15701
15702 void FreeEngineSnapshotSingle(void)
15703 {
15704   FreeSnapshotSingle();
15705
15706   setString(&snapshot_level_identifier, NULL);
15707   snapshot_level_nr = -1;
15708 }
15709
15710 void FreeEngineSnapshotList(void)
15711 {
15712   FreeSnapshotList();
15713 }
15714
15715 static ListNode *SaveEngineSnapshotBuffers(void)
15716 {
15717   ListNode *buffers = NULL;
15718
15719   // copy some special values to a structure better suited for the snapshot
15720
15721   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15722     SaveEngineSnapshotValues_RND();
15723   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15724     SaveEngineSnapshotValues_EM();
15725   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15726     SaveEngineSnapshotValues_SP(&buffers);
15727   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15728     SaveEngineSnapshotValues_MM(&buffers);
15729
15730   // save values stored in special snapshot structure
15731
15732   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15733     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15734   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15735     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15736   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15737     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15738   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15739     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15740
15741   // save further RND engine values
15742
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15746
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15752
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15756
15757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15758
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15761
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15780
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15783
15784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15787
15788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15790
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15796
15797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15799
15800 #if 0
15801   ListNode *node = engine_snapshot_list_rnd;
15802   int num_bytes = 0;
15803
15804   while (node != NULL)
15805   {
15806     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15807
15808     node = node->next;
15809   }
15810
15811   Debug("game:playing:SaveEngineSnapshotBuffers",
15812         "size of engine snapshot: %d bytes", num_bytes);
15813 #endif
15814
15815   return buffers;
15816 }
15817
15818 void SaveEngineSnapshotSingle(void)
15819 {
15820   ListNode *buffers = SaveEngineSnapshotBuffers();
15821
15822   // finally save all snapshot buffers to single snapshot
15823   SaveSnapshotSingle(buffers);
15824
15825   // save level identification information
15826   setString(&snapshot_level_identifier, leveldir_current->identifier);
15827   snapshot_level_nr = level_nr;
15828 }
15829
15830 boolean CheckSaveEngineSnapshotToList(void)
15831 {
15832   boolean save_snapshot =
15833     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15834      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15835       game.snapshot.changed_action) ||
15836      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15837       game.snapshot.collected_item));
15838
15839   game.snapshot.changed_action = FALSE;
15840   game.snapshot.collected_item = FALSE;
15841   game.snapshot.save_snapshot = save_snapshot;
15842
15843   return save_snapshot;
15844 }
15845
15846 void SaveEngineSnapshotToList(void)
15847 {
15848   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15849       tape.quick_resume)
15850     return;
15851
15852   ListNode *buffers = SaveEngineSnapshotBuffers();
15853
15854   // finally save all snapshot buffers to snapshot list
15855   SaveSnapshotToList(buffers);
15856 }
15857
15858 void SaveEngineSnapshotToListInitial(void)
15859 {
15860   FreeEngineSnapshotList();
15861
15862   SaveEngineSnapshotToList();
15863 }
15864
15865 static void LoadEngineSnapshotValues(void)
15866 {
15867   // restore special values from snapshot structure
15868
15869   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15870     LoadEngineSnapshotValues_RND();
15871   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15872     LoadEngineSnapshotValues_EM();
15873   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15874     LoadEngineSnapshotValues_SP();
15875   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15876     LoadEngineSnapshotValues_MM();
15877 }
15878
15879 void LoadEngineSnapshotSingle(void)
15880 {
15881   LoadSnapshotSingle();
15882
15883   LoadEngineSnapshotValues();
15884 }
15885
15886 static void LoadEngineSnapshot_Undo(int steps)
15887 {
15888   LoadSnapshotFromList_Older(steps);
15889
15890   LoadEngineSnapshotValues();
15891 }
15892
15893 static void LoadEngineSnapshot_Redo(int steps)
15894 {
15895   LoadSnapshotFromList_Newer(steps);
15896
15897   LoadEngineSnapshotValues();
15898 }
15899
15900 boolean CheckEngineSnapshotSingle(void)
15901 {
15902   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15903           snapshot_level_nr == level_nr);
15904 }
15905
15906 boolean CheckEngineSnapshotList(void)
15907 {
15908   return CheckSnapshotList();
15909 }
15910
15911
15912 // ---------- new game button stuff -------------------------------------------
15913
15914 static struct
15915 {
15916   int graphic;
15917   struct XY *pos;
15918   int gadget_id;
15919   boolean *setup_value;
15920   boolean allowed_on_tape;
15921   boolean is_touch_button;
15922   char *infotext;
15923 } gamebutton_info[NUM_GAME_BUTTONS] =
15924 {
15925   {
15926     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15927     GAME_CTRL_ID_STOP,                          NULL,
15928     TRUE, FALSE,                                "stop game"
15929   },
15930   {
15931     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15932     GAME_CTRL_ID_PAUSE,                         NULL,
15933     TRUE, FALSE,                                "pause game"
15934   },
15935   {
15936     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15937     GAME_CTRL_ID_PLAY,                          NULL,
15938     TRUE, FALSE,                                "play game"
15939   },
15940   {
15941     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15942     GAME_CTRL_ID_UNDO,                          NULL,
15943     TRUE, FALSE,                                "undo step"
15944   },
15945   {
15946     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15947     GAME_CTRL_ID_REDO,                          NULL,
15948     TRUE, FALSE,                                "redo step"
15949   },
15950   {
15951     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15952     GAME_CTRL_ID_SAVE,                          NULL,
15953     TRUE, FALSE,                                "save game"
15954   },
15955   {
15956     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15957     GAME_CTRL_ID_PAUSE2,                        NULL,
15958     TRUE, FALSE,                                "pause game"
15959   },
15960   {
15961     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15962     GAME_CTRL_ID_LOAD,                          NULL,
15963     TRUE, FALSE,                                "load game"
15964   },
15965   {
15966     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15967     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15968     FALSE, FALSE,                               "stop game"
15969   },
15970   {
15971     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15972     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15973     FALSE, FALSE,                               "pause game"
15974   },
15975   {
15976     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15977     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15978     FALSE, FALSE,                               "play game"
15979   },
15980   {
15981     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15982     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15983     FALSE, TRUE,                                "stop game"
15984   },
15985   {
15986     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15987     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15988     FALSE, TRUE,                                "pause game"
15989   },
15990   {
15991     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15992     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15993     TRUE, FALSE,                                "background music on/off"
15994   },
15995   {
15996     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15997     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15998     TRUE, FALSE,                                "sound loops on/off"
15999   },
16000   {
16001     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16002     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16003     TRUE, FALSE,                                "normal sounds on/off"
16004   },
16005   {
16006     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16007     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16008     FALSE, FALSE,                               "background music on/off"
16009   },
16010   {
16011     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16012     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16013     FALSE, FALSE,                               "sound loops on/off"
16014   },
16015   {
16016     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16017     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16018     FALSE, FALSE,                               "normal sounds on/off"
16019   }
16020 };
16021
16022 void CreateGameButtons(void)
16023 {
16024   int i;
16025
16026   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16027   {
16028     int graphic = gamebutton_info[i].graphic;
16029     struct GraphicInfo *gfx = &graphic_info[graphic];
16030     struct XY *pos = gamebutton_info[i].pos;
16031     struct GadgetInfo *gi;
16032     int button_type;
16033     boolean checked;
16034     unsigned int event_mask;
16035     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16036     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16037     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16038     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16039     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16040     int gd_x   = gfx->src_x;
16041     int gd_y   = gfx->src_y;
16042     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16043     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16044     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16045     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16046     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16047     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16048     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16049     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16050     int id = i;
16051
16052     if (gfx->bitmap == NULL)
16053     {
16054       game_gadget[id] = NULL;
16055
16056       continue;
16057     }
16058
16059     if (id == GAME_CTRL_ID_STOP ||
16060         id == GAME_CTRL_ID_PANEL_STOP ||
16061         id == GAME_CTRL_ID_TOUCH_STOP ||
16062         id == GAME_CTRL_ID_PLAY ||
16063         id == GAME_CTRL_ID_PANEL_PLAY ||
16064         id == GAME_CTRL_ID_SAVE ||
16065         id == GAME_CTRL_ID_LOAD)
16066     {
16067       button_type = GD_TYPE_NORMAL_BUTTON;
16068       checked = FALSE;
16069       event_mask = GD_EVENT_RELEASED;
16070     }
16071     else if (id == GAME_CTRL_ID_UNDO ||
16072              id == GAME_CTRL_ID_REDO)
16073     {
16074       button_type = GD_TYPE_NORMAL_BUTTON;
16075       checked = FALSE;
16076       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16077     }
16078     else
16079     {
16080       button_type = GD_TYPE_CHECK_BUTTON;
16081       checked = (gamebutton_info[i].setup_value != NULL ?
16082                  *gamebutton_info[i].setup_value : FALSE);
16083       event_mask = GD_EVENT_PRESSED;
16084     }
16085
16086     gi = CreateGadget(GDI_CUSTOM_ID, id,
16087                       GDI_IMAGE_ID, graphic,
16088                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16089                       GDI_X, base_x + x,
16090                       GDI_Y, base_y + y,
16091                       GDI_WIDTH, gfx->width,
16092                       GDI_HEIGHT, gfx->height,
16093                       GDI_TYPE, button_type,
16094                       GDI_STATE, GD_BUTTON_UNPRESSED,
16095                       GDI_CHECKED, checked,
16096                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16097                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16098                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16099                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16100                       GDI_DIRECT_DRAW, FALSE,
16101                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16102                       GDI_EVENT_MASK, event_mask,
16103                       GDI_CALLBACK_ACTION, HandleGameButtons,
16104                       GDI_END);
16105
16106     if (gi == NULL)
16107       Fail("cannot create gadget");
16108
16109     game_gadget[id] = gi;
16110   }
16111 }
16112
16113 void FreeGameButtons(void)
16114 {
16115   int i;
16116
16117   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16118     FreeGadget(game_gadget[i]);
16119 }
16120
16121 static void UnmapGameButtonsAtSamePosition(int id)
16122 {
16123   int i;
16124
16125   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16126     if (i != id &&
16127         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16128         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16129       UnmapGadget(game_gadget[i]);
16130 }
16131
16132 static void UnmapGameButtonsAtSamePosition_All(void)
16133 {
16134   if (setup.show_snapshot_buttons)
16135   {
16136     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16137     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16138     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16139   }
16140   else
16141   {
16142     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16143     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16144     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16145
16146     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16147     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16148     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16149   }
16150 }
16151
16152 static void MapGameButtonsAtSamePosition(int id)
16153 {
16154   int i;
16155
16156   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16157     if (i != id &&
16158         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16159         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16160       MapGadget(game_gadget[i]);
16161
16162   UnmapGameButtonsAtSamePosition_All();
16163 }
16164
16165 void MapUndoRedoButtons(void)
16166 {
16167   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16168   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16169
16170   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16171   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16172 }
16173
16174 void UnmapUndoRedoButtons(void)
16175 {
16176   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16177   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16178
16179   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16180   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16181 }
16182
16183 void ModifyPauseButtons(void)
16184 {
16185   static int ids[] =
16186   {
16187     GAME_CTRL_ID_PAUSE,
16188     GAME_CTRL_ID_PAUSE2,
16189     GAME_CTRL_ID_PANEL_PAUSE,
16190     GAME_CTRL_ID_TOUCH_PAUSE,
16191     -1
16192   };
16193   int i;
16194
16195   for (i = 0; ids[i] > -1; i++)
16196     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16197 }
16198
16199 static void MapGameButtonsExt(boolean on_tape)
16200 {
16201   int i;
16202
16203   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16204     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16205         i != GAME_CTRL_ID_UNDO &&
16206         i != GAME_CTRL_ID_REDO)
16207       MapGadget(game_gadget[i]);
16208
16209   UnmapGameButtonsAtSamePosition_All();
16210
16211   RedrawGameButtons();
16212 }
16213
16214 static void UnmapGameButtonsExt(boolean on_tape)
16215 {
16216   int i;
16217
16218   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16219     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16220       UnmapGadget(game_gadget[i]);
16221 }
16222
16223 static void RedrawGameButtonsExt(boolean on_tape)
16224 {
16225   int i;
16226
16227   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16228     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16229       RedrawGadget(game_gadget[i]);
16230 }
16231
16232 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16233 {
16234   if (gi == NULL)
16235     return;
16236
16237   gi->checked = state;
16238 }
16239
16240 static void RedrawSoundButtonGadget(int id)
16241 {
16242   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16243              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16244              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16245              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16246              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16247              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16248              id);
16249
16250   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16251   RedrawGadget(game_gadget[id2]);
16252 }
16253
16254 void MapGameButtons(void)
16255 {
16256   MapGameButtonsExt(FALSE);
16257 }
16258
16259 void UnmapGameButtons(void)
16260 {
16261   UnmapGameButtonsExt(FALSE);
16262 }
16263
16264 void RedrawGameButtons(void)
16265 {
16266   RedrawGameButtonsExt(FALSE);
16267 }
16268
16269 void MapGameButtonsOnTape(void)
16270 {
16271   MapGameButtonsExt(TRUE);
16272 }
16273
16274 void UnmapGameButtonsOnTape(void)
16275 {
16276   UnmapGameButtonsExt(TRUE);
16277 }
16278
16279 void RedrawGameButtonsOnTape(void)
16280 {
16281   RedrawGameButtonsExt(TRUE);
16282 }
16283
16284 static void GameUndoRedoExt(void)
16285 {
16286   ClearPlayerAction();
16287
16288   tape.pausing = TRUE;
16289
16290   RedrawPlayfield();
16291   UpdateAndDisplayGameControlValues();
16292
16293   DrawCompleteVideoDisplay();
16294   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16295   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16296   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16297
16298   BackToFront();
16299 }
16300
16301 static void GameUndo(int steps)
16302 {
16303   if (!CheckEngineSnapshotList())
16304     return;
16305
16306   LoadEngineSnapshot_Undo(steps);
16307
16308   GameUndoRedoExt();
16309 }
16310
16311 static void GameRedo(int steps)
16312 {
16313   if (!CheckEngineSnapshotList())
16314     return;
16315
16316   LoadEngineSnapshot_Redo(steps);
16317
16318   GameUndoRedoExt();
16319 }
16320
16321 static void HandleGameButtonsExt(int id, int button)
16322 {
16323   static boolean game_undo_executed = FALSE;
16324   int steps = BUTTON_STEPSIZE(button);
16325   boolean handle_game_buttons =
16326     (game_status == GAME_MODE_PLAYING ||
16327      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16328
16329   if (!handle_game_buttons)
16330     return;
16331
16332   switch (id)
16333   {
16334     case GAME_CTRL_ID_STOP:
16335     case GAME_CTRL_ID_PANEL_STOP:
16336     case GAME_CTRL_ID_TOUCH_STOP:
16337       if (game_status == GAME_MODE_MAIN)
16338         break;
16339
16340       if (tape.playing)
16341         TapeStop();
16342       else
16343         RequestQuitGame(FALSE);
16344
16345       break;
16346
16347     case GAME_CTRL_ID_PAUSE:
16348     case GAME_CTRL_ID_PAUSE2:
16349     case GAME_CTRL_ID_PANEL_PAUSE:
16350     case GAME_CTRL_ID_TOUCH_PAUSE:
16351       if (network.enabled && game_status == GAME_MODE_PLAYING)
16352       {
16353         if (tape.pausing)
16354           SendToServer_ContinuePlaying();
16355         else
16356           SendToServer_PausePlaying();
16357       }
16358       else
16359         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16360
16361       game_undo_executed = FALSE;
16362
16363       break;
16364
16365     case GAME_CTRL_ID_PLAY:
16366     case GAME_CTRL_ID_PANEL_PLAY:
16367       if (game_status == GAME_MODE_MAIN)
16368       {
16369         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16370       }
16371       else if (tape.pausing)
16372       {
16373         if (network.enabled)
16374           SendToServer_ContinuePlaying();
16375         else
16376           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16377       }
16378       break;
16379
16380     case GAME_CTRL_ID_UNDO:
16381       // Important: When using "save snapshot when collecting an item" mode,
16382       // load last (current) snapshot for first "undo" after pressing "pause"
16383       // (else the last-but-one snapshot would be loaded, because the snapshot
16384       // pointer already points to the last snapshot when pressing "pause",
16385       // which is fine for "every step/move" mode, but not for "every collect")
16386       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16387           !game_undo_executed)
16388         steps--;
16389
16390       game_undo_executed = TRUE;
16391
16392       GameUndo(steps);
16393       break;
16394
16395     case GAME_CTRL_ID_REDO:
16396       GameRedo(steps);
16397       break;
16398
16399     case GAME_CTRL_ID_SAVE:
16400       TapeQuickSave();
16401       break;
16402
16403     case GAME_CTRL_ID_LOAD:
16404       TapeQuickLoad();
16405       break;
16406
16407     case SOUND_CTRL_ID_MUSIC:
16408     case SOUND_CTRL_ID_PANEL_MUSIC:
16409       if (setup.sound_music)
16410       { 
16411         setup.sound_music = FALSE;
16412
16413         FadeMusic();
16414       }
16415       else if (audio.music_available)
16416       { 
16417         setup.sound = setup.sound_music = TRUE;
16418
16419         SetAudioMode(setup.sound);
16420
16421         if (game_status == GAME_MODE_PLAYING)
16422           PlayLevelMusic();
16423       }
16424
16425       RedrawSoundButtonGadget(id);
16426
16427       break;
16428
16429     case SOUND_CTRL_ID_LOOPS:
16430     case SOUND_CTRL_ID_PANEL_LOOPS:
16431       if (setup.sound_loops)
16432         setup.sound_loops = FALSE;
16433       else if (audio.loops_available)
16434       {
16435         setup.sound = setup.sound_loops = TRUE;
16436
16437         SetAudioMode(setup.sound);
16438       }
16439
16440       RedrawSoundButtonGadget(id);
16441
16442       break;
16443
16444     case SOUND_CTRL_ID_SIMPLE:
16445     case SOUND_CTRL_ID_PANEL_SIMPLE:
16446       if (setup.sound_simple)
16447         setup.sound_simple = FALSE;
16448       else if (audio.sound_available)
16449       {
16450         setup.sound = setup.sound_simple = TRUE;
16451
16452         SetAudioMode(setup.sound);
16453       }
16454
16455       RedrawSoundButtonGadget(id);
16456
16457       break;
16458
16459     default:
16460       break;
16461   }
16462 }
16463
16464 static void HandleGameButtons(struct GadgetInfo *gi)
16465 {
16466   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16467 }
16468
16469 void HandleSoundButtonKeys(Key key)
16470 {
16471   if (key == setup.shortcut.sound_simple)
16472     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16473   else if (key == setup.shortcut.sound_loops)
16474     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16475   else if (key == setup.shortcut.sound_music)
16476     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16477 }