moved updating game panel values after solving game to separate function
[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_sp = TRUE;    // unless non-SUPAPLEX    elements found
3546   int initial_move_dir = MV_DOWN;
3547   int i, j, x, y;
3548
3549   // required here to update video display before fading (FIX THIS)
3550   DrawMaskedBorder(REDRAW_DOOR_2);
3551
3552   if (!game.restart_level)
3553     CloseDoor(DOOR_CLOSE_1);
3554
3555   SetGameStatus(GAME_MODE_PLAYING);
3556
3557   if (level_editor_test_game)
3558     FadeSkipNextFadeOut();
3559   else
3560     FadeSetEnterScreen();
3561
3562   if (CheckFadeAll())
3563     fade_mask = REDRAW_ALL;
3564
3565   FadeLevelSoundsAndMusic();
3566
3567   ExpireSoundLoops(TRUE);
3568
3569   FadeOut(fade_mask);
3570
3571   if (level_editor_test_game)
3572     FadeSkipNextFadeIn();
3573
3574   // needed if different viewport properties defined for playing
3575   ChangeViewportPropertiesIfNeeded();
3576
3577   ClearField();
3578
3579   DrawCompleteVideoDisplay();
3580
3581   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3582
3583   InitGameEngine();
3584   InitGameControlValues();
3585
3586   if (tape.recording)
3587   {
3588     // initialize tape actions from game when recording tape
3589     tape.use_key_actions   = game.use_key_actions;
3590     tape.use_mouse_actions = game.use_mouse_actions;
3591
3592     // initialize visible playfield size when recording tape (for team mode)
3593     tape.scr_fieldx = SCR_FIELDX;
3594     tape.scr_fieldy = SCR_FIELDY;
3595   }
3596
3597   // don't play tapes over network
3598   network_playing = (network.enabled && !tape.playing);
3599
3600   for (i = 0; i < MAX_PLAYERS; i++)
3601   {
3602     struct PlayerInfo *player = &stored_player[i];
3603
3604     player->index_nr = i;
3605     player->index_bit = (1 << i);
3606     player->element_nr = EL_PLAYER_1 + i;
3607
3608     player->present = FALSE;
3609     player->active = FALSE;
3610     player->mapped = FALSE;
3611
3612     player->killed = FALSE;
3613     player->reanimated = FALSE;
3614     player->buried = FALSE;
3615
3616     player->action = 0;
3617     player->effective_action = 0;
3618     player->programmed_action = 0;
3619     player->snap_action = 0;
3620
3621     player->mouse_action.lx = 0;
3622     player->mouse_action.ly = 0;
3623     player->mouse_action.button = 0;
3624     player->mouse_action.button_hint = 0;
3625
3626     player->effective_mouse_action.lx = 0;
3627     player->effective_mouse_action.ly = 0;
3628     player->effective_mouse_action.button = 0;
3629     player->effective_mouse_action.button_hint = 0;
3630
3631     for (j = 0; j < MAX_NUM_KEYS; j++)
3632       player->key[j] = FALSE;
3633
3634     player->num_white_keys = 0;
3635
3636     player->dynabomb_count = 0;
3637     player->dynabomb_size = 1;
3638     player->dynabombs_left = 0;
3639     player->dynabomb_xl = FALSE;
3640
3641     player->MovDir = initial_move_dir;
3642     player->MovPos = 0;
3643     player->GfxPos = 0;
3644     player->GfxDir = initial_move_dir;
3645     player->GfxAction = ACTION_DEFAULT;
3646     player->Frame = 0;
3647     player->StepFrame = 0;
3648
3649     player->initial_element = player->element_nr;
3650     player->artwork_element =
3651       (level.use_artwork_element[i] ? level.artwork_element[i] :
3652        player->element_nr);
3653     player->use_murphy = FALSE;
3654
3655     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3656     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3657
3658     player->gravity = level.initial_player_gravity[i];
3659
3660     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3661
3662     player->actual_frame_counter = 0;
3663
3664     player->step_counter = 0;
3665
3666     player->last_move_dir = initial_move_dir;
3667
3668     player->is_active = FALSE;
3669
3670     player->is_waiting = FALSE;
3671     player->is_moving = FALSE;
3672     player->is_auto_moving = FALSE;
3673     player->is_digging = FALSE;
3674     player->is_snapping = FALSE;
3675     player->is_collecting = FALSE;
3676     player->is_pushing = FALSE;
3677     player->is_switching = FALSE;
3678     player->is_dropping = FALSE;
3679     player->is_dropping_pressed = FALSE;
3680
3681     player->is_bored = FALSE;
3682     player->is_sleeping = FALSE;
3683
3684     player->was_waiting = TRUE;
3685     player->was_moving = FALSE;
3686     player->was_snapping = FALSE;
3687     player->was_dropping = FALSE;
3688
3689     player->force_dropping = FALSE;
3690
3691     player->frame_counter_bored = -1;
3692     player->frame_counter_sleeping = -1;
3693
3694     player->anim_delay_counter = 0;
3695     player->post_delay_counter = 0;
3696
3697     player->dir_waiting = initial_move_dir;
3698     player->action_waiting = ACTION_DEFAULT;
3699     player->last_action_waiting = ACTION_DEFAULT;
3700     player->special_action_bored = ACTION_DEFAULT;
3701     player->special_action_sleeping = ACTION_DEFAULT;
3702
3703     player->switch_x = -1;
3704     player->switch_y = -1;
3705
3706     player->drop_x = -1;
3707     player->drop_y = -1;
3708
3709     player->show_envelope = 0;
3710
3711     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3712
3713     player->push_delay       = -1;      // initialized when pushing starts
3714     player->push_delay_value = game.initial_push_delay_value;
3715
3716     player->drop_delay = 0;
3717     player->drop_pressed_delay = 0;
3718
3719     player->last_jx = -1;
3720     player->last_jy = -1;
3721     player->jx = -1;
3722     player->jy = -1;
3723
3724     player->shield_normal_time_left = 0;
3725     player->shield_deadly_time_left = 0;
3726
3727     player->last_removed_element = EL_UNDEFINED;
3728
3729     player->inventory_infinite_element = EL_UNDEFINED;
3730     player->inventory_size = 0;
3731
3732     if (level.use_initial_inventory[i])
3733     {
3734       for (j = 0; j < level.initial_inventory_size[i]; j++)
3735       {
3736         int element = level.initial_inventory_content[i][j];
3737         int collect_count = element_info[element].collect_count_initial;
3738         int k;
3739
3740         if (!IS_CUSTOM_ELEMENT(element))
3741           collect_count = 1;
3742
3743         if (collect_count == 0)
3744           player->inventory_infinite_element = element;
3745         else
3746           for (k = 0; k < collect_count; k++)
3747             if (player->inventory_size < MAX_INVENTORY_SIZE)
3748               player->inventory_element[player->inventory_size++] = element;
3749       }
3750     }
3751
3752     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3753     SnapField(player, 0, 0);
3754
3755     map_player_action[i] = i;
3756   }
3757
3758   network_player_action_received = FALSE;
3759
3760   // initial null action
3761   if (network_playing)
3762     SendToServer_MovePlayer(MV_NONE);
3763
3764   FrameCounter = 0;
3765   TimeFrames = 0;
3766   TimePlayed = 0;
3767   TimeLeft = level.time;
3768   TapeTime = 0;
3769
3770   ScreenMovDir = MV_NONE;
3771   ScreenMovPos = 0;
3772   ScreenGfxPos = 0;
3773
3774   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3775
3776   game.robot_wheel_x = -1;
3777   game.robot_wheel_y = -1;
3778
3779   game.exit_x = -1;
3780   game.exit_y = -1;
3781
3782   game.all_players_gone = FALSE;
3783
3784   game.LevelSolved = FALSE;
3785   game.GameOver = FALSE;
3786
3787   game.GamePlayed = !tape.playing;
3788
3789   game.LevelSolved_GameWon = FALSE;
3790   game.LevelSolved_GameEnd = FALSE;
3791   game.LevelSolved_SaveTape = FALSE;
3792   game.LevelSolved_SaveScore = FALSE;
3793
3794   game.LevelSolved_CountingTime = 0;
3795   game.LevelSolved_CountingScore = 0;
3796   game.LevelSolved_CountingHealth = 0;
3797
3798   game.panel.active = TRUE;
3799
3800   game.no_time_limit = (level.time == 0);
3801
3802   game.yamyam_content_nr = 0;
3803   game.robot_wheel_active = FALSE;
3804   game.magic_wall_active = FALSE;
3805   game.magic_wall_time_left = 0;
3806   game.light_time_left = 0;
3807   game.timegate_time_left = 0;
3808   game.switchgate_pos = 0;
3809   game.wind_direction = level.wind_direction_initial;
3810
3811   game.time_final = 0;
3812   game.score_time_final = 0;
3813
3814   game.score = 0;
3815   game.score_final = 0;
3816
3817   game.health = MAX_HEALTH;
3818   game.health_final = MAX_HEALTH;
3819
3820   game.gems_still_needed = level.gems_needed;
3821   game.sokoban_fields_still_needed = 0;
3822   game.sokoban_objects_still_needed = 0;
3823   game.lights_still_needed = 0;
3824   game.players_still_needed = 0;
3825   game.friends_still_needed = 0;
3826
3827   game.lenses_time_left = 0;
3828   game.magnify_time_left = 0;
3829
3830   game.ball_active = level.ball_active_initial;
3831   game.ball_content_nr = 0;
3832
3833   game.explosions_delayed = TRUE;
3834
3835   game.envelope_active = FALSE;
3836
3837   for (i = 0; i < NUM_BELTS; i++)
3838   {
3839     game.belt_dir[i] = MV_NONE;
3840     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3841   }
3842
3843   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3844     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3845
3846 #if DEBUG_INIT_PLAYER
3847   DebugPrintPlayerStatus("Player status at level initialization");
3848 #endif
3849
3850   SCAN_PLAYFIELD(x, y)
3851   {
3852     Tile[x][y] = Last[x][y] = level.field[x][y];
3853     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3854     ChangeDelay[x][y] = 0;
3855     ChangePage[x][y] = -1;
3856     CustomValue[x][y] = 0;              // initialized in InitField()
3857     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3858     AmoebaNr[x][y] = 0;
3859     WasJustMoving[x][y] = 0;
3860     WasJustFalling[x][y] = 0;
3861     CheckCollision[x][y] = 0;
3862     CheckImpact[x][y] = 0;
3863     Stop[x][y] = FALSE;
3864     Pushed[x][y] = FALSE;
3865
3866     ChangeCount[x][y] = 0;
3867     ChangeEvent[x][y] = -1;
3868
3869     ExplodePhase[x][y] = 0;
3870     ExplodeDelay[x][y] = 0;
3871     ExplodeField[x][y] = EX_TYPE_NONE;
3872
3873     RunnerVisit[x][y] = 0;
3874     PlayerVisit[x][y] = 0;
3875
3876     GfxFrame[x][y] = 0;
3877     GfxRandom[x][y] = INIT_GFX_RANDOM();
3878     GfxElement[x][y] = EL_UNDEFINED;
3879     GfxAction[x][y] = ACTION_DEFAULT;
3880     GfxDir[x][y] = MV_NONE;
3881     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3882   }
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3887       emulate_bd = FALSE;
3888     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3889       emulate_sp = FALSE;
3890
3891     InitField(x, y, TRUE);
3892
3893     ResetGfxAnimation(x, y);
3894   }
3895
3896   InitBeltMovement();
3897
3898   for (i = 0; i < MAX_PLAYERS; i++)
3899   {
3900     struct PlayerInfo *player = &stored_player[i];
3901
3902     // set number of special actions for bored and sleeping animation
3903     player->num_special_action_bored =
3904       get_num_special_action(player->artwork_element,
3905                              ACTION_BORING_1, ACTION_BORING_LAST);
3906     player->num_special_action_sleeping =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3909   }
3910
3911   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3912                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3913
3914   // initialize type of slippery elements
3915   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3916   {
3917     if (!IS_CUSTOM_ELEMENT(i))
3918     {
3919       // default: elements slip down either to the left or right randomly
3920       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3921
3922       // SP style elements prefer to slip down on the left side
3923       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3924         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3925
3926       // BD style elements prefer to slip down on the left side
3927       if (game.emulation == EMU_BOULDERDASH)
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929     }
3930   }
3931
3932   // initialize explosion and ignition delay
3933   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3934   {
3935     if (!IS_CUSTOM_ELEMENT(i))
3936     {
3937       int num_phase = 8;
3938       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3939                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3940                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3941       int last_phase = (num_phase + 1) * delay;
3942       int half_phase = (num_phase / 2) * delay;
3943
3944       element_info[i].explosion_delay = last_phase - 1;
3945       element_info[i].ignition_delay = half_phase;
3946
3947       if (i == EL_BLACK_ORB)
3948         element_info[i].ignition_delay = 1;
3949     }
3950   }
3951
3952   // correct non-moving belts to start moving left
3953   for (i = 0; i < NUM_BELTS; i++)
3954     if (game.belt_dir[i] == MV_NONE)
3955       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3956
3957 #if USE_NEW_PLAYER_ASSIGNMENTS
3958   // use preferred player also in local single-player mode
3959   if (!network.enabled && !game.team_mode)
3960   {
3961     int new_index_nr = setup.network_player_nr;
3962
3963     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3964     {
3965       for (i = 0; i < MAX_PLAYERS; i++)
3966         stored_player[i].connected_locally = FALSE;
3967
3968       stored_player[new_index_nr].connected_locally = TRUE;
3969     }
3970   }
3971
3972   for (i = 0; i < MAX_PLAYERS; i++)
3973   {
3974     stored_player[i].connected = FALSE;
3975
3976     // in network game mode, the local player might not be the first player
3977     if (stored_player[i].connected_locally)
3978       local_player = &stored_player[i];
3979   }
3980
3981   if (!network.enabled)
3982     local_player->connected = TRUE;
3983
3984   if (tape.playing)
3985   {
3986     for (i = 0; i < MAX_PLAYERS; i++)
3987       stored_player[i].connected = tape.player_participates[i];
3988   }
3989   else if (network.enabled)
3990   {
3991     // add team mode players connected over the network (needed for correct
3992     // assignment of player figures from level to locally playing players)
3993
3994     for (i = 0; i < MAX_PLAYERS; i++)
3995       if (stored_player[i].connected_network)
3996         stored_player[i].connected = TRUE;
3997   }
3998   else if (game.team_mode)
3999   {
4000     // try to guess locally connected team mode players (needed for correct
4001     // assignment of player figures from level to locally playing players)
4002
4003     for (i = 0; i < MAX_PLAYERS; i++)
4004       if (setup.input[i].use_joystick ||
4005           setup.input[i].key.left != KSYM_UNDEFINED)
4006         stored_player[i].connected = TRUE;
4007   }
4008
4009 #if DEBUG_INIT_PLAYER
4010   DebugPrintPlayerStatus("Player status after level initialization");
4011 #endif
4012
4013 #if DEBUG_INIT_PLAYER
4014   Debug("game:init:player", "Reassigning players ...");
4015 #endif
4016
4017   // check if any connected player was not found in playfield
4018   for (i = 0; i < MAX_PLAYERS; i++)
4019   {
4020     struct PlayerInfo *player = &stored_player[i];
4021
4022     if (player->connected && !player->present)
4023     {
4024       struct PlayerInfo *field_player = NULL;
4025
4026 #if DEBUG_INIT_PLAYER
4027       Debug("game:init:player",
4028             "- looking for field player for player %d ...", i + 1);
4029 #endif
4030
4031       // assign first free player found that is present in the playfield
4032
4033       // first try: look for unmapped playfield player that is not connected
4034       for (j = 0; j < MAX_PLAYERS; j++)
4035         if (field_player == NULL &&
4036             stored_player[j].present &&
4037             !stored_player[j].mapped &&
4038             !stored_player[j].connected)
4039           field_player = &stored_player[j];
4040
4041       // second try: look for *any* unmapped playfield player
4042       for (j = 0; j < MAX_PLAYERS; j++)
4043         if (field_player == NULL &&
4044             stored_player[j].present &&
4045             !stored_player[j].mapped)
4046           field_player = &stored_player[j];
4047
4048       if (field_player != NULL)
4049       {
4050         int jx = field_player->jx, jy = field_player->jy;
4051
4052 #if DEBUG_INIT_PLAYER
4053         Debug("game:init:player", "- found player %d",
4054               field_player->index_nr + 1);
4055 #endif
4056
4057         player->present = FALSE;
4058         player->active = FALSE;
4059
4060         field_player->present = TRUE;
4061         field_player->active = TRUE;
4062
4063         /*
4064         player->initial_element = field_player->initial_element;
4065         player->artwork_element = field_player->artwork_element;
4066
4067         player->block_last_field       = field_player->block_last_field;
4068         player->block_delay_adjustment = field_player->block_delay_adjustment;
4069         */
4070
4071         StorePlayer[jx][jy] = field_player->element_nr;
4072
4073         field_player->jx = field_player->last_jx = jx;
4074         field_player->jy = field_player->last_jy = jy;
4075
4076         if (local_player == player)
4077           local_player = field_player;
4078
4079         map_player_action[field_player->index_nr] = i;
4080
4081         field_player->mapped = TRUE;
4082
4083 #if DEBUG_INIT_PLAYER
4084         Debug("game:init:player", "- map_player_action[%d] == %d",
4085               field_player->index_nr + 1, i + 1);
4086 #endif
4087       }
4088     }
4089
4090     if (player->connected && player->present)
4091       player->mapped = TRUE;
4092   }
4093
4094 #if DEBUG_INIT_PLAYER
4095   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4096 #endif
4097
4098 #else
4099
4100   // check if any connected player was not found in playfield
4101   for (i = 0; i < MAX_PLAYERS; i++)
4102   {
4103     struct PlayerInfo *player = &stored_player[i];
4104
4105     if (player->connected && !player->present)
4106     {
4107       for (j = 0; j < MAX_PLAYERS; j++)
4108       {
4109         struct PlayerInfo *field_player = &stored_player[j];
4110         int jx = field_player->jx, jy = field_player->jy;
4111
4112         // assign first free player found that is present in the playfield
4113         if (field_player->present && !field_player->connected)
4114         {
4115           player->present = TRUE;
4116           player->active = TRUE;
4117
4118           field_player->present = FALSE;
4119           field_player->active = FALSE;
4120
4121           player->initial_element = field_player->initial_element;
4122           player->artwork_element = field_player->artwork_element;
4123
4124           player->block_last_field       = field_player->block_last_field;
4125           player->block_delay_adjustment = field_player->block_delay_adjustment;
4126
4127           StorePlayer[jx][jy] = player->element_nr;
4128
4129           player->jx = player->last_jx = jx;
4130           player->jy = player->last_jy = jy;
4131
4132           break;
4133         }
4134       }
4135     }
4136   }
4137 #endif
4138
4139 #if 0
4140   Debug("game:init:player", "local_player->present == %d",
4141         local_player->present);
4142 #endif
4143
4144   // set focus to local player for network games, else to all players
4145   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4146   game.centered_player_nr_next = game.centered_player_nr;
4147   game.set_centered_player = FALSE;
4148   game.set_centered_player_wrap = FALSE;
4149
4150   if (network_playing && tape.recording)
4151   {
4152     // store client dependent player focus when recording network games
4153     tape.centered_player_nr_next = game.centered_player_nr_next;
4154     tape.set_centered_player = TRUE;
4155   }
4156
4157   if (tape.playing)
4158   {
4159     // when playing a tape, eliminate all players who do not participate
4160
4161 #if USE_NEW_PLAYER_ASSIGNMENTS
4162
4163     if (!game.team_mode)
4164     {
4165       for (i = 0; i < MAX_PLAYERS; i++)
4166       {
4167         if (stored_player[i].active &&
4168             !tape.player_participates[map_player_action[i]])
4169         {
4170           struct PlayerInfo *player = &stored_player[i];
4171           int jx = player->jx, jy = player->jy;
4172
4173 #if DEBUG_INIT_PLAYER
4174           Debug("game:init:player", "Removing player %d at (%d, %d)",
4175                 i + 1, jx, jy);
4176 #endif
4177
4178           player->active = FALSE;
4179           StorePlayer[jx][jy] = 0;
4180           Tile[jx][jy] = EL_EMPTY;
4181         }
4182       }
4183     }
4184
4185 #else
4186
4187     for (i = 0; i < MAX_PLAYERS; i++)
4188     {
4189       if (stored_player[i].active &&
4190           !tape.player_participates[i])
4191       {
4192         struct PlayerInfo *player = &stored_player[i];
4193         int jx = player->jx, jy = player->jy;
4194
4195         player->active = FALSE;
4196         StorePlayer[jx][jy] = 0;
4197         Tile[jx][jy] = EL_EMPTY;
4198       }
4199     }
4200 #endif
4201   }
4202   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4203   {
4204     // when in single player mode, eliminate all but the local player
4205
4206     for (i = 0; i < MAX_PLAYERS; i++)
4207     {
4208       struct PlayerInfo *player = &stored_player[i];
4209
4210       if (player->active && player != local_player)
4211       {
4212         int jx = player->jx, jy = player->jy;
4213
4214         player->active = FALSE;
4215         player->present = FALSE;
4216
4217         StorePlayer[jx][jy] = 0;
4218         Tile[jx][jy] = EL_EMPTY;
4219       }
4220     }
4221   }
4222
4223   for (i = 0; i < MAX_PLAYERS; i++)
4224     if (stored_player[i].active)
4225       game.players_still_needed++;
4226
4227   if (level.solved_by_one_player)
4228     game.players_still_needed = 1;
4229
4230   // when recording the game, store which players take part in the game
4231   if (tape.recording)
4232   {
4233 #if USE_NEW_PLAYER_ASSIGNMENTS
4234     for (i = 0; i < MAX_PLAYERS; i++)
4235       if (stored_player[i].connected)
4236         tape.player_participates[i] = TRUE;
4237 #else
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].active)
4240         tape.player_participates[i] = TRUE;
4241 #endif
4242   }
4243
4244 #if DEBUG_INIT_PLAYER
4245   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4246 #endif
4247
4248   if (BorderElement == EL_EMPTY)
4249   {
4250     SBX_Left = 0;
4251     SBX_Right = lev_fieldx - SCR_FIELDX;
4252     SBY_Upper = 0;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY;
4254   }
4255   else
4256   {
4257     SBX_Left = -1;
4258     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4259     SBY_Upper = -1;
4260     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4261   }
4262
4263   if (full_lev_fieldx <= SCR_FIELDX)
4264     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4265   if (full_lev_fieldy <= SCR_FIELDY)
4266     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4267
4268   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4269     SBX_Left--;
4270   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4271     SBY_Upper--;
4272
4273   // if local player not found, look for custom element that might create
4274   // the player (make some assumptions about the right custom element)
4275   if (!local_player->present)
4276   {
4277     int start_x = 0, start_y = 0;
4278     int found_rating = 0;
4279     int found_element = EL_UNDEFINED;
4280     int player_nr = local_player->index_nr;
4281
4282     SCAN_PLAYFIELD(x, y)
4283     {
4284       int element = Tile[x][y];
4285       int content;
4286       int xx, yy;
4287       boolean is_player;
4288
4289       if (level.use_start_element[player_nr] &&
4290           level.start_element[player_nr] == element &&
4291           found_rating < 4)
4292       {
4293         start_x = x;
4294         start_y = y;
4295
4296         found_rating = 4;
4297         found_element = element;
4298       }
4299
4300       if (!IS_CUSTOM_ELEMENT(element))
4301         continue;
4302
4303       if (CAN_CHANGE(element))
4304       {
4305         for (i = 0; i < element_info[element].num_change_pages; i++)
4306         {
4307           // check for player created from custom element as single target
4308           content = element_info[element].change_page[i].target_element;
4309           is_player = IS_PLAYER_ELEMENT(content);
4310
4311           if (is_player && (found_rating < 3 ||
4312                             (found_rating == 3 && element < found_element)))
4313           {
4314             start_x = x;
4315             start_y = y;
4316
4317             found_rating = 3;
4318             found_element = element;
4319           }
4320         }
4321       }
4322
4323       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4324       {
4325         // check for player created from custom element as explosion content
4326         content = element_info[element].content.e[xx][yy];
4327         is_player = IS_PLAYER_ELEMENT(content);
4328
4329         if (is_player && (found_rating < 2 ||
4330                           (found_rating == 2 && element < found_element)))
4331         {
4332           start_x = x + xx - 1;
4333           start_y = y + yy - 1;
4334
4335           found_rating = 2;
4336           found_element = element;
4337         }
4338
4339         if (!CAN_CHANGE(element))
4340           continue;
4341
4342         for (i = 0; i < element_info[element].num_change_pages; i++)
4343         {
4344           // check for player created from custom element as extended target
4345           content =
4346             element_info[element].change_page[i].target_content.e[xx][yy];
4347
4348           is_player = IS_PLAYER_ELEMENT(content);
4349
4350           if (is_player && (found_rating < 1 ||
4351                             (found_rating == 1 && element < found_element)))
4352           {
4353             start_x = x + xx - 1;
4354             start_y = y + yy - 1;
4355
4356             found_rating = 1;
4357             found_element = element;
4358           }
4359         }
4360       }
4361     }
4362
4363     scroll_x = SCROLL_POSITION_X(start_x);
4364     scroll_y = SCROLL_POSITION_Y(start_y);
4365   }
4366   else
4367   {
4368     scroll_x = SCROLL_POSITION_X(local_player->jx);
4369     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4370   }
4371
4372   // !!! FIX THIS (START) !!!
4373   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4374   {
4375     InitGameEngine_EM();
4376   }
4377   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4378   {
4379     InitGameEngine_SP();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4382   {
4383     InitGameEngine_MM();
4384   }
4385   else
4386   {
4387     DrawLevel(REDRAW_FIELD);
4388     DrawAllPlayers();
4389
4390     // after drawing the level, correct some elements
4391     if (game.timegate_time_left == 0)
4392       CloseAllOpenTimegates();
4393   }
4394
4395   // blit playfield from scroll buffer to normal back buffer for fading in
4396   BlitScreenToBitmap(backbuffer);
4397   // !!! FIX THIS (END) !!!
4398
4399   DrawMaskedBorder(fade_mask);
4400
4401   FadeIn(fade_mask);
4402
4403 #if 1
4404   // full screen redraw is required at this point in the following cases:
4405   // - special editor door undrawn when game was started from level editor
4406   // - drawing area (playfield) was changed and has to be removed completely
4407   redraw_mask = REDRAW_ALL;
4408   BackToFront();
4409 #endif
4410
4411   if (!game.restart_level)
4412   {
4413     // copy default game door content to main double buffer
4414
4415     // !!! CHECK AGAIN !!!
4416     SetPanelBackground();
4417     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4418     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4419   }
4420
4421   SetPanelBackground();
4422   SetDrawBackgroundMask(REDRAW_DOOR_1);
4423
4424   UpdateAndDisplayGameControlValues();
4425
4426   if (!game.restart_level)
4427   {
4428     UnmapGameButtons();
4429     UnmapTapeButtons();
4430
4431     FreeGameButtons();
4432     CreateGameButtons();
4433
4434     MapGameButtons();
4435     MapTapeButtons();
4436
4437     // copy actual game door content to door double buffer for OpenDoor()
4438     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4439
4440     OpenDoor(DOOR_OPEN_ALL);
4441
4442     KeyboardAutoRepeatOffUnlessAutoplay();
4443
4444 #if DEBUG_INIT_PLAYER
4445     DebugPrintPlayerStatus("Player status (final)");
4446 #endif
4447   }
4448
4449   UnmapAllGadgets();
4450
4451   MapGameButtons();
4452   MapTapeButtons();
4453
4454   if (!game.restart_level && !tape.playing)
4455   {
4456     LevelStats_incPlayed(level_nr);
4457
4458     SaveLevelSetup_SeriesInfo();
4459   }
4460
4461   game.restart_level = FALSE;
4462   game.restart_game_message = NULL;
4463
4464   game.request_active = FALSE;
4465   game.request_active_or_moving = FALSE;
4466
4467   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4468     InitGameActions_MM();
4469
4470   SaveEngineSnapshotToListInitial();
4471
4472   if (!game.restart_level)
4473   {
4474     PlaySound(SND_GAME_STARTING);
4475
4476     if (setup.sound_music)
4477       PlayLevelMusic();
4478   }
4479 }
4480
4481 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4482                         int actual_player_x, int actual_player_y)
4483 {
4484   // this is used for non-R'n'D game engines to update certain engine values
4485
4486   // needed to determine if sounds are played within the visible screen area
4487   scroll_x = actual_scroll_x;
4488   scroll_y = actual_scroll_y;
4489
4490   // needed to get player position for "follow finger" playing input method
4491   local_player->jx = actual_player_x;
4492   local_player->jy = actual_player_y;
4493 }
4494
4495 void InitMovDir(int x, int y)
4496 {
4497   int i, element = Tile[x][y];
4498   static int xy[4][2] =
4499   {
4500     {  0, +1 },
4501     { +1,  0 },
4502     {  0, -1 },
4503     { -1,  0 }
4504   };
4505   static int direction[3][4] =
4506   {
4507     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4508     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4509     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4510   };
4511
4512   switch (element)
4513   {
4514     case EL_BUG_RIGHT:
4515     case EL_BUG_UP:
4516     case EL_BUG_LEFT:
4517     case EL_BUG_DOWN:
4518       Tile[x][y] = EL_BUG;
4519       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4520       break;
4521
4522     case EL_SPACESHIP_RIGHT:
4523     case EL_SPACESHIP_UP:
4524     case EL_SPACESHIP_LEFT:
4525     case EL_SPACESHIP_DOWN:
4526       Tile[x][y] = EL_SPACESHIP;
4527       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4528       break;
4529
4530     case EL_BD_BUTTERFLY_RIGHT:
4531     case EL_BD_BUTTERFLY_UP:
4532     case EL_BD_BUTTERFLY_LEFT:
4533     case EL_BD_BUTTERFLY_DOWN:
4534       Tile[x][y] = EL_BD_BUTTERFLY;
4535       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4536       break;
4537
4538     case EL_BD_FIREFLY_RIGHT:
4539     case EL_BD_FIREFLY_UP:
4540     case EL_BD_FIREFLY_LEFT:
4541     case EL_BD_FIREFLY_DOWN:
4542       Tile[x][y] = EL_BD_FIREFLY;
4543       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4544       break;
4545
4546     case EL_PACMAN_RIGHT:
4547     case EL_PACMAN_UP:
4548     case EL_PACMAN_LEFT:
4549     case EL_PACMAN_DOWN:
4550       Tile[x][y] = EL_PACMAN;
4551       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4552       break;
4553
4554     case EL_YAMYAM_LEFT:
4555     case EL_YAMYAM_RIGHT:
4556     case EL_YAMYAM_UP:
4557     case EL_YAMYAM_DOWN:
4558       Tile[x][y] = EL_YAMYAM;
4559       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4560       break;
4561
4562     case EL_SP_SNIKSNAK:
4563       MovDir[x][y] = MV_UP;
4564       break;
4565
4566     case EL_SP_ELECTRON:
4567       MovDir[x][y] = MV_LEFT;
4568       break;
4569
4570     case EL_MOLE_LEFT:
4571     case EL_MOLE_RIGHT:
4572     case EL_MOLE_UP:
4573     case EL_MOLE_DOWN:
4574       Tile[x][y] = EL_MOLE;
4575       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4576       break;
4577
4578     case EL_SPRING_LEFT:
4579     case EL_SPRING_RIGHT:
4580       Tile[x][y] = EL_SPRING;
4581       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4582       break;
4583
4584     default:
4585       if (IS_CUSTOM_ELEMENT(element))
4586       {
4587         struct ElementInfo *ei = &element_info[element];
4588         int move_direction_initial = ei->move_direction_initial;
4589         int move_pattern = ei->move_pattern;
4590
4591         if (move_direction_initial == MV_START_PREVIOUS)
4592         {
4593           if (MovDir[x][y] != MV_NONE)
4594             return;
4595
4596           move_direction_initial = MV_START_AUTOMATIC;
4597         }
4598
4599         if (move_direction_initial == MV_START_RANDOM)
4600           MovDir[x][y] = 1 << RND(4);
4601         else if (move_direction_initial & MV_ANY_DIRECTION)
4602           MovDir[x][y] = move_direction_initial;
4603         else if (move_pattern == MV_ALL_DIRECTIONS ||
4604                  move_pattern == MV_TURNING_LEFT ||
4605                  move_pattern == MV_TURNING_RIGHT ||
4606                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4607                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4608                  move_pattern == MV_TURNING_RANDOM)
4609           MovDir[x][y] = 1 << RND(4);
4610         else if (move_pattern == MV_HORIZONTAL)
4611           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4612         else if (move_pattern == MV_VERTICAL)
4613           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4614         else if (move_pattern & MV_ANY_DIRECTION)
4615           MovDir[x][y] = element_info[element].move_pattern;
4616         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4617                  move_pattern == MV_ALONG_RIGHT_SIDE)
4618         {
4619           // use random direction as default start direction
4620           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4621             MovDir[x][y] = 1 << RND(4);
4622
4623           for (i = 0; i < NUM_DIRECTIONS; i++)
4624           {
4625             int x1 = x + xy[i][0];
4626             int y1 = y + xy[i][1];
4627
4628             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4629             {
4630               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4631                 MovDir[x][y] = direction[0][i];
4632               else
4633                 MovDir[x][y] = direction[1][i];
4634
4635               break;
4636             }
4637           }
4638         }                
4639       }
4640       else
4641       {
4642         MovDir[x][y] = 1 << RND(4);
4643
4644         if (element != EL_BUG &&
4645             element != EL_SPACESHIP &&
4646             element != EL_BD_BUTTERFLY &&
4647             element != EL_BD_FIREFLY)
4648           break;
4649
4650         for (i = 0; i < NUM_DIRECTIONS; i++)
4651         {
4652           int x1 = x + xy[i][0];
4653           int y1 = y + xy[i][1];
4654
4655           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4656           {
4657             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4658             {
4659               MovDir[x][y] = direction[0][i];
4660               break;
4661             }
4662             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4663                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4664             {
4665               MovDir[x][y] = direction[1][i];
4666               break;
4667             }
4668           }
4669         }
4670       }
4671       break;
4672   }
4673
4674   GfxDir[x][y] = MovDir[x][y];
4675 }
4676
4677 void InitAmoebaNr(int x, int y)
4678 {
4679   int i;
4680   int group_nr = AmoebaNeighbourNr(x, y);
4681
4682   if (group_nr == 0)
4683   {
4684     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4685     {
4686       if (AmoebaCnt[i] == 0)
4687       {
4688         group_nr = i;
4689         break;
4690       }
4691     }
4692   }
4693
4694   AmoebaNr[x][y] = group_nr;
4695   AmoebaCnt[group_nr]++;
4696   AmoebaCnt2[group_nr]++;
4697 }
4698
4699 static void LevelSolved_SetFinalGameValues(void)
4700 {
4701   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4702   game.score_time_final = (level.use_step_counter ? TimePlayed :
4703                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4704
4705   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4706                       game_em.lev->score :
4707                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4708                       game_mm.score :
4709                       game.score);
4710
4711   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4712                        MM_HEALTH(game_mm.laser_overload_value) :
4713                        game.health);
4714
4715   game.LevelSolved_CountingTime = game.time_final;
4716   game.LevelSolved_CountingScore = game.score_final;
4717   game.LevelSolved_CountingHealth = game.health_final;
4718 }
4719
4720 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4721 {
4722   game.LevelSolved_CountingTime = time;
4723   game.LevelSolved_CountingScore = score;
4724   game.LevelSolved_CountingHealth = health;
4725
4726   game_panel_controls[GAME_PANEL_TIME].value = time;
4727   game_panel_controls[GAME_PANEL_SCORE].value = score;
4728   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4729
4730   DisplayGameControlValues();
4731 }
4732
4733 static void LevelSolved(void)
4734 {
4735   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4736       game.players_still_needed > 0)
4737     return;
4738
4739   game.LevelSolved = TRUE;
4740   game.GameOver = TRUE;
4741
4742   // needed here to display correct panel values while player walks into exit
4743   LevelSolved_SetFinalGameValues();
4744 }
4745
4746 void GameWon(void)
4747 {
4748   static int time_count_steps;
4749   static int time, time_final;
4750   static float score, score_final; // needed for time score < 10 for 10 seconds
4751   static int health, health_final;
4752   static int game_over_delay_1 = 0;
4753   static int game_over_delay_2 = 0;
4754   static int game_over_delay_3 = 0;
4755   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4756   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4757
4758   if (!game.LevelSolved_GameWon)
4759   {
4760     int i;
4761
4762     // do not start end game actions before the player stops moving (to exit)
4763     if (local_player->active && local_player->MovPos)
4764       return;
4765
4766     // calculate final game values after player finished walking into exit
4767     LevelSolved_SetFinalGameValues();
4768
4769     game.LevelSolved_GameWon = TRUE;
4770     game.LevelSolved_SaveTape = tape.recording;
4771     game.LevelSolved_SaveScore = !tape.playing;
4772
4773     if (!tape.playing)
4774     {
4775       LevelStats_incSolved(level_nr);
4776
4777       SaveLevelSetup_SeriesInfo();
4778     }
4779
4780     if (tape.auto_play)         // tape might already be stopped here
4781       tape.auto_play_level_solved = TRUE;
4782
4783     TapeStop();
4784
4785     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4786     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4787     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4788
4789     time = time_final = game.time_final;
4790     score = score_final = game.score_final;
4791     health = health_final = game.health_final;
4792
4793     // if level has time score defined, calculate new final game values
4794     if (time_score > 0)
4795     {
4796       int time_final_max = 999;
4797       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4798       int time_frames = 0;
4799       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4800       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4801
4802       if (TimeLeft > 0)
4803       {
4804         time_final = 0;
4805         time_frames = time_frames_left;
4806       }
4807       else if (game.no_time_limit && TimePlayed < time_final_max)
4808       {
4809         time_final = time_final_max;
4810         time_frames = time_frames_final_max - time_frames_played;
4811       }
4812
4813       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4814
4815       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4816
4817       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4818       {
4819         health_final = 0;
4820         score_final += health * time_score;
4821       }
4822
4823       game.score_final = score_final;
4824       game.health_final = health_final;
4825     }
4826
4827     // if not counting score after game, immediately update game panel values
4828     if (level_editor_test_game || !setup.count_score_after_game)
4829     {
4830       time = time_final;
4831       score = score_final;
4832
4833       LevelSolved_DisplayFinalGameValues(time, score, health);
4834     }
4835
4836     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4837     {
4838       // check if last player has left the level
4839       if (game.exit_x >= 0 &&
4840           game.exit_y >= 0)
4841       {
4842         int x = game.exit_x;
4843         int y = game.exit_y;
4844         int element = Tile[x][y];
4845
4846         // close exit door after last player
4847         if ((game.all_players_gone &&
4848              (element == EL_EXIT_OPEN ||
4849               element == EL_SP_EXIT_OPEN ||
4850               element == EL_STEEL_EXIT_OPEN)) ||
4851             element == EL_EM_EXIT_OPEN ||
4852             element == EL_EM_STEEL_EXIT_OPEN)
4853         {
4854
4855           Tile[x][y] =
4856             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4857              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4858              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4859              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4860              EL_EM_STEEL_EXIT_CLOSING);
4861
4862           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4863         }
4864
4865         // player disappears
4866         DrawLevelField(x, y);
4867       }
4868
4869       for (i = 0; i < MAX_PLAYERS; i++)
4870       {
4871         struct PlayerInfo *player = &stored_player[i];
4872
4873         if (player->present)
4874         {
4875           RemovePlayer(player);
4876
4877           // player disappears
4878           DrawLevelField(player->jx, player->jy);
4879         }
4880       }
4881     }
4882
4883     PlaySound(SND_GAME_WINNING);
4884   }
4885
4886   if (setup.count_score_after_game)
4887   {
4888     if (time != time_final)
4889     {
4890       if (game_over_delay_1 > 0)
4891       {
4892         game_over_delay_1--;
4893
4894         return;
4895       }
4896
4897       int time_to_go = ABS(time_final - time);
4898       int time_count_dir = (time < time_final ? +1 : -1);
4899
4900       if (time_to_go < time_count_steps)
4901         time_count_steps = 1;
4902
4903       time  += time_count_steps * time_count_dir;
4904       score += time_count_steps * time_score;
4905
4906       // set final score to correct rounding differences after counting score
4907       if (time == time_final)
4908         score = score_final;
4909
4910       LevelSolved_DisplayFinalGameValues(time, score, health);
4911
4912       if (time == time_final)
4913         StopSound(SND_GAME_LEVELTIME_BONUS);
4914       else if (setup.sound_loops)
4915         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4916       else
4917         PlaySound(SND_GAME_LEVELTIME_BONUS);
4918
4919       return;
4920     }
4921
4922     if (health != health_final)
4923     {
4924       if (game_over_delay_2 > 0)
4925       {
4926         game_over_delay_2--;
4927
4928         return;
4929       }
4930
4931       int health_count_dir = (health < health_final ? +1 : -1);
4932
4933       health += health_count_dir;
4934       score  += time_score;
4935
4936       LevelSolved_DisplayFinalGameValues(time, score, health);
4937
4938       if (health == health_final)
4939         StopSound(SND_GAME_LEVELTIME_BONUS);
4940       else if (setup.sound_loops)
4941         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4942       else
4943         PlaySound(SND_GAME_LEVELTIME_BONUS);
4944
4945       return;
4946     }
4947   }
4948
4949   game.panel.active = FALSE;
4950
4951   if (game_over_delay_3 > 0)
4952   {
4953     game_over_delay_3--;
4954
4955     return;
4956   }
4957
4958   GameEnd();
4959 }
4960
4961 void GameEnd(void)
4962 {
4963   // used instead of "level_nr" (needed for network games)
4964   int last_level_nr = levelset.level_nr;
4965
4966   game.LevelSolved_GameEnd = TRUE;
4967
4968   if (game.LevelSolved_SaveTape)
4969   {
4970     // make sure that request dialog to save tape does not open door again
4971     if (!global.use_envelope_request)
4972       CloseDoor(DOOR_CLOSE_1);
4973
4974     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4975
4976     // set unique basename for score tape (also saved in high score table)
4977     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4978   }
4979
4980   // if no tape is to be saved, close both doors simultaneously
4981   CloseDoor(DOOR_CLOSE_ALL);
4982
4983   if (level_editor_test_game)
4984   {
4985     SetGameStatus(GAME_MODE_MAIN);
4986
4987     DrawMainMenu();
4988
4989     return;
4990   }
4991
4992   if (!game.LevelSolved_SaveScore)
4993   {
4994     SetGameStatus(GAME_MODE_MAIN);
4995
4996     DrawMainMenu();
4997
4998     return;
4999   }
5000
5001   if (level_nr == leveldir_current->handicap_level)
5002   {
5003     leveldir_current->handicap_level++;
5004
5005     SaveLevelSetup_SeriesInfo();
5006   }
5007
5008   // save score and score tape before potentially erasing tape below
5009   NewHighScore(last_level_nr);
5010
5011   if (setup.increment_levels &&
5012       level_nr < leveldir_current->last_level &&
5013       !network_playing)
5014   {
5015     level_nr++;         // advance to next level
5016     TapeErase();        // start with empty tape
5017
5018     if (setup.auto_play_next_level)
5019     {
5020       LoadLevel(level_nr);
5021
5022       SaveLevelSetup_SeriesInfo();
5023     }
5024   }
5025
5026   if (scores.last_added >= 0 && setup.show_scores_after_game)
5027   {
5028     SetGameStatus(GAME_MODE_SCORES);
5029
5030     DrawHallOfFame(last_level_nr);
5031   }
5032   else if (setup.auto_play_next_level && setup.increment_levels &&
5033            last_level_nr < leveldir_current->last_level &&
5034            !network_playing)
5035   {
5036     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5037   }
5038   else
5039   {
5040     SetGameStatus(GAME_MODE_MAIN);
5041
5042     DrawMainMenu();
5043   }
5044 }
5045
5046 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5047                          boolean one_score_entry_per_name)
5048 {
5049   int i;
5050
5051   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5052     return -1;
5053
5054   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5055   {
5056     struct ScoreEntry *entry = &list->entry[i];
5057     boolean score_is_better = (new_entry->score >  entry->score);
5058     boolean score_is_equal  = (new_entry->score == entry->score);
5059     boolean time_is_better  = (new_entry->time  <  entry->time);
5060     boolean time_is_equal   = (new_entry->time  == entry->time);
5061     boolean better_by_score = (score_is_better ||
5062                                (score_is_equal && time_is_better));
5063     boolean better_by_time  = (time_is_better ||
5064                                (time_is_equal && score_is_better));
5065     boolean is_better = (level.rate_time_over_score ? better_by_time :
5066                          better_by_score);
5067     boolean entry_is_empty = (entry->score == 0 &&
5068                               entry->time == 0);
5069
5070     // prevent adding server score entries if also existing in local score file
5071     // (special case: historic score entries have an empty tape basename entry)
5072     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5073         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5074       return -1;
5075
5076     if (is_better || entry_is_empty)
5077     {
5078       // player has made it to the hall of fame
5079
5080       if (i < MAX_SCORE_ENTRIES - 1)
5081       {
5082         int m = MAX_SCORE_ENTRIES - 1;
5083         int l;
5084
5085         if (one_score_entry_per_name)
5086         {
5087           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5088             if (strEqual(list->entry[l].name, new_entry->name))
5089               m = l;
5090
5091           if (m == i)   // player's new highscore overwrites his old one
5092             goto put_into_list;
5093         }
5094
5095         for (l = m; l > i; l--)
5096           list->entry[l] = list->entry[l - 1];
5097       }
5098
5099       put_into_list:
5100
5101       *entry = *new_entry;
5102
5103       return i;
5104     }
5105     else if (one_score_entry_per_name &&
5106              strEqual(entry->name, new_entry->name))
5107     {
5108       // player already in high score list with better score or time
5109
5110       return -1;
5111     }
5112   }
5113
5114   return -1;
5115 }
5116
5117 void NewHighScore(int level_nr)
5118 {
5119   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5120   boolean one_per_name = FALSE;
5121
5122   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5123   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5124
5125   new_entry.score = game.score_final;
5126   new_entry.time = game.score_time_final;
5127
5128   LoadScore(level_nr);
5129
5130   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5131
5132   if (scores.last_added >= 0)
5133   {
5134     SaveScore(level_nr);
5135
5136     // store last added local score entry (before merging server scores)
5137     scores.last_added_local = scores.last_added;
5138   }
5139
5140   if (game.LevelSolved_SaveTape)
5141   {
5142     SaveScoreTape(level_nr);
5143     SaveServerScore(level_nr);
5144   }
5145 }
5146
5147 void MergeServerScore(void)
5148 {
5149   struct ScoreEntry last_added_entry;
5150   boolean one_per_name = FALSE;
5151   int i;
5152
5153   if (scores.last_added >= 0)
5154     last_added_entry = scores.entry[scores.last_added];
5155
5156   for (i = 0; i < server_scores.num_entries; i++)
5157   {
5158     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5159
5160     if (pos >= 0 && pos <= scores.last_added)
5161       scores.last_added++;
5162   }
5163
5164   if (scores.last_added >= MAX_SCORE_ENTRIES)
5165   {
5166     scores.last_added = MAX_SCORE_ENTRIES - 1;
5167     scores.force_last_added = TRUE;
5168
5169     scores.entry[scores.last_added] = last_added_entry;
5170   }
5171 }
5172
5173 static int getElementMoveStepsizeExt(int x, int y, int direction)
5174 {
5175   int element = Tile[x][y];
5176   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5177   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5178   int horiz_move = (dx != 0);
5179   int sign = (horiz_move ? dx : dy);
5180   int step = sign * element_info[element].move_stepsize;
5181
5182   // special values for move stepsize for spring and things on conveyor belt
5183   if (horiz_move)
5184   {
5185     if (CAN_FALL(element) &&
5186         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5187       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5188     else if (element == EL_SPRING)
5189       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5190   }
5191
5192   return step;
5193 }
5194
5195 static int getElementMoveStepsize(int x, int y)
5196 {
5197   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5198 }
5199
5200 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5201 {
5202   if (player->GfxAction != action || player->GfxDir != dir)
5203   {
5204     player->GfxAction = action;
5205     player->GfxDir = dir;
5206     player->Frame = 0;
5207     player->StepFrame = 0;
5208   }
5209 }
5210
5211 static void ResetGfxFrame(int x, int y)
5212 {
5213   // profiling showed that "autotest" spends 10~20% of its time in this function
5214   if (DrawingDeactivatedField())
5215     return;
5216
5217   int element = Tile[x][y];
5218   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5219
5220   if (graphic_info[graphic].anim_global_sync)
5221     GfxFrame[x][y] = FrameCounter;
5222   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5223     GfxFrame[x][y] = CustomValue[x][y];
5224   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5225     GfxFrame[x][y] = element_info[element].collect_score;
5226   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5227     GfxFrame[x][y] = ChangeDelay[x][y];
5228 }
5229
5230 static void ResetGfxAnimation(int x, int y)
5231 {
5232   GfxAction[x][y] = ACTION_DEFAULT;
5233   GfxDir[x][y] = MovDir[x][y];
5234   GfxFrame[x][y] = 0;
5235
5236   ResetGfxFrame(x, y);
5237 }
5238
5239 static void ResetRandomAnimationValue(int x, int y)
5240 {
5241   GfxRandom[x][y] = INIT_GFX_RANDOM();
5242 }
5243
5244 static void InitMovingField(int x, int y, int direction)
5245 {
5246   int element = Tile[x][y];
5247   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5248   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5249   int newx = x + dx;
5250   int newy = y + dy;
5251   boolean is_moving_before, is_moving_after;
5252
5253   // check if element was/is moving or being moved before/after mode change
5254   is_moving_before = (WasJustMoving[x][y] != 0);
5255   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5256
5257   // reset animation only for moving elements which change direction of moving
5258   // or which just started or stopped moving
5259   // (else CEs with property "can move" / "not moving" are reset each frame)
5260   if (is_moving_before != is_moving_after ||
5261       direction != MovDir[x][y])
5262     ResetGfxAnimation(x, y);
5263
5264   MovDir[x][y] = direction;
5265   GfxDir[x][y] = direction;
5266
5267   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5268                      direction == MV_DOWN && CAN_FALL(element) ?
5269                      ACTION_FALLING : ACTION_MOVING);
5270
5271   // this is needed for CEs with property "can move" / "not moving"
5272
5273   if (is_moving_after)
5274   {
5275     if (Tile[newx][newy] == EL_EMPTY)
5276       Tile[newx][newy] = EL_BLOCKED;
5277
5278     MovDir[newx][newy] = MovDir[x][y];
5279
5280     CustomValue[newx][newy] = CustomValue[x][y];
5281
5282     GfxFrame[newx][newy] = GfxFrame[x][y];
5283     GfxRandom[newx][newy] = GfxRandom[x][y];
5284     GfxAction[newx][newy] = GfxAction[x][y];
5285     GfxDir[newx][newy] = GfxDir[x][y];
5286   }
5287 }
5288
5289 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5290 {
5291   int direction = MovDir[x][y];
5292   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5293   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5294
5295   *goes_to_x = newx;
5296   *goes_to_y = newy;
5297 }
5298
5299 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5300 {
5301   int oldx = x, oldy = y;
5302   int direction = MovDir[x][y];
5303
5304   if (direction == MV_LEFT)
5305     oldx++;
5306   else if (direction == MV_RIGHT)
5307     oldx--;
5308   else if (direction == MV_UP)
5309     oldy++;
5310   else if (direction == MV_DOWN)
5311     oldy--;
5312
5313   *comes_from_x = oldx;
5314   *comes_from_y = oldy;
5315 }
5316
5317 static int MovingOrBlocked2Element(int x, int y)
5318 {
5319   int element = Tile[x][y];
5320
5321   if (element == EL_BLOCKED)
5322   {
5323     int oldx, oldy;
5324
5325     Blocked2Moving(x, y, &oldx, &oldy);
5326     return Tile[oldx][oldy];
5327   }
5328   else
5329     return element;
5330 }
5331
5332 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5333 {
5334   // like MovingOrBlocked2Element(), but if element is moving
5335   // and (x,y) is the field the moving element is just leaving,
5336   // return EL_BLOCKED instead of the element value
5337   int element = Tile[x][y];
5338
5339   if (IS_MOVING(x, y))
5340   {
5341     if (element == EL_BLOCKED)
5342     {
5343       int oldx, oldy;
5344
5345       Blocked2Moving(x, y, &oldx, &oldy);
5346       return Tile[oldx][oldy];
5347     }
5348     else
5349       return EL_BLOCKED;
5350   }
5351   else
5352     return element;
5353 }
5354
5355 static void RemoveField(int x, int y)
5356 {
5357   Tile[x][y] = EL_EMPTY;
5358
5359   MovPos[x][y] = 0;
5360   MovDir[x][y] = 0;
5361   MovDelay[x][y] = 0;
5362
5363   CustomValue[x][y] = 0;
5364
5365   AmoebaNr[x][y] = 0;
5366   ChangeDelay[x][y] = 0;
5367   ChangePage[x][y] = -1;
5368   Pushed[x][y] = FALSE;
5369
5370   GfxElement[x][y] = EL_UNDEFINED;
5371   GfxAction[x][y] = ACTION_DEFAULT;
5372   GfxDir[x][y] = MV_NONE;
5373 }
5374
5375 static void RemoveMovingField(int x, int y)
5376 {
5377   int oldx = x, oldy = y, newx = x, newy = y;
5378   int element = Tile[x][y];
5379   int next_element = EL_UNDEFINED;
5380
5381   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5382     return;
5383
5384   if (IS_MOVING(x, y))
5385   {
5386     Moving2Blocked(x, y, &newx, &newy);
5387
5388     if (Tile[newx][newy] != EL_BLOCKED)
5389     {
5390       // element is moving, but target field is not free (blocked), but
5391       // already occupied by something different (example: acid pool);
5392       // in this case, only remove the moving field, but not the target
5393
5394       RemoveField(oldx, oldy);
5395
5396       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5397
5398       TEST_DrawLevelField(oldx, oldy);
5399
5400       return;
5401     }
5402   }
5403   else if (element == EL_BLOCKED)
5404   {
5405     Blocked2Moving(x, y, &oldx, &oldy);
5406     if (!IS_MOVING(oldx, oldy))
5407       return;
5408   }
5409
5410   if (element == EL_BLOCKED &&
5411       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5412        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5413        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5414        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5415        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5416        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5417     next_element = get_next_element(Tile[oldx][oldy]);
5418
5419   RemoveField(oldx, oldy);
5420   RemoveField(newx, newy);
5421
5422   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5423
5424   if (next_element != EL_UNDEFINED)
5425     Tile[oldx][oldy] = next_element;
5426
5427   TEST_DrawLevelField(oldx, oldy);
5428   TEST_DrawLevelField(newx, newy);
5429 }
5430
5431 void DrawDynamite(int x, int y)
5432 {
5433   int sx = SCREENX(x), sy = SCREENY(y);
5434   int graphic = el2img(Tile[x][y]);
5435   int frame;
5436
5437   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5438     return;
5439
5440   if (IS_WALKABLE_INSIDE(Back[x][y]))
5441     return;
5442
5443   if (Back[x][y])
5444     DrawLevelElement(x, y, Back[x][y]);
5445   else if (Store[x][y])
5446     DrawLevelElement(x, y, Store[x][y]);
5447   else if (game.use_masked_elements)
5448     DrawLevelElement(x, y, EL_EMPTY);
5449
5450   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5451
5452   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5453     DrawGraphicThruMask(sx, sy, graphic, frame);
5454   else
5455     DrawGraphic(sx, sy, graphic, frame);
5456 }
5457
5458 static void CheckDynamite(int x, int y)
5459 {
5460   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5461   {
5462     MovDelay[x][y]--;
5463
5464     if (MovDelay[x][y] != 0)
5465     {
5466       DrawDynamite(x, y);
5467       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5468
5469       return;
5470     }
5471   }
5472
5473   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5474
5475   Bang(x, y);
5476 }
5477
5478 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5479 {
5480   boolean num_checked_players = 0;
5481   int i;
5482
5483   for (i = 0; i < MAX_PLAYERS; i++)
5484   {
5485     if (stored_player[i].active)
5486     {
5487       int sx = stored_player[i].jx;
5488       int sy = stored_player[i].jy;
5489
5490       if (num_checked_players == 0)
5491       {
5492         *sx1 = *sx2 = sx;
5493         *sy1 = *sy2 = sy;
5494       }
5495       else
5496       {
5497         *sx1 = MIN(*sx1, sx);
5498         *sy1 = MIN(*sy1, sy);
5499         *sx2 = MAX(*sx2, sx);
5500         *sy2 = MAX(*sy2, sy);
5501       }
5502
5503       num_checked_players++;
5504     }
5505   }
5506 }
5507
5508 static boolean checkIfAllPlayersFitToScreen_RND(void)
5509 {
5510   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5511
5512   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5513
5514   return (sx2 - sx1 < SCR_FIELDX &&
5515           sy2 - sy1 < SCR_FIELDY);
5516 }
5517
5518 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5519 {
5520   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5521
5522   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5523
5524   *sx = (sx1 + sx2) / 2;
5525   *sy = (sy1 + sy2) / 2;
5526 }
5527
5528 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5529                                boolean center_screen, boolean quick_relocation)
5530 {
5531   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5532   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5533   boolean no_delay = (tape.warp_forward);
5534   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5535   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5536   int new_scroll_x, new_scroll_y;
5537
5538   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5539   {
5540     // case 1: quick relocation inside visible screen (without scrolling)
5541
5542     RedrawPlayfield();
5543
5544     return;
5545   }
5546
5547   if (!level.shifted_relocation || center_screen)
5548   {
5549     // relocation _with_ centering of screen
5550
5551     new_scroll_x = SCROLL_POSITION_X(x);
5552     new_scroll_y = SCROLL_POSITION_Y(y);
5553   }
5554   else
5555   {
5556     // relocation _without_ centering of screen
5557
5558     int center_scroll_x = SCROLL_POSITION_X(old_x);
5559     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5560     int offset_x = x + (scroll_x - center_scroll_x);
5561     int offset_y = y + (scroll_y - center_scroll_y);
5562
5563     // for new screen position, apply previous offset to center position
5564     new_scroll_x = SCROLL_POSITION_X(offset_x);
5565     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5566   }
5567
5568   if (quick_relocation)
5569   {
5570     // case 2: quick relocation (redraw without visible scrolling)
5571
5572     scroll_x = new_scroll_x;
5573     scroll_y = new_scroll_y;
5574
5575     RedrawPlayfield();
5576
5577     return;
5578   }
5579
5580   // case 3: visible relocation (with scrolling to new position)
5581
5582   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5583
5584   SetVideoFrameDelay(wait_delay_value);
5585
5586   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5587   {
5588     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5589     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5590
5591     if (dx == 0 && dy == 0)             // no scrolling needed at all
5592       break;
5593
5594     scroll_x -= dx;
5595     scroll_y -= dy;
5596
5597     // set values for horizontal/vertical screen scrolling (half tile size)
5598     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5599     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5600     int pos_x = dx * TILEX / 2;
5601     int pos_y = dy * TILEY / 2;
5602     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5603     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5604
5605     ScrollLevel(dx, dy);
5606     DrawAllPlayers();
5607
5608     // scroll in two steps of half tile size to make things smoother
5609     BlitScreenToBitmapExt_RND(window, fx, fy);
5610
5611     // scroll second step to align at full tile size
5612     BlitScreenToBitmap(window);
5613   }
5614
5615   DrawAllPlayers();
5616   BackToFront();
5617
5618   SetVideoFrameDelay(frame_delay_value_old);
5619 }
5620
5621 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5622 {
5623   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5624   int player_nr = GET_PLAYER_NR(el_player);
5625   struct PlayerInfo *player = &stored_player[player_nr];
5626   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5627   boolean no_delay = (tape.warp_forward);
5628   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5629   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5630   int old_jx = player->jx;
5631   int old_jy = player->jy;
5632   int old_element = Tile[old_jx][old_jy];
5633   int element = Tile[jx][jy];
5634   boolean player_relocated = (old_jx != jx || old_jy != jy);
5635
5636   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5637   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5638   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5639   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5640   int leave_side_horiz = move_dir_horiz;
5641   int leave_side_vert  = move_dir_vert;
5642   int enter_side = enter_side_horiz | enter_side_vert;
5643   int leave_side = leave_side_horiz | leave_side_vert;
5644
5645   if (player->buried)           // do not reanimate dead player
5646     return;
5647
5648   if (!player_relocated)        // no need to relocate the player
5649     return;
5650
5651   if (IS_PLAYER(jx, jy))        // player already placed at new position
5652   {
5653     RemoveField(jx, jy);        // temporarily remove newly placed player
5654     DrawLevelField(jx, jy);
5655   }
5656
5657   if (player->present)
5658   {
5659     while (player->MovPos)
5660     {
5661       ScrollPlayer(player, SCROLL_GO_ON);
5662       ScrollScreen(NULL, SCROLL_GO_ON);
5663
5664       AdvanceFrameAndPlayerCounters(player->index_nr);
5665
5666       DrawPlayer(player);
5667
5668       BackToFront_WithFrameDelay(wait_delay_value);
5669     }
5670
5671     DrawPlayer(player);         // needed here only to cleanup last field
5672     DrawLevelField(player->jx, player->jy);     // remove player graphic
5673
5674     player->is_moving = FALSE;
5675   }
5676
5677   if (IS_CUSTOM_ELEMENT(old_element))
5678     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5679                                CE_LEFT_BY_PLAYER,
5680                                player->index_bit, leave_side);
5681
5682   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5683                                       CE_PLAYER_LEAVES_X,
5684                                       player->index_bit, leave_side);
5685
5686   Tile[jx][jy] = el_player;
5687   InitPlayerField(jx, jy, el_player, TRUE);
5688
5689   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5690      possible that the relocation target field did not contain a player element,
5691      but a walkable element, to which the new player was relocated -- in this
5692      case, restore that (already initialized!) element on the player field */
5693   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5694   {
5695     Tile[jx][jy] = element;     // restore previously existing element
5696   }
5697
5698   // only visually relocate centered player
5699   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5700                      FALSE, level.instant_relocation);
5701
5702   TestIfPlayerTouchesBadThing(jx, jy);
5703   TestIfPlayerTouchesCustomElement(jx, jy);
5704
5705   if (IS_CUSTOM_ELEMENT(element))
5706     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5707                                player->index_bit, enter_side);
5708
5709   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5710                                       player->index_bit, enter_side);
5711
5712   if (player->is_switching)
5713   {
5714     /* ensure that relocation while still switching an element does not cause
5715        a new element to be treated as also switched directly after relocation
5716        (this is important for teleporter switches that teleport the player to
5717        a place where another teleporter switch is in the same direction, which
5718        would then incorrectly be treated as immediately switched before the
5719        direction key that caused the switch was released) */
5720
5721     player->switch_x += jx - old_jx;
5722     player->switch_y += jy - old_jy;
5723   }
5724 }
5725
5726 static void Explode(int ex, int ey, int phase, int mode)
5727 {
5728   int x, y;
5729   int last_phase;
5730   int border_element;
5731
5732   // !!! eliminate this variable !!!
5733   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5734
5735   if (game.explosions_delayed)
5736   {
5737     ExplodeField[ex][ey] = mode;
5738     return;
5739   }
5740
5741   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5742   {
5743     int center_element = Tile[ex][ey];
5744     int artwork_element, explosion_element;     // set these values later
5745
5746     // remove things displayed in background while burning dynamite
5747     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5748       Back[ex][ey] = 0;
5749
5750     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5751     {
5752       // put moving element to center field (and let it explode there)
5753       center_element = MovingOrBlocked2Element(ex, ey);
5754       RemoveMovingField(ex, ey);
5755       Tile[ex][ey] = center_element;
5756     }
5757
5758     // now "center_element" is finally determined -- set related values now
5759     artwork_element = center_element;           // for custom player artwork
5760     explosion_element = center_element;         // for custom player artwork
5761
5762     if (IS_PLAYER(ex, ey))
5763     {
5764       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5765
5766       artwork_element = stored_player[player_nr].artwork_element;
5767
5768       if (level.use_explosion_element[player_nr])
5769       {
5770         explosion_element = level.explosion_element[player_nr];
5771         artwork_element = explosion_element;
5772       }
5773     }
5774
5775     if (mode == EX_TYPE_NORMAL ||
5776         mode == EX_TYPE_CENTER ||
5777         mode == EX_TYPE_CROSS)
5778       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5779
5780     last_phase = element_info[explosion_element].explosion_delay + 1;
5781
5782     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5783     {
5784       int xx = x - ex + 1;
5785       int yy = y - ey + 1;
5786       int element;
5787
5788       if (!IN_LEV_FIELD(x, y) ||
5789           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5790           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5791         continue;
5792
5793       element = Tile[x][y];
5794
5795       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5796       {
5797         element = MovingOrBlocked2Element(x, y);
5798
5799         if (!IS_EXPLOSION_PROOF(element))
5800           RemoveMovingField(x, y);
5801       }
5802
5803       // indestructible elements can only explode in center (but not flames)
5804       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5805                                            mode == EX_TYPE_BORDER)) ||
5806           element == EL_FLAMES)
5807         continue;
5808
5809       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5810          behaviour, for example when touching a yamyam that explodes to rocks
5811          with active deadly shield, a rock is created under the player !!! */
5812       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5813 #if 0
5814       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5815           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5816            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5817 #else
5818       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5819 #endif
5820       {
5821         if (IS_ACTIVE_BOMB(element))
5822         {
5823           // re-activate things under the bomb like gate or penguin
5824           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5825           Back[x][y] = 0;
5826         }
5827
5828         continue;
5829       }
5830
5831       // save walkable background elements while explosion on same tile
5832       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5833           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5834         Back[x][y] = element;
5835
5836       // ignite explodable elements reached by other explosion
5837       if (element == EL_EXPLOSION)
5838         element = Store2[x][y];
5839
5840       if (AmoebaNr[x][y] &&
5841           (element == EL_AMOEBA_FULL ||
5842            element == EL_BD_AMOEBA ||
5843            element == EL_AMOEBA_GROWING))
5844       {
5845         AmoebaCnt[AmoebaNr[x][y]]--;
5846         AmoebaCnt2[AmoebaNr[x][y]]--;
5847       }
5848
5849       RemoveField(x, y);
5850
5851       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5852       {
5853         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5854
5855         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5856
5857         if (PLAYERINFO(ex, ey)->use_murphy)
5858           Store[x][y] = EL_EMPTY;
5859       }
5860
5861       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5862       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5863       else if (IS_PLAYER_ELEMENT(center_element))
5864         Store[x][y] = EL_EMPTY;
5865       else if (center_element == EL_YAMYAM)
5866         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5867       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5868         Store[x][y] = element_info[center_element].content.e[xx][yy];
5869 #if 1
5870       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5871       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5872       // otherwise) -- FIX THIS !!!
5873       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5874         Store[x][y] = element_info[element].content.e[1][1];
5875 #else
5876       else if (!CAN_EXPLODE(element))
5877         Store[x][y] = element_info[element].content.e[1][1];
5878 #endif
5879       else
5880         Store[x][y] = EL_EMPTY;
5881
5882       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5883           center_element == EL_AMOEBA_TO_DIAMOND)
5884         Store2[x][y] = element;
5885
5886       Tile[x][y] = EL_EXPLOSION;
5887       GfxElement[x][y] = artwork_element;
5888
5889       ExplodePhase[x][y] = 1;
5890       ExplodeDelay[x][y] = last_phase;
5891
5892       Stop[x][y] = TRUE;
5893     }
5894
5895     if (center_element == EL_YAMYAM)
5896       game.yamyam_content_nr =
5897         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5898
5899     return;
5900   }
5901
5902   if (Stop[ex][ey])
5903     return;
5904
5905   x = ex;
5906   y = ey;
5907
5908   if (phase == 1)
5909     GfxFrame[x][y] = 0;         // restart explosion animation
5910
5911   last_phase = ExplodeDelay[x][y];
5912
5913   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5914
5915   // this can happen if the player leaves an explosion just in time
5916   if (GfxElement[x][y] == EL_UNDEFINED)
5917     GfxElement[x][y] = EL_EMPTY;
5918
5919   border_element = Store2[x][y];
5920   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5921     border_element = StorePlayer[x][y];
5922
5923   if (phase == element_info[border_element].ignition_delay ||
5924       phase == last_phase)
5925   {
5926     boolean border_explosion = FALSE;
5927
5928     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5929         !PLAYER_EXPLOSION_PROTECTED(x, y))
5930     {
5931       KillPlayerUnlessExplosionProtected(x, y);
5932       border_explosion = TRUE;
5933     }
5934     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5935     {
5936       Tile[x][y] = Store2[x][y];
5937       Store2[x][y] = 0;
5938       Bang(x, y);
5939       border_explosion = TRUE;
5940     }
5941     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5942     {
5943       AmoebaToDiamond(x, y);
5944       Store2[x][y] = 0;
5945       border_explosion = TRUE;
5946     }
5947
5948     // if an element just explodes due to another explosion (chain-reaction),
5949     // do not immediately end the new explosion when it was the last frame of
5950     // the explosion (as it would be done in the following "if"-statement!)
5951     if (border_explosion && phase == last_phase)
5952       return;
5953   }
5954
5955   if (phase == last_phase)
5956   {
5957     int element;
5958
5959     element = Tile[x][y] = Store[x][y];
5960     Store[x][y] = Store2[x][y] = 0;
5961     GfxElement[x][y] = EL_UNDEFINED;
5962
5963     // player can escape from explosions and might therefore be still alive
5964     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5965         element <= EL_PLAYER_IS_EXPLODING_4)
5966     {
5967       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5968       int explosion_element = EL_PLAYER_1 + player_nr;
5969       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5970       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5971
5972       if (level.use_explosion_element[player_nr])
5973         explosion_element = level.explosion_element[player_nr];
5974
5975       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5976                     element_info[explosion_element].content.e[xx][yy]);
5977     }
5978
5979     // restore probably existing indestructible background element
5980     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5981       element = Tile[x][y] = Back[x][y];
5982     Back[x][y] = 0;
5983
5984     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5985     GfxDir[x][y] = MV_NONE;
5986     ChangeDelay[x][y] = 0;
5987     ChangePage[x][y] = -1;
5988
5989     CustomValue[x][y] = 0;
5990
5991     InitField_WithBug2(x, y, FALSE);
5992
5993     TEST_DrawLevelField(x, y);
5994
5995     TestIfElementTouchesCustomElement(x, y);
5996
5997     if (GFX_CRUMBLED(element))
5998       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5999
6000     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6001       StorePlayer[x][y] = 0;
6002
6003     if (IS_PLAYER_ELEMENT(element))
6004       RelocatePlayer(x, y, element);
6005   }
6006   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6007   {
6008     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6009     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6010
6011     if (phase == delay)
6012       TEST_DrawLevelFieldCrumbled(x, y);
6013
6014     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6015     {
6016       DrawLevelElement(x, y, Back[x][y]);
6017       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6018     }
6019     else if (IS_WALKABLE_UNDER(Back[x][y]))
6020     {
6021       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6022       DrawLevelElementThruMask(x, y, Back[x][y]);
6023     }
6024     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6025       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6026   }
6027 }
6028
6029 static void DynaExplode(int ex, int ey)
6030 {
6031   int i, j;
6032   int dynabomb_element = Tile[ex][ey];
6033   int dynabomb_size = 1;
6034   boolean dynabomb_xl = FALSE;
6035   struct PlayerInfo *player;
6036   static int xy[4][2] =
6037   {
6038     { 0, -1 },
6039     { -1, 0 },
6040     { +1, 0 },
6041     { 0, +1 }
6042   };
6043
6044   if (IS_ACTIVE_BOMB(dynabomb_element))
6045   {
6046     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6047     dynabomb_size = player->dynabomb_size;
6048     dynabomb_xl = player->dynabomb_xl;
6049     player->dynabombs_left++;
6050   }
6051
6052   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6053
6054   for (i = 0; i < NUM_DIRECTIONS; i++)
6055   {
6056     for (j = 1; j <= dynabomb_size; j++)
6057     {
6058       int x = ex + j * xy[i][0];
6059       int y = ey + j * xy[i][1];
6060       int element;
6061
6062       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6063         break;
6064
6065       element = Tile[x][y];
6066
6067       // do not restart explosions of fields with active bombs
6068       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6069         continue;
6070
6071       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6072
6073       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6074           !IS_DIGGABLE(element) && !dynabomb_xl)
6075         break;
6076     }
6077   }
6078 }
6079
6080 void Bang(int x, int y)
6081 {
6082   int element = MovingOrBlocked2Element(x, y);
6083   int explosion_type = EX_TYPE_NORMAL;
6084
6085   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6086   {
6087     struct PlayerInfo *player = PLAYERINFO(x, y);
6088
6089     element = Tile[x][y] = player->initial_element;
6090
6091     if (level.use_explosion_element[player->index_nr])
6092     {
6093       int explosion_element = level.explosion_element[player->index_nr];
6094
6095       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6096         explosion_type = EX_TYPE_CROSS;
6097       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6098         explosion_type = EX_TYPE_CENTER;
6099     }
6100   }
6101
6102   switch (element)
6103   {
6104     case EL_BUG:
6105     case EL_SPACESHIP:
6106     case EL_BD_BUTTERFLY:
6107     case EL_BD_FIREFLY:
6108     case EL_YAMYAM:
6109     case EL_DARK_YAMYAM:
6110     case EL_ROBOT:
6111     case EL_PACMAN:
6112     case EL_MOLE:
6113       RaiseScoreElement(element);
6114       break;
6115
6116     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6117     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6118     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6119     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6120     case EL_DYNABOMB_INCREASE_NUMBER:
6121     case EL_DYNABOMB_INCREASE_SIZE:
6122     case EL_DYNABOMB_INCREASE_POWER:
6123       explosion_type = EX_TYPE_DYNA;
6124       break;
6125
6126     case EL_DC_LANDMINE:
6127       explosion_type = EX_TYPE_CENTER;
6128       break;
6129
6130     case EL_PENGUIN:
6131     case EL_LAMP:
6132     case EL_LAMP_ACTIVE:
6133     case EL_AMOEBA_TO_DIAMOND:
6134       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6135         explosion_type = EX_TYPE_CENTER;
6136       break;
6137
6138     default:
6139       if (element_info[element].explosion_type == EXPLODES_CROSS)
6140         explosion_type = EX_TYPE_CROSS;
6141       else if (element_info[element].explosion_type == EXPLODES_1X1)
6142         explosion_type = EX_TYPE_CENTER;
6143       break;
6144   }
6145
6146   if (explosion_type == EX_TYPE_DYNA)
6147     DynaExplode(x, y);
6148   else
6149     Explode(x, y, EX_PHASE_START, explosion_type);
6150
6151   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6152 }
6153
6154 static void SplashAcid(int x, int y)
6155 {
6156   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6157       (!IN_LEV_FIELD(x - 1, y - 2) ||
6158        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6159     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6160
6161   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6162       (!IN_LEV_FIELD(x + 1, y - 2) ||
6163        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6164     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6165
6166   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6167 }
6168
6169 static void InitBeltMovement(void)
6170 {
6171   static int belt_base_element[4] =
6172   {
6173     EL_CONVEYOR_BELT_1_LEFT,
6174     EL_CONVEYOR_BELT_2_LEFT,
6175     EL_CONVEYOR_BELT_3_LEFT,
6176     EL_CONVEYOR_BELT_4_LEFT
6177   };
6178   static int belt_base_active_element[4] =
6179   {
6180     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6181     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6182     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6183     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6184   };
6185
6186   int x, y, i, j;
6187
6188   // set frame order for belt animation graphic according to belt direction
6189   for (i = 0; i < NUM_BELTS; i++)
6190   {
6191     int belt_nr = i;
6192
6193     for (j = 0; j < NUM_BELT_PARTS; j++)
6194     {
6195       int element = belt_base_active_element[belt_nr] + j;
6196       int graphic_1 = el2img(element);
6197       int graphic_2 = el2panelimg(element);
6198
6199       if (game.belt_dir[i] == MV_LEFT)
6200       {
6201         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6202         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6203       }
6204       else
6205       {
6206         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6207         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6208       }
6209     }
6210   }
6211
6212   SCAN_PLAYFIELD(x, y)
6213   {
6214     int element = Tile[x][y];
6215
6216     for (i = 0; i < NUM_BELTS; i++)
6217     {
6218       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6219       {
6220         int e_belt_nr = getBeltNrFromBeltElement(element);
6221         int belt_nr = i;
6222
6223         if (e_belt_nr == belt_nr)
6224         {
6225           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6226
6227           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6228         }
6229       }
6230     }
6231   }
6232 }
6233
6234 static void ToggleBeltSwitch(int x, int y)
6235 {
6236   static int belt_base_element[4] =
6237   {
6238     EL_CONVEYOR_BELT_1_LEFT,
6239     EL_CONVEYOR_BELT_2_LEFT,
6240     EL_CONVEYOR_BELT_3_LEFT,
6241     EL_CONVEYOR_BELT_4_LEFT
6242   };
6243   static int belt_base_active_element[4] =
6244   {
6245     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6246     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6247     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6248     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6249   };
6250   static int belt_base_switch_element[4] =
6251   {
6252     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6253     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6254     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6255     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6256   };
6257   static int belt_move_dir[4] =
6258   {
6259     MV_LEFT,
6260     MV_NONE,
6261     MV_RIGHT,
6262     MV_NONE,
6263   };
6264
6265   int element = Tile[x][y];
6266   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6267   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6268   int belt_dir = belt_move_dir[belt_dir_nr];
6269   int xx, yy, i;
6270
6271   if (!IS_BELT_SWITCH(element))
6272     return;
6273
6274   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6275   game.belt_dir[belt_nr] = belt_dir;
6276
6277   if (belt_dir_nr == 3)
6278     belt_dir_nr = 1;
6279
6280   // set frame order for belt animation graphic according to belt direction
6281   for (i = 0; i < NUM_BELT_PARTS; i++)
6282   {
6283     int element = belt_base_active_element[belt_nr] + i;
6284     int graphic_1 = el2img(element);
6285     int graphic_2 = el2panelimg(element);
6286
6287     if (belt_dir == MV_LEFT)
6288     {
6289       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6290       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6291     }
6292     else
6293     {
6294       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6295       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6296     }
6297   }
6298
6299   SCAN_PLAYFIELD(xx, yy)
6300   {
6301     int element = Tile[xx][yy];
6302
6303     if (IS_BELT_SWITCH(element))
6304     {
6305       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6306
6307       if (e_belt_nr == belt_nr)
6308       {
6309         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6310         TEST_DrawLevelField(xx, yy);
6311       }
6312     }
6313     else if (IS_BELT(element) && belt_dir != MV_NONE)
6314     {
6315       int e_belt_nr = getBeltNrFromBeltElement(element);
6316
6317       if (e_belt_nr == belt_nr)
6318       {
6319         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6320
6321         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6322         TEST_DrawLevelField(xx, yy);
6323       }
6324     }
6325     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6326     {
6327       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6328
6329       if (e_belt_nr == belt_nr)
6330       {
6331         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6332
6333         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6334         TEST_DrawLevelField(xx, yy);
6335       }
6336     }
6337   }
6338 }
6339
6340 static void ToggleSwitchgateSwitch(int x, int y)
6341 {
6342   int xx, yy;
6343
6344   game.switchgate_pos = !game.switchgate_pos;
6345
6346   SCAN_PLAYFIELD(xx, yy)
6347   {
6348     int element = Tile[xx][yy];
6349
6350     if (element == EL_SWITCHGATE_SWITCH_UP)
6351     {
6352       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6353       TEST_DrawLevelField(xx, yy);
6354     }
6355     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6356     {
6357       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6358       TEST_DrawLevelField(xx, yy);
6359     }
6360     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6361     {
6362       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6363       TEST_DrawLevelField(xx, yy);
6364     }
6365     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6366     {
6367       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6368       TEST_DrawLevelField(xx, yy);
6369     }
6370     else if (element == EL_SWITCHGATE_OPEN ||
6371              element == EL_SWITCHGATE_OPENING)
6372     {
6373       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6374
6375       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6376     }
6377     else if (element == EL_SWITCHGATE_CLOSED ||
6378              element == EL_SWITCHGATE_CLOSING)
6379     {
6380       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6381
6382       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6383     }
6384   }
6385 }
6386
6387 static int getInvisibleActiveFromInvisibleElement(int element)
6388 {
6389   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6390           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6391           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6392           element);
6393 }
6394
6395 static int getInvisibleFromInvisibleActiveElement(int element)
6396 {
6397   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6398           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6399           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6400           element);
6401 }
6402
6403 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6404 {
6405   int x, y;
6406
6407   SCAN_PLAYFIELD(x, y)
6408   {
6409     int element = Tile[x][y];
6410
6411     if (element == EL_LIGHT_SWITCH &&
6412         game.light_time_left > 0)
6413     {
6414       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6415       TEST_DrawLevelField(x, y);
6416     }
6417     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6418              game.light_time_left == 0)
6419     {
6420       Tile[x][y] = EL_LIGHT_SWITCH;
6421       TEST_DrawLevelField(x, y);
6422     }
6423     else if (element == EL_EMC_DRIPPER &&
6424              game.light_time_left > 0)
6425     {
6426       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6427       TEST_DrawLevelField(x, y);
6428     }
6429     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6430              game.light_time_left == 0)
6431     {
6432       Tile[x][y] = EL_EMC_DRIPPER;
6433       TEST_DrawLevelField(x, y);
6434     }
6435     else if (element == EL_INVISIBLE_STEELWALL ||
6436              element == EL_INVISIBLE_WALL ||
6437              element == EL_INVISIBLE_SAND)
6438     {
6439       if (game.light_time_left > 0)
6440         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6441
6442       TEST_DrawLevelField(x, y);
6443
6444       // uncrumble neighbour fields, if needed
6445       if (element == EL_INVISIBLE_SAND)
6446         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6447     }
6448     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6449              element == EL_INVISIBLE_WALL_ACTIVE ||
6450              element == EL_INVISIBLE_SAND_ACTIVE)
6451     {
6452       if (game.light_time_left == 0)
6453         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6454
6455       TEST_DrawLevelField(x, y);
6456
6457       // re-crumble neighbour fields, if needed
6458       if (element == EL_INVISIBLE_SAND)
6459         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6460     }
6461   }
6462 }
6463
6464 static void RedrawAllInvisibleElementsForLenses(void)
6465 {
6466   int x, y;
6467
6468   SCAN_PLAYFIELD(x, y)
6469   {
6470     int element = Tile[x][y];
6471
6472     if (element == EL_EMC_DRIPPER &&
6473         game.lenses_time_left > 0)
6474     {
6475       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6476       TEST_DrawLevelField(x, y);
6477     }
6478     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6479              game.lenses_time_left == 0)
6480     {
6481       Tile[x][y] = EL_EMC_DRIPPER;
6482       TEST_DrawLevelField(x, y);
6483     }
6484     else if (element == EL_INVISIBLE_STEELWALL ||
6485              element == EL_INVISIBLE_WALL ||
6486              element == EL_INVISIBLE_SAND)
6487     {
6488       if (game.lenses_time_left > 0)
6489         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6490
6491       TEST_DrawLevelField(x, y);
6492
6493       // uncrumble neighbour fields, if needed
6494       if (element == EL_INVISIBLE_SAND)
6495         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6496     }
6497     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6498              element == EL_INVISIBLE_WALL_ACTIVE ||
6499              element == EL_INVISIBLE_SAND_ACTIVE)
6500     {
6501       if (game.lenses_time_left == 0)
6502         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6503
6504       TEST_DrawLevelField(x, y);
6505
6506       // re-crumble neighbour fields, if needed
6507       if (element == EL_INVISIBLE_SAND)
6508         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6509     }
6510   }
6511 }
6512
6513 static void RedrawAllInvisibleElementsForMagnifier(void)
6514 {
6515   int x, y;
6516
6517   SCAN_PLAYFIELD(x, y)
6518   {
6519     int element = Tile[x][y];
6520
6521     if (element == EL_EMC_FAKE_GRASS &&
6522         game.magnify_time_left > 0)
6523     {
6524       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6525       TEST_DrawLevelField(x, y);
6526     }
6527     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6528              game.magnify_time_left == 0)
6529     {
6530       Tile[x][y] = EL_EMC_FAKE_GRASS;
6531       TEST_DrawLevelField(x, y);
6532     }
6533     else if (IS_GATE_GRAY(element) &&
6534              game.magnify_time_left > 0)
6535     {
6536       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6537                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6538                     IS_EM_GATE_GRAY(element) ?
6539                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6540                     IS_EMC_GATE_GRAY(element) ?
6541                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6542                     IS_DC_GATE_GRAY(element) ?
6543                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6544                     element);
6545       TEST_DrawLevelField(x, y);
6546     }
6547     else if (IS_GATE_GRAY_ACTIVE(element) &&
6548              game.magnify_time_left == 0)
6549     {
6550       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6551                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6552                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6553                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6554                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6555                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6556                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6557                     EL_DC_GATE_WHITE_GRAY :
6558                     element);
6559       TEST_DrawLevelField(x, y);
6560     }
6561   }
6562 }
6563
6564 static void ToggleLightSwitch(int x, int y)
6565 {
6566   int element = Tile[x][y];
6567
6568   game.light_time_left =
6569     (element == EL_LIGHT_SWITCH ?
6570      level.time_light * FRAMES_PER_SECOND : 0);
6571
6572   RedrawAllLightSwitchesAndInvisibleElements();
6573 }
6574
6575 static void ActivateTimegateSwitch(int x, int y)
6576 {
6577   int xx, yy;
6578
6579   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6580
6581   SCAN_PLAYFIELD(xx, yy)
6582   {
6583     int element = Tile[xx][yy];
6584
6585     if (element == EL_TIMEGATE_CLOSED ||
6586         element == EL_TIMEGATE_CLOSING)
6587     {
6588       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6589       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6590     }
6591
6592     /*
6593     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6594     {
6595       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6596       TEST_DrawLevelField(xx, yy);
6597     }
6598     */
6599
6600   }
6601
6602   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6603                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6604 }
6605
6606 static void Impact(int x, int y)
6607 {
6608   boolean last_line = (y == lev_fieldy - 1);
6609   boolean object_hit = FALSE;
6610   boolean impact = (last_line || object_hit);
6611   int element = Tile[x][y];
6612   int smashed = EL_STEELWALL;
6613
6614   if (!last_line)       // check if element below was hit
6615   {
6616     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6617       return;
6618
6619     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6620                                          MovDir[x][y + 1] != MV_DOWN ||
6621                                          MovPos[x][y + 1] <= TILEY / 2));
6622
6623     // do not smash moving elements that left the smashed field in time
6624     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6625         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6626       object_hit = FALSE;
6627
6628 #if USE_QUICKSAND_IMPACT_BUGFIX
6629     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6630     {
6631       RemoveMovingField(x, y + 1);
6632       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6633       Tile[x][y + 2] = EL_ROCK;
6634       TEST_DrawLevelField(x, y + 2);
6635
6636       object_hit = TRUE;
6637     }
6638
6639     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6640     {
6641       RemoveMovingField(x, y + 1);
6642       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6643       Tile[x][y + 2] = EL_ROCK;
6644       TEST_DrawLevelField(x, y + 2);
6645
6646       object_hit = TRUE;
6647     }
6648 #endif
6649
6650     if (object_hit)
6651       smashed = MovingOrBlocked2Element(x, y + 1);
6652
6653     impact = (last_line || object_hit);
6654   }
6655
6656   if (!last_line && smashed == EL_ACID) // element falls into acid
6657   {
6658     SplashAcid(x, y + 1);
6659     return;
6660   }
6661
6662   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6663   // only reset graphic animation if graphic really changes after impact
6664   if (impact &&
6665       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6666   {
6667     ResetGfxAnimation(x, y);
6668     TEST_DrawLevelField(x, y);
6669   }
6670
6671   if (impact && CAN_EXPLODE_IMPACT(element))
6672   {
6673     Bang(x, y);
6674     return;
6675   }
6676   else if (impact && element == EL_PEARL &&
6677            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6678   {
6679     ResetGfxAnimation(x, y);
6680
6681     Tile[x][y] = EL_PEARL_BREAKING;
6682     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6683     return;
6684   }
6685   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6686   {
6687     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6688
6689     return;
6690   }
6691
6692   if (impact && element == EL_AMOEBA_DROP)
6693   {
6694     if (object_hit && IS_PLAYER(x, y + 1))
6695       KillPlayerUnlessEnemyProtected(x, y + 1);
6696     else if (object_hit && smashed == EL_PENGUIN)
6697       Bang(x, y + 1);
6698     else
6699     {
6700       Tile[x][y] = EL_AMOEBA_GROWING;
6701       Store[x][y] = EL_AMOEBA_WET;
6702
6703       ResetRandomAnimationValue(x, y);
6704     }
6705     return;
6706   }
6707
6708   if (object_hit)               // check which object was hit
6709   {
6710     if ((CAN_PASS_MAGIC_WALL(element) && 
6711          (smashed == EL_MAGIC_WALL ||
6712           smashed == EL_BD_MAGIC_WALL)) ||
6713         (CAN_PASS_DC_MAGIC_WALL(element) &&
6714          smashed == EL_DC_MAGIC_WALL))
6715     {
6716       int xx, yy;
6717       int activated_magic_wall =
6718         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6719          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6720          EL_DC_MAGIC_WALL_ACTIVE);
6721
6722       // activate magic wall / mill
6723       SCAN_PLAYFIELD(xx, yy)
6724       {
6725         if (Tile[xx][yy] == smashed)
6726           Tile[xx][yy] = activated_magic_wall;
6727       }
6728
6729       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6730       game.magic_wall_active = TRUE;
6731
6732       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6733                             SND_MAGIC_WALL_ACTIVATING :
6734                             smashed == EL_BD_MAGIC_WALL ?
6735                             SND_BD_MAGIC_WALL_ACTIVATING :
6736                             SND_DC_MAGIC_WALL_ACTIVATING));
6737     }
6738
6739     if (IS_PLAYER(x, y + 1))
6740     {
6741       if (CAN_SMASH_PLAYER(element))
6742       {
6743         KillPlayerUnlessEnemyProtected(x, y + 1);
6744         return;
6745       }
6746     }
6747     else if (smashed == EL_PENGUIN)
6748     {
6749       if (CAN_SMASH_PLAYER(element))
6750       {
6751         Bang(x, y + 1);
6752         return;
6753       }
6754     }
6755     else if (element == EL_BD_DIAMOND)
6756     {
6757       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6758       {
6759         Bang(x, y + 1);
6760         return;
6761       }
6762     }
6763     else if (((element == EL_SP_INFOTRON ||
6764                element == EL_SP_ZONK) &&
6765               (smashed == EL_SP_SNIKSNAK ||
6766                smashed == EL_SP_ELECTRON ||
6767                smashed == EL_SP_DISK_ORANGE)) ||
6768              (element == EL_SP_INFOTRON &&
6769               smashed == EL_SP_DISK_YELLOW))
6770     {
6771       Bang(x, y + 1);
6772       return;
6773     }
6774     else if (CAN_SMASH_EVERYTHING(element))
6775     {
6776       if (IS_CLASSIC_ENEMY(smashed) ||
6777           CAN_EXPLODE_SMASHED(smashed))
6778       {
6779         Bang(x, y + 1);
6780         return;
6781       }
6782       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6783       {
6784         if (smashed == EL_LAMP ||
6785             smashed == EL_LAMP_ACTIVE)
6786         {
6787           Bang(x, y + 1);
6788           return;
6789         }
6790         else if (smashed == EL_NUT)
6791         {
6792           Tile[x][y + 1] = EL_NUT_BREAKING;
6793           PlayLevelSound(x, y, SND_NUT_BREAKING);
6794           RaiseScoreElement(EL_NUT);
6795           return;
6796         }
6797         else if (smashed == EL_PEARL)
6798         {
6799           ResetGfxAnimation(x, y);
6800
6801           Tile[x][y + 1] = EL_PEARL_BREAKING;
6802           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6803           return;
6804         }
6805         else if (smashed == EL_DIAMOND)
6806         {
6807           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6808           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6809           return;
6810         }
6811         else if (IS_BELT_SWITCH(smashed))
6812         {
6813           ToggleBeltSwitch(x, y + 1);
6814         }
6815         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6816                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6817                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6818                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6819         {
6820           ToggleSwitchgateSwitch(x, y + 1);
6821         }
6822         else if (smashed == EL_LIGHT_SWITCH ||
6823                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6824         {
6825           ToggleLightSwitch(x, y + 1);
6826         }
6827         else
6828         {
6829           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6830
6831           CheckElementChangeBySide(x, y + 1, smashed, element,
6832                                    CE_SWITCHED, CH_SIDE_TOP);
6833           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6834                                             CH_SIDE_TOP);
6835         }
6836       }
6837       else
6838       {
6839         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6840       }
6841     }
6842   }
6843
6844   // play sound of magic wall / mill
6845   if (!last_line &&
6846       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6847        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6848        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6849   {
6850     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6851       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6852     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6853       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6854     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6855       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6856
6857     return;
6858   }
6859
6860   // play sound of object that hits the ground
6861   if (last_line || object_hit)
6862     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6863 }
6864
6865 static void TurnRoundExt(int x, int y)
6866 {
6867   static struct
6868   {
6869     int dx, dy;
6870   } move_xy[] =
6871   {
6872     {  0,  0 },
6873     { -1,  0 },
6874     { +1,  0 },
6875     {  0,  0 },
6876     {  0, -1 },
6877     {  0,  0 }, { 0, 0 }, { 0, 0 },
6878     {  0, +1 }
6879   };
6880   static struct
6881   {
6882     int left, right, back;
6883   } turn[] =
6884   {
6885     { 0,        0,              0        },
6886     { MV_DOWN,  MV_UP,          MV_RIGHT },
6887     { MV_UP,    MV_DOWN,        MV_LEFT  },
6888     { 0,        0,              0        },
6889     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6890     { 0,        0,              0        },
6891     { 0,        0,              0        },
6892     { 0,        0,              0        },
6893     { MV_RIGHT, MV_LEFT,        MV_UP    }
6894   };
6895
6896   int element = Tile[x][y];
6897   int move_pattern = element_info[element].move_pattern;
6898
6899   int old_move_dir = MovDir[x][y];
6900   int left_dir  = turn[old_move_dir].left;
6901   int right_dir = turn[old_move_dir].right;
6902   int back_dir  = turn[old_move_dir].back;
6903
6904   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6905   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6906   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6907   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6908
6909   int left_x  = x + left_dx,  left_y  = y + left_dy;
6910   int right_x = x + right_dx, right_y = y + right_dy;
6911   int move_x  = x + move_dx,  move_y  = y + move_dy;
6912
6913   int xx, yy;
6914
6915   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6916   {
6917     TestIfBadThingTouchesOtherBadThing(x, y);
6918
6919     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6920       MovDir[x][y] = right_dir;
6921     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6922       MovDir[x][y] = left_dir;
6923
6924     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6925       MovDelay[x][y] = 9;
6926     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6927       MovDelay[x][y] = 1;
6928   }
6929   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6930   {
6931     TestIfBadThingTouchesOtherBadThing(x, y);
6932
6933     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6934       MovDir[x][y] = left_dir;
6935     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6936       MovDir[x][y] = right_dir;
6937
6938     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6939       MovDelay[x][y] = 9;
6940     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6941       MovDelay[x][y] = 1;
6942   }
6943   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6944   {
6945     TestIfBadThingTouchesOtherBadThing(x, y);
6946
6947     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6948       MovDir[x][y] = left_dir;
6949     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6950       MovDir[x][y] = right_dir;
6951
6952     if (MovDir[x][y] != old_move_dir)
6953       MovDelay[x][y] = 9;
6954   }
6955   else if (element == EL_YAMYAM)
6956   {
6957     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6958     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, 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_DARK_YAMYAM)
6972   {
6973     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6974                                                          left_x, left_y);
6975     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6976                                                          right_x, right_y);
6977
6978     if (can_turn_left && can_turn_right)
6979       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6980     else if (can_turn_left)
6981       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6982     else if (can_turn_right)
6983       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6984     else
6985       MovDir[x][y] = back_dir;
6986
6987     MovDelay[x][y] = 16 + 16 * RND(3);
6988   }
6989   else if (element == EL_PACMAN)
6990   {
6991     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6992     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6993
6994     if (can_turn_left && can_turn_right)
6995       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6996     else if (can_turn_left)
6997       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6998     else if (can_turn_right)
6999       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7000     else
7001       MovDir[x][y] = back_dir;
7002
7003     MovDelay[x][y] = 6 + RND(40);
7004   }
7005   else if (element == EL_PIG)
7006   {
7007     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7008     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7009     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7010     boolean should_turn_left, should_turn_right, should_move_on;
7011     int rnd_value = 24;
7012     int rnd = RND(rnd_value);
7013
7014     should_turn_left = (can_turn_left &&
7015                         (!can_move_on ||
7016                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7017                                                    y + back_dy + left_dy)));
7018     should_turn_right = (can_turn_right &&
7019                          (!can_move_on ||
7020                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7021                                                     y + back_dy + right_dy)));
7022     should_move_on = (can_move_on &&
7023                       (!can_turn_left ||
7024                        !can_turn_right ||
7025                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7026                                                  y + move_dy + left_dy) ||
7027                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7028                                                  y + move_dy + right_dy)));
7029
7030     if (should_turn_left || should_turn_right || should_move_on)
7031     {
7032       if (should_turn_left && should_turn_right && should_move_on)
7033         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7034                         rnd < 2 * rnd_value / 3 ? right_dir :
7035                         old_move_dir);
7036       else if (should_turn_left && should_turn_right)
7037         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7038       else if (should_turn_left && should_move_on)
7039         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7040       else if (should_turn_right && should_move_on)
7041         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7042       else if (should_turn_left)
7043         MovDir[x][y] = left_dir;
7044       else if (should_turn_right)
7045         MovDir[x][y] = right_dir;
7046       else if (should_move_on)
7047         MovDir[x][y] = old_move_dir;
7048     }
7049     else if (can_move_on && rnd > rnd_value / 8)
7050       MovDir[x][y] = old_move_dir;
7051     else if (can_turn_left && can_turn_right)
7052       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7053     else if (can_turn_left && rnd > rnd_value / 8)
7054       MovDir[x][y] = left_dir;
7055     else if (can_turn_right && rnd > rnd_value/8)
7056       MovDir[x][y] = right_dir;
7057     else
7058       MovDir[x][y] = back_dir;
7059
7060     xx = x + move_xy[MovDir[x][y]].dx;
7061     yy = y + move_xy[MovDir[x][y]].dy;
7062
7063     if (!IN_LEV_FIELD(xx, yy) ||
7064         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7065       MovDir[x][y] = old_move_dir;
7066
7067     MovDelay[x][y] = 0;
7068   }
7069   else if (element == EL_DRAGON)
7070   {
7071     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7072     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7073     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7074     int rnd_value = 24;
7075     int rnd = RND(rnd_value);
7076
7077     if (can_move_on && rnd > rnd_value / 8)
7078       MovDir[x][y] = old_move_dir;
7079     else if (can_turn_left && can_turn_right)
7080       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7081     else if (can_turn_left && rnd > rnd_value / 8)
7082       MovDir[x][y] = left_dir;
7083     else if (can_turn_right && rnd > rnd_value / 8)
7084       MovDir[x][y] = right_dir;
7085     else
7086       MovDir[x][y] = back_dir;
7087
7088     xx = x + move_xy[MovDir[x][y]].dx;
7089     yy = y + move_xy[MovDir[x][y]].dy;
7090
7091     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7092       MovDir[x][y] = old_move_dir;
7093
7094     MovDelay[x][y] = 0;
7095   }
7096   else if (element == EL_MOLE)
7097   {
7098     boolean can_move_on =
7099       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7100                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7101                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7102     if (!can_move_on)
7103     {
7104       boolean can_turn_left =
7105         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7106                               IS_AMOEBOID(Tile[left_x][left_y])));
7107
7108       boolean can_turn_right =
7109         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7110                               IS_AMOEBOID(Tile[right_x][right_y])));
7111
7112       if (can_turn_left && can_turn_right)
7113         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7114       else if (can_turn_left)
7115         MovDir[x][y] = left_dir;
7116       else
7117         MovDir[x][y] = right_dir;
7118     }
7119
7120     if (MovDir[x][y] != old_move_dir)
7121       MovDelay[x][y] = 9;
7122   }
7123   else if (element == EL_BALLOON)
7124   {
7125     MovDir[x][y] = game.wind_direction;
7126     MovDelay[x][y] = 0;
7127   }
7128   else if (element == EL_SPRING)
7129   {
7130     if (MovDir[x][y] & MV_HORIZONTAL)
7131     {
7132       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7133           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7134       {
7135         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7136         ResetGfxAnimation(move_x, move_y);
7137         TEST_DrawLevelField(move_x, move_y);
7138
7139         MovDir[x][y] = back_dir;
7140       }
7141       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7142                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7143         MovDir[x][y] = MV_NONE;
7144     }
7145
7146     MovDelay[x][y] = 0;
7147   }
7148   else if (element == EL_ROBOT ||
7149            element == EL_SATELLITE ||
7150            element == EL_PENGUIN ||
7151            element == EL_EMC_ANDROID)
7152   {
7153     int attr_x = -1, attr_y = -1;
7154
7155     if (game.all_players_gone)
7156     {
7157       attr_x = game.exit_x;
7158       attr_y = game.exit_y;
7159     }
7160     else
7161     {
7162       int i;
7163
7164       for (i = 0; i < MAX_PLAYERS; i++)
7165       {
7166         struct PlayerInfo *player = &stored_player[i];
7167         int jx = player->jx, jy = player->jy;
7168
7169         if (!player->active)
7170           continue;
7171
7172         if (attr_x == -1 ||
7173             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7174         {
7175           attr_x = jx;
7176           attr_y = jy;
7177         }
7178       }
7179     }
7180
7181     if (element == EL_ROBOT &&
7182         game.robot_wheel_x >= 0 &&
7183         game.robot_wheel_y >= 0 &&
7184         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7185          game.engine_version < VERSION_IDENT(3,1,0,0)))
7186     {
7187       attr_x = game.robot_wheel_x;
7188       attr_y = game.robot_wheel_y;
7189     }
7190
7191     if (element == EL_PENGUIN)
7192     {
7193       int i;
7194       static int xy[4][2] =
7195       {
7196         { 0, -1 },
7197         { -1, 0 },
7198         { +1, 0 },
7199         { 0, +1 }
7200       };
7201
7202       for (i = 0; i < NUM_DIRECTIONS; i++)
7203       {
7204         int ex = x + xy[i][0];
7205         int ey = y + xy[i][1];
7206
7207         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7208                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7209                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7210                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7211         {
7212           attr_x = ex;
7213           attr_y = ey;
7214           break;
7215         }
7216       }
7217     }
7218
7219     MovDir[x][y] = MV_NONE;
7220     if (attr_x < x)
7221       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7222     else if (attr_x > x)
7223       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7224     if (attr_y < y)
7225       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7226     else if (attr_y > y)
7227       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7228
7229     if (element == EL_ROBOT)
7230     {
7231       int newx, newy;
7232
7233       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7234         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7235       Moving2Blocked(x, y, &newx, &newy);
7236
7237       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7238         MovDelay[x][y] = 8 + 8 * !RND(3);
7239       else
7240         MovDelay[x][y] = 16;
7241     }
7242     else if (element == EL_PENGUIN)
7243     {
7244       int newx, newy;
7245
7246       MovDelay[x][y] = 1;
7247
7248       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7249       {
7250         boolean first_horiz = RND(2);
7251         int new_move_dir = MovDir[x][y];
7252
7253         MovDir[x][y] =
7254           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7255         Moving2Blocked(x, y, &newx, &newy);
7256
7257         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7258           return;
7259
7260         MovDir[x][y] =
7261           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7262         Moving2Blocked(x, y, &newx, &newy);
7263
7264         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7265           return;
7266
7267         MovDir[x][y] = old_move_dir;
7268         return;
7269       }
7270     }
7271     else if (element == EL_SATELLITE)
7272     {
7273       int newx, newy;
7274
7275       MovDelay[x][y] = 1;
7276
7277       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7278       {
7279         boolean first_horiz = RND(2);
7280         int new_move_dir = MovDir[x][y];
7281
7282         MovDir[x][y] =
7283           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7284         Moving2Blocked(x, y, &newx, &newy);
7285
7286         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7287           return;
7288
7289         MovDir[x][y] =
7290           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7291         Moving2Blocked(x, y, &newx, &newy);
7292
7293         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7294           return;
7295
7296         MovDir[x][y] = old_move_dir;
7297         return;
7298       }
7299     }
7300     else if (element == EL_EMC_ANDROID)
7301     {
7302       static int check_pos[16] =
7303       {
7304         -1,             //  0 => (invalid)
7305         7,              //  1 => MV_LEFT
7306         3,              //  2 => MV_RIGHT
7307         -1,             //  3 => (invalid)
7308         1,              //  4 =>            MV_UP
7309         0,              //  5 => MV_LEFT  | MV_UP
7310         2,              //  6 => MV_RIGHT | MV_UP
7311         -1,             //  7 => (invalid)
7312         5,              //  8 =>            MV_DOWN
7313         6,              //  9 => MV_LEFT  | MV_DOWN
7314         4,              // 10 => MV_RIGHT | MV_DOWN
7315         -1,             // 11 => (invalid)
7316         -1,             // 12 => (invalid)
7317         -1,             // 13 => (invalid)
7318         -1,             // 14 => (invalid)
7319         -1,             // 15 => (invalid)
7320       };
7321       static struct
7322       {
7323         int dx, dy;
7324         int dir;
7325       } check_xy[8] =
7326       {
7327         { -1, -1,       MV_LEFT  | MV_UP   },
7328         {  0, -1,                  MV_UP   },
7329         { +1, -1,       MV_RIGHT | MV_UP   },
7330         { +1,  0,       MV_RIGHT           },
7331         { +1, +1,       MV_RIGHT | MV_DOWN },
7332         {  0, +1,                  MV_DOWN },
7333         { -1, +1,       MV_LEFT  | MV_DOWN },
7334         { -1,  0,       MV_LEFT            },
7335       };
7336       int start_pos, check_order;
7337       boolean can_clone = FALSE;
7338       int i;
7339
7340       // check if there is any free field around current position
7341       for (i = 0; i < 8; i++)
7342       {
7343         int newx = x + check_xy[i].dx;
7344         int newy = y + check_xy[i].dy;
7345
7346         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7347         {
7348           can_clone = TRUE;
7349
7350           break;
7351         }
7352       }
7353
7354       if (can_clone)            // randomly find an element to clone
7355       {
7356         can_clone = FALSE;
7357
7358         start_pos = check_pos[RND(8)];
7359         check_order = (RND(2) ? -1 : +1);
7360
7361         for (i = 0; i < 8; i++)
7362         {
7363           int pos_raw = start_pos + i * check_order;
7364           int pos = (pos_raw + 8) % 8;
7365           int newx = x + check_xy[pos].dx;
7366           int newy = y + check_xy[pos].dy;
7367
7368           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7369           {
7370             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7371             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7372
7373             Store[x][y] = Tile[newx][newy];
7374
7375             can_clone = TRUE;
7376
7377             break;
7378           }
7379         }
7380       }
7381
7382       if (can_clone)            // randomly find a direction to move
7383       {
7384         can_clone = FALSE;
7385
7386         start_pos = check_pos[RND(8)];
7387         check_order = (RND(2) ? -1 : +1);
7388
7389         for (i = 0; i < 8; i++)
7390         {
7391           int pos_raw = start_pos + i * check_order;
7392           int pos = (pos_raw + 8) % 8;
7393           int newx = x + check_xy[pos].dx;
7394           int newy = y + check_xy[pos].dy;
7395           int new_move_dir = check_xy[pos].dir;
7396
7397           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7398           {
7399             MovDir[x][y] = new_move_dir;
7400             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7401
7402             can_clone = TRUE;
7403
7404             break;
7405           }
7406         }
7407       }
7408
7409       if (can_clone)            // cloning and moving successful
7410         return;
7411
7412       // cannot clone -- try to move towards player
7413
7414       start_pos = check_pos[MovDir[x][y] & 0x0f];
7415       check_order = (RND(2) ? -1 : +1);
7416
7417       for (i = 0; i < 3; i++)
7418       {
7419         // first check start_pos, then previous/next or (next/previous) pos
7420         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7421         int pos = (pos_raw + 8) % 8;
7422         int newx = x + check_xy[pos].dx;
7423         int newy = y + check_xy[pos].dy;
7424         int new_move_dir = check_xy[pos].dir;
7425
7426         if (IS_PLAYER(newx, newy))
7427           break;
7428
7429         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7430         {
7431           MovDir[x][y] = new_move_dir;
7432           MovDelay[x][y] = level.android_move_time * 8 + 1;
7433
7434           break;
7435         }
7436       }
7437     }
7438   }
7439   else if (move_pattern == MV_TURNING_LEFT ||
7440            move_pattern == MV_TURNING_RIGHT ||
7441            move_pattern == MV_TURNING_LEFT_RIGHT ||
7442            move_pattern == MV_TURNING_RIGHT_LEFT ||
7443            move_pattern == MV_TURNING_RANDOM ||
7444            move_pattern == MV_ALL_DIRECTIONS)
7445   {
7446     boolean can_turn_left =
7447       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7448     boolean can_turn_right =
7449       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7450
7451     if (element_info[element].move_stepsize == 0)       // "not moving"
7452       return;
7453
7454     if (move_pattern == MV_TURNING_LEFT)
7455       MovDir[x][y] = left_dir;
7456     else if (move_pattern == MV_TURNING_RIGHT)
7457       MovDir[x][y] = right_dir;
7458     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7459       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7460     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7461       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7462     else if (move_pattern == MV_TURNING_RANDOM)
7463       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7464                       can_turn_right && !can_turn_left ? right_dir :
7465                       RND(2) ? left_dir : right_dir);
7466     else if (can_turn_left && can_turn_right)
7467       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7468     else if (can_turn_left)
7469       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7470     else if (can_turn_right)
7471       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7472     else
7473       MovDir[x][y] = back_dir;
7474
7475     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7476   }
7477   else if (move_pattern == MV_HORIZONTAL ||
7478            move_pattern == MV_VERTICAL)
7479   {
7480     if (move_pattern & old_move_dir)
7481       MovDir[x][y] = back_dir;
7482     else if (move_pattern == MV_HORIZONTAL)
7483       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7484     else if (move_pattern == MV_VERTICAL)
7485       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7486
7487     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7488   }
7489   else if (move_pattern & MV_ANY_DIRECTION)
7490   {
7491     MovDir[x][y] = move_pattern;
7492     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7493   }
7494   else if (move_pattern & MV_WIND_DIRECTION)
7495   {
7496     MovDir[x][y] = game.wind_direction;
7497     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7498   }
7499   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7500   {
7501     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7502       MovDir[x][y] = left_dir;
7503     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7504       MovDir[x][y] = right_dir;
7505
7506     if (MovDir[x][y] != old_move_dir)
7507       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7508   }
7509   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7510   {
7511     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7512       MovDir[x][y] = right_dir;
7513     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7514       MovDir[x][y] = left_dir;
7515
7516     if (MovDir[x][y] != old_move_dir)
7517       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7518   }
7519   else if (move_pattern == MV_TOWARDS_PLAYER ||
7520            move_pattern == MV_AWAY_FROM_PLAYER)
7521   {
7522     int attr_x = -1, attr_y = -1;
7523     int newx, newy;
7524     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7525
7526     if (game.all_players_gone)
7527     {
7528       attr_x = game.exit_x;
7529       attr_y = game.exit_y;
7530     }
7531     else
7532     {
7533       int i;
7534
7535       for (i = 0; i < MAX_PLAYERS; i++)
7536       {
7537         struct PlayerInfo *player = &stored_player[i];
7538         int jx = player->jx, jy = player->jy;
7539
7540         if (!player->active)
7541           continue;
7542
7543         if (attr_x == -1 ||
7544             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7545         {
7546           attr_x = jx;
7547           attr_y = jy;
7548         }
7549       }
7550     }
7551
7552     MovDir[x][y] = MV_NONE;
7553     if (attr_x < x)
7554       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7555     else if (attr_x > x)
7556       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7557     if (attr_y < y)
7558       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7559     else if (attr_y > y)
7560       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7561
7562     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7563
7564     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7565     {
7566       boolean first_horiz = RND(2);
7567       int new_move_dir = MovDir[x][y];
7568
7569       if (element_info[element].move_stepsize == 0)     // "not moving"
7570       {
7571         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7572         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7573
7574         return;
7575       }
7576
7577       MovDir[x][y] =
7578         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7579       Moving2Blocked(x, y, &newx, &newy);
7580
7581       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7582         return;
7583
7584       MovDir[x][y] =
7585         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7586       Moving2Blocked(x, y, &newx, &newy);
7587
7588       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7589         return;
7590
7591       MovDir[x][y] = old_move_dir;
7592     }
7593   }
7594   else if (move_pattern == MV_WHEN_PUSHED ||
7595            move_pattern == MV_WHEN_DROPPED)
7596   {
7597     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7598       MovDir[x][y] = MV_NONE;
7599
7600     MovDelay[x][y] = 0;
7601   }
7602   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7603   {
7604     static int test_xy[7][2] =
7605     {
7606       { 0, -1 },
7607       { -1, 0 },
7608       { +1, 0 },
7609       { 0, +1 },
7610       { 0, -1 },
7611       { -1, 0 },
7612       { +1, 0 },
7613     };
7614     static int test_dir[7] =
7615     {
7616       MV_UP,
7617       MV_LEFT,
7618       MV_RIGHT,
7619       MV_DOWN,
7620       MV_UP,
7621       MV_LEFT,
7622       MV_RIGHT,
7623     };
7624     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7625     int move_preference = -1000000;     // start with very low preference
7626     int new_move_dir = MV_NONE;
7627     int start_test = RND(4);
7628     int i;
7629
7630     for (i = 0; i < NUM_DIRECTIONS; i++)
7631     {
7632       int move_dir = test_dir[start_test + i];
7633       int move_dir_preference;
7634
7635       xx = x + test_xy[start_test + i][0];
7636       yy = y + test_xy[start_test + i][1];
7637
7638       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7639           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7640       {
7641         new_move_dir = move_dir;
7642
7643         break;
7644       }
7645
7646       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7647         continue;
7648
7649       move_dir_preference = -1 * RunnerVisit[xx][yy];
7650       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7651         move_dir_preference = PlayerVisit[xx][yy];
7652
7653       if (move_dir_preference > move_preference)
7654       {
7655         // prefer field that has not been visited for the longest time
7656         move_preference = move_dir_preference;
7657         new_move_dir = move_dir;
7658       }
7659       else if (move_dir_preference == move_preference &&
7660                move_dir == old_move_dir)
7661       {
7662         // prefer last direction when all directions are preferred equally
7663         move_preference = move_dir_preference;
7664         new_move_dir = move_dir;
7665       }
7666     }
7667
7668     MovDir[x][y] = new_move_dir;
7669     if (old_move_dir != new_move_dir)
7670       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7671   }
7672 }
7673
7674 static void TurnRound(int x, int y)
7675 {
7676   int direction = MovDir[x][y];
7677
7678   TurnRoundExt(x, y);
7679
7680   GfxDir[x][y] = MovDir[x][y];
7681
7682   if (direction != MovDir[x][y])
7683     GfxFrame[x][y] = 0;
7684
7685   if (MovDelay[x][y])
7686     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7687
7688   ResetGfxFrame(x, y);
7689 }
7690
7691 static boolean JustBeingPushed(int x, int y)
7692 {
7693   int i;
7694
7695   for (i = 0; i < MAX_PLAYERS; i++)
7696   {
7697     struct PlayerInfo *player = &stored_player[i];
7698
7699     if (player->active && player->is_pushing && player->MovPos)
7700     {
7701       int next_jx = player->jx + (player->jx - player->last_jx);
7702       int next_jy = player->jy + (player->jy - player->last_jy);
7703
7704       if (x == next_jx && y == next_jy)
7705         return TRUE;
7706     }
7707   }
7708
7709   return FALSE;
7710 }
7711
7712 static void StartMoving(int x, int y)
7713 {
7714   boolean started_moving = FALSE;       // some elements can fall _and_ move
7715   int element = Tile[x][y];
7716
7717   if (Stop[x][y])
7718     return;
7719
7720   if (MovDelay[x][y] == 0)
7721     GfxAction[x][y] = ACTION_DEFAULT;
7722
7723   if (CAN_FALL(element) && y < lev_fieldy - 1)
7724   {
7725     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7726         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7727       if (JustBeingPushed(x, y))
7728         return;
7729
7730     if (element == EL_QUICKSAND_FULL)
7731     {
7732       if (IS_FREE(x, y + 1))
7733       {
7734         InitMovingField(x, y, MV_DOWN);
7735         started_moving = TRUE;
7736
7737         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7738 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7739         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7740           Store[x][y] = EL_ROCK;
7741 #else
7742         Store[x][y] = EL_ROCK;
7743 #endif
7744
7745         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7746       }
7747       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7748       {
7749         if (!MovDelay[x][y])
7750         {
7751           MovDelay[x][y] = TILEY + 1;
7752
7753           ResetGfxAnimation(x, y);
7754           ResetGfxAnimation(x, y + 1);
7755         }
7756
7757         if (MovDelay[x][y])
7758         {
7759           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7760           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7761
7762           MovDelay[x][y]--;
7763           if (MovDelay[x][y])
7764             return;
7765         }
7766
7767         Tile[x][y] = EL_QUICKSAND_EMPTY;
7768         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7769         Store[x][y + 1] = Store[x][y];
7770         Store[x][y] = 0;
7771
7772         PlayLevelSoundAction(x, y, ACTION_FILLING);
7773       }
7774       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7775       {
7776         if (!MovDelay[x][y])
7777         {
7778           MovDelay[x][y] = TILEY + 1;
7779
7780           ResetGfxAnimation(x, y);
7781           ResetGfxAnimation(x, y + 1);
7782         }
7783
7784         if (MovDelay[x][y])
7785         {
7786           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7787           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7788
7789           MovDelay[x][y]--;
7790           if (MovDelay[x][y])
7791             return;
7792         }
7793
7794         Tile[x][y] = EL_QUICKSAND_EMPTY;
7795         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7796         Store[x][y + 1] = Store[x][y];
7797         Store[x][y] = 0;
7798
7799         PlayLevelSoundAction(x, y, ACTION_FILLING);
7800       }
7801     }
7802     else if (element == EL_QUICKSAND_FAST_FULL)
7803     {
7804       if (IS_FREE(x, y + 1))
7805       {
7806         InitMovingField(x, y, MV_DOWN);
7807         started_moving = TRUE;
7808
7809         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7810 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7811         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7812           Store[x][y] = EL_ROCK;
7813 #else
7814         Store[x][y] = EL_ROCK;
7815 #endif
7816
7817         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7818       }
7819       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7820       {
7821         if (!MovDelay[x][y])
7822         {
7823           MovDelay[x][y] = TILEY + 1;
7824
7825           ResetGfxAnimation(x, y);
7826           ResetGfxAnimation(x, y + 1);
7827         }
7828
7829         if (MovDelay[x][y])
7830         {
7831           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7832           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7833
7834           MovDelay[x][y]--;
7835           if (MovDelay[x][y])
7836             return;
7837         }
7838
7839         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7840         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7841         Store[x][y + 1] = Store[x][y];
7842         Store[x][y] = 0;
7843
7844         PlayLevelSoundAction(x, y, ACTION_FILLING);
7845       }
7846       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7847       {
7848         if (!MovDelay[x][y])
7849         {
7850           MovDelay[x][y] = TILEY + 1;
7851
7852           ResetGfxAnimation(x, y);
7853           ResetGfxAnimation(x, y + 1);
7854         }
7855
7856         if (MovDelay[x][y])
7857         {
7858           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7859           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7860
7861           MovDelay[x][y]--;
7862           if (MovDelay[x][y])
7863             return;
7864         }
7865
7866         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7867         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7868         Store[x][y + 1] = Store[x][y];
7869         Store[x][y] = 0;
7870
7871         PlayLevelSoundAction(x, y, ACTION_FILLING);
7872       }
7873     }
7874     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7875              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7876     {
7877       InitMovingField(x, y, MV_DOWN);
7878       started_moving = TRUE;
7879
7880       Tile[x][y] = EL_QUICKSAND_FILLING;
7881       Store[x][y] = element;
7882
7883       PlayLevelSoundAction(x, y, ACTION_FILLING);
7884     }
7885     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7886              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887     {
7888       InitMovingField(x, y, MV_DOWN);
7889       started_moving = TRUE;
7890
7891       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7892       Store[x][y] = element;
7893
7894       PlayLevelSoundAction(x, y, ACTION_FILLING);
7895     }
7896     else if (element == EL_MAGIC_WALL_FULL)
7897     {
7898       if (IS_FREE(x, y + 1))
7899       {
7900         InitMovingField(x, y, MV_DOWN);
7901         started_moving = TRUE;
7902
7903         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7904         Store[x][y] = EL_CHANGED(Store[x][y]);
7905       }
7906       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7907       {
7908         if (!MovDelay[x][y])
7909           MovDelay[x][y] = TILEY / 4 + 1;
7910
7911         if (MovDelay[x][y])
7912         {
7913           MovDelay[x][y]--;
7914           if (MovDelay[x][y])
7915             return;
7916         }
7917
7918         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7919         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7920         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7921         Store[x][y] = 0;
7922       }
7923     }
7924     else if (element == EL_BD_MAGIC_WALL_FULL)
7925     {
7926       if (IS_FREE(x, y + 1))
7927       {
7928         InitMovingField(x, y, MV_DOWN);
7929         started_moving = TRUE;
7930
7931         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7932         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7933       }
7934       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7935       {
7936         if (!MovDelay[x][y])
7937           MovDelay[x][y] = TILEY / 4 + 1;
7938
7939         if (MovDelay[x][y])
7940         {
7941           MovDelay[x][y]--;
7942           if (MovDelay[x][y])
7943             return;
7944         }
7945
7946         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7947         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7948         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7949         Store[x][y] = 0;
7950       }
7951     }
7952     else if (element == EL_DC_MAGIC_WALL_FULL)
7953     {
7954       if (IS_FREE(x, y + 1))
7955       {
7956         InitMovingField(x, y, MV_DOWN);
7957         started_moving = TRUE;
7958
7959         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7960         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7961       }
7962       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7963       {
7964         if (!MovDelay[x][y])
7965           MovDelay[x][y] = TILEY / 4 + 1;
7966
7967         if (MovDelay[x][y])
7968         {
7969           MovDelay[x][y]--;
7970           if (MovDelay[x][y])
7971             return;
7972         }
7973
7974         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7975         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7976         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7977         Store[x][y] = 0;
7978       }
7979     }
7980     else if ((CAN_PASS_MAGIC_WALL(element) &&
7981               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7982                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7983              (CAN_PASS_DC_MAGIC_WALL(element) &&
7984               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7985
7986     {
7987       InitMovingField(x, y, MV_DOWN);
7988       started_moving = TRUE;
7989
7990       Tile[x][y] =
7991         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7992          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7993          EL_DC_MAGIC_WALL_FILLING);
7994       Store[x][y] = element;
7995     }
7996     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7997     {
7998       SplashAcid(x, y + 1);
7999
8000       InitMovingField(x, y, MV_DOWN);
8001       started_moving = TRUE;
8002
8003       Store[x][y] = EL_ACID;
8004     }
8005     else if (
8006              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8007               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8008              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8009               CAN_FALL(element) && WasJustFalling[x][y] &&
8010               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8011
8012              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8013               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8014               (Tile[x][y + 1] == EL_BLOCKED)))
8015     {
8016       /* this is needed for a special case not covered by calling "Impact()"
8017          from "ContinueMoving()": if an element moves to a tile directly below
8018          another element which was just falling on that tile (which was empty
8019          in the previous frame), the falling element above would just stop
8020          instead of smashing the element below (in previous version, the above
8021          element was just checked for "moving" instead of "falling", resulting
8022          in incorrect smashes caused by horizontal movement of the above
8023          element; also, the case of the player being the element to smash was
8024          simply not covered here... :-/ ) */
8025
8026       CheckCollision[x][y] = 0;
8027       CheckImpact[x][y] = 0;
8028
8029       Impact(x, y);
8030     }
8031     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8032     {
8033       if (MovDir[x][y] == MV_NONE)
8034       {
8035         InitMovingField(x, y, MV_DOWN);
8036         started_moving = TRUE;
8037       }
8038     }
8039     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8040     {
8041       if (WasJustFalling[x][y]) // prevent animation from being restarted
8042         MovDir[x][y] = MV_DOWN;
8043
8044       InitMovingField(x, y, MV_DOWN);
8045       started_moving = TRUE;
8046     }
8047     else if (element == EL_AMOEBA_DROP)
8048     {
8049       Tile[x][y] = EL_AMOEBA_GROWING;
8050       Store[x][y] = EL_AMOEBA_WET;
8051     }
8052     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8053               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8054              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8055              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8056     {
8057       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8058                                 (IS_FREE(x - 1, y + 1) ||
8059                                  Tile[x - 1][y + 1] == EL_ACID));
8060       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8061                                 (IS_FREE(x + 1, y + 1) ||
8062                                  Tile[x + 1][y + 1] == EL_ACID));
8063       boolean can_fall_any  = (can_fall_left || can_fall_right);
8064       boolean can_fall_both = (can_fall_left && can_fall_right);
8065       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8066
8067       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8068       {
8069         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8070           can_fall_right = FALSE;
8071         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8072           can_fall_left = FALSE;
8073         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8074           can_fall_right = FALSE;
8075         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8076           can_fall_left = FALSE;
8077
8078         can_fall_any  = (can_fall_left || can_fall_right);
8079         can_fall_both = FALSE;
8080       }
8081
8082       if (can_fall_both)
8083       {
8084         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8085           can_fall_right = FALSE;       // slip down on left side
8086         else
8087           can_fall_left = !(can_fall_right = RND(2));
8088
8089         can_fall_both = FALSE;
8090       }
8091
8092       if (can_fall_any)
8093       {
8094         // if not determined otherwise, prefer left side for slipping down
8095         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8096         started_moving = TRUE;
8097       }
8098     }
8099     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8100     {
8101       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8102       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8103       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8104       int belt_dir = game.belt_dir[belt_nr];
8105
8106       if ((belt_dir == MV_LEFT  && left_is_free) ||
8107           (belt_dir == MV_RIGHT && right_is_free))
8108       {
8109         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8110
8111         InitMovingField(x, y, belt_dir);
8112         started_moving = TRUE;
8113
8114         Pushed[x][y] = TRUE;
8115         Pushed[nextx][y] = TRUE;
8116
8117         GfxAction[x][y] = ACTION_DEFAULT;
8118       }
8119       else
8120       {
8121         MovDir[x][y] = 0;       // if element was moving, stop it
8122       }
8123     }
8124   }
8125
8126   // not "else if" because of elements that can fall and move (EL_SPRING)
8127   if (CAN_MOVE(element) && !started_moving)
8128   {
8129     int move_pattern = element_info[element].move_pattern;
8130     int newx, newy;
8131
8132     Moving2Blocked(x, y, &newx, &newy);
8133
8134     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8135       return;
8136
8137     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8138         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8139     {
8140       WasJustMoving[x][y] = 0;
8141       CheckCollision[x][y] = 0;
8142
8143       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8144
8145       if (Tile[x][y] != element)        // element has changed
8146         return;
8147     }
8148
8149     if (!MovDelay[x][y])        // start new movement phase
8150     {
8151       // all objects that can change their move direction after each step
8152       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8153
8154       if (element != EL_YAMYAM &&
8155           element != EL_DARK_YAMYAM &&
8156           element != EL_PACMAN &&
8157           !(move_pattern & MV_ANY_DIRECTION) &&
8158           move_pattern != MV_TURNING_LEFT &&
8159           move_pattern != MV_TURNING_RIGHT &&
8160           move_pattern != MV_TURNING_LEFT_RIGHT &&
8161           move_pattern != MV_TURNING_RIGHT_LEFT &&
8162           move_pattern != MV_TURNING_RANDOM)
8163       {
8164         TurnRound(x, y);
8165
8166         if (MovDelay[x][y] && (element == EL_BUG ||
8167                                element == EL_SPACESHIP ||
8168                                element == EL_SP_SNIKSNAK ||
8169                                element == EL_SP_ELECTRON ||
8170                                element == EL_MOLE))
8171           TEST_DrawLevelField(x, y);
8172       }
8173     }
8174
8175     if (MovDelay[x][y])         // wait some time before next movement
8176     {
8177       MovDelay[x][y]--;
8178
8179       if (element == EL_ROBOT ||
8180           element == EL_YAMYAM ||
8181           element == EL_DARK_YAMYAM)
8182       {
8183         DrawLevelElementAnimationIfNeeded(x, y, element);
8184         PlayLevelSoundAction(x, y, ACTION_WAITING);
8185       }
8186       else if (element == EL_SP_ELECTRON)
8187         DrawLevelElementAnimationIfNeeded(x, y, element);
8188       else if (element == EL_DRAGON)
8189       {
8190         int i;
8191         int dir = MovDir[x][y];
8192         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8193         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8194         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8195                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8196                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8197                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8198         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8199
8200         GfxAction[x][y] = ACTION_ATTACKING;
8201
8202         if (IS_PLAYER(x, y))
8203           DrawPlayerField(x, y);
8204         else
8205           TEST_DrawLevelField(x, y);
8206
8207         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8208
8209         for (i = 1; i <= 3; i++)
8210         {
8211           int xx = x + i * dx;
8212           int yy = y + i * dy;
8213           int sx = SCREENX(xx);
8214           int sy = SCREENY(yy);
8215           int flame_graphic = graphic + (i - 1);
8216
8217           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8218             break;
8219
8220           if (MovDelay[x][y])
8221           {
8222             int flamed = MovingOrBlocked2Element(xx, yy);
8223
8224             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8225               Bang(xx, yy);
8226             else
8227               RemoveMovingField(xx, yy);
8228
8229             ChangeDelay[xx][yy] = 0;
8230
8231             Tile[xx][yy] = EL_FLAMES;
8232
8233             if (IN_SCR_FIELD(sx, sy))
8234             {
8235               TEST_DrawLevelFieldCrumbled(xx, yy);
8236               DrawGraphic(sx, sy, flame_graphic, frame);
8237             }
8238           }
8239           else
8240           {
8241             if (Tile[xx][yy] == EL_FLAMES)
8242               Tile[xx][yy] = EL_EMPTY;
8243             TEST_DrawLevelField(xx, yy);
8244           }
8245         }
8246       }
8247
8248       if (MovDelay[x][y])       // element still has to wait some time
8249       {
8250         PlayLevelSoundAction(x, y, ACTION_WAITING);
8251
8252         return;
8253       }
8254     }
8255
8256     // now make next step
8257
8258     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8259
8260     if (DONT_COLLIDE_WITH(element) &&
8261         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8262         !PLAYER_ENEMY_PROTECTED(newx, newy))
8263     {
8264       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8265
8266       return;
8267     }
8268
8269     else if (CAN_MOVE_INTO_ACID(element) &&
8270              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8271              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8272              (MovDir[x][y] == MV_DOWN ||
8273               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8274     {
8275       SplashAcid(newx, newy);
8276       Store[x][y] = EL_ACID;
8277     }
8278     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8279     {
8280       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8281           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8282           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8283           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8284       {
8285         RemoveField(x, y);
8286         TEST_DrawLevelField(x, y);
8287
8288         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8289         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8290           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8291
8292         game.friends_still_needed--;
8293         if (!game.friends_still_needed &&
8294             !game.GameOver &&
8295             game.all_players_gone)
8296           LevelSolved();
8297
8298         return;
8299       }
8300       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8301       {
8302         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8303           TEST_DrawLevelField(newx, newy);
8304         else
8305           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8306       }
8307       else if (!IS_FREE(newx, newy))
8308       {
8309         GfxAction[x][y] = ACTION_WAITING;
8310
8311         if (IS_PLAYER(x, y))
8312           DrawPlayerField(x, y);
8313         else
8314           TEST_DrawLevelField(x, y);
8315
8316         return;
8317       }
8318     }
8319     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8320     {
8321       if (IS_FOOD_PIG(Tile[newx][newy]))
8322       {
8323         if (IS_MOVING(newx, newy))
8324           RemoveMovingField(newx, newy);
8325         else
8326         {
8327           Tile[newx][newy] = EL_EMPTY;
8328           TEST_DrawLevelField(newx, newy);
8329         }
8330
8331         PlayLevelSound(x, y, SND_PIG_DIGGING);
8332       }
8333       else if (!IS_FREE(newx, newy))
8334       {
8335         if (IS_PLAYER(x, y))
8336           DrawPlayerField(x, y);
8337         else
8338           TEST_DrawLevelField(x, y);
8339
8340         return;
8341       }
8342     }
8343     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8344     {
8345       if (Store[x][y] != EL_EMPTY)
8346       {
8347         boolean can_clone = FALSE;
8348         int xx, yy;
8349
8350         // check if element to clone is still there
8351         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8352         {
8353           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8354           {
8355             can_clone = TRUE;
8356
8357             break;
8358           }
8359         }
8360
8361         // cannot clone or target field not free anymore -- do not clone
8362         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8363           Store[x][y] = EL_EMPTY;
8364       }
8365
8366       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8367       {
8368         if (IS_MV_DIAGONAL(MovDir[x][y]))
8369         {
8370           int diagonal_move_dir = MovDir[x][y];
8371           int stored = Store[x][y];
8372           int change_delay = 8;
8373           int graphic;
8374
8375           // android is moving diagonally
8376
8377           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8378
8379           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8380           GfxElement[x][y] = EL_EMC_ANDROID;
8381           GfxAction[x][y] = ACTION_SHRINKING;
8382           GfxDir[x][y] = diagonal_move_dir;
8383           ChangeDelay[x][y] = change_delay;
8384
8385           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8386                                    GfxDir[x][y]);
8387
8388           DrawLevelGraphicAnimation(x, y, graphic);
8389           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8390
8391           if (Tile[newx][newy] == EL_ACID)
8392           {
8393             SplashAcid(newx, newy);
8394
8395             return;
8396           }
8397
8398           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8399
8400           Store[newx][newy] = EL_EMC_ANDROID;
8401           GfxElement[newx][newy] = EL_EMC_ANDROID;
8402           GfxAction[newx][newy] = ACTION_GROWING;
8403           GfxDir[newx][newy] = diagonal_move_dir;
8404           ChangeDelay[newx][newy] = change_delay;
8405
8406           graphic = el_act_dir2img(GfxElement[newx][newy],
8407                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8408
8409           DrawLevelGraphicAnimation(newx, newy, graphic);
8410           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8411
8412           return;
8413         }
8414         else
8415         {
8416           Tile[newx][newy] = EL_EMPTY;
8417           TEST_DrawLevelField(newx, newy);
8418
8419           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8420         }
8421       }
8422       else if (!IS_FREE(newx, newy))
8423       {
8424         return;
8425       }
8426     }
8427     else if (IS_CUSTOM_ELEMENT(element) &&
8428              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8429     {
8430       if (!DigFieldByCE(newx, newy, element))
8431         return;
8432
8433       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8434       {
8435         RunnerVisit[x][y] = FrameCounter;
8436         PlayerVisit[x][y] /= 8;         // expire player visit path
8437       }
8438     }
8439     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8440     {
8441       if (!IS_FREE(newx, newy))
8442       {
8443         if (IS_PLAYER(x, y))
8444           DrawPlayerField(x, y);
8445         else
8446           TEST_DrawLevelField(x, y);
8447
8448         return;
8449       }
8450       else
8451       {
8452         boolean wanna_flame = !RND(10);
8453         int dx = newx - x, dy = newy - y;
8454         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8455         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8456         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8457                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8458         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8459                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8460
8461         if ((wanna_flame ||
8462              IS_CLASSIC_ENEMY(element1) ||
8463              IS_CLASSIC_ENEMY(element2)) &&
8464             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8465             element1 != EL_FLAMES && element2 != EL_FLAMES)
8466         {
8467           ResetGfxAnimation(x, y);
8468           GfxAction[x][y] = ACTION_ATTACKING;
8469
8470           if (IS_PLAYER(x, y))
8471             DrawPlayerField(x, y);
8472           else
8473             TEST_DrawLevelField(x, y);
8474
8475           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8476
8477           MovDelay[x][y] = 50;
8478
8479           Tile[newx][newy] = EL_FLAMES;
8480           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8481             Tile[newx1][newy1] = EL_FLAMES;
8482           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8483             Tile[newx2][newy2] = EL_FLAMES;
8484
8485           return;
8486         }
8487       }
8488     }
8489     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8490              Tile[newx][newy] == EL_DIAMOND)
8491     {
8492       if (IS_MOVING(newx, newy))
8493         RemoveMovingField(newx, newy);
8494       else
8495       {
8496         Tile[newx][newy] = EL_EMPTY;
8497         TEST_DrawLevelField(newx, newy);
8498       }
8499
8500       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8501     }
8502     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8503              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8504     {
8505       if (AmoebaNr[newx][newy])
8506       {
8507         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8508         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8509             Tile[newx][newy] == EL_BD_AMOEBA)
8510           AmoebaCnt[AmoebaNr[newx][newy]]--;
8511       }
8512
8513       if (IS_MOVING(newx, newy))
8514       {
8515         RemoveMovingField(newx, newy);
8516       }
8517       else
8518       {
8519         Tile[newx][newy] = EL_EMPTY;
8520         TEST_DrawLevelField(newx, newy);
8521       }
8522
8523       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8524     }
8525     else if ((element == EL_PACMAN || element == EL_MOLE)
8526              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8527     {
8528       if (AmoebaNr[newx][newy])
8529       {
8530         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8531         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8532             Tile[newx][newy] == EL_BD_AMOEBA)
8533           AmoebaCnt[AmoebaNr[newx][newy]]--;
8534       }
8535
8536       if (element == EL_MOLE)
8537       {
8538         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8539         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8540
8541         ResetGfxAnimation(x, y);
8542         GfxAction[x][y] = ACTION_DIGGING;
8543         TEST_DrawLevelField(x, y);
8544
8545         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8546
8547         return;                         // wait for shrinking amoeba
8548       }
8549       else      // element == EL_PACMAN
8550       {
8551         Tile[newx][newy] = EL_EMPTY;
8552         TEST_DrawLevelField(newx, newy);
8553         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8554       }
8555     }
8556     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8557              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8558               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8559     {
8560       // wait for shrinking amoeba to completely disappear
8561       return;
8562     }
8563     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8564     {
8565       // object was running against a wall
8566
8567       TurnRound(x, y);
8568
8569       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8570         DrawLevelElementAnimation(x, y, element);
8571
8572       if (DONT_TOUCH(element))
8573         TestIfBadThingTouchesPlayer(x, y);
8574
8575       return;
8576     }
8577
8578     InitMovingField(x, y, MovDir[x][y]);
8579
8580     PlayLevelSoundAction(x, y, ACTION_MOVING);
8581   }
8582
8583   if (MovDir[x][y])
8584     ContinueMoving(x, y);
8585 }
8586
8587 void ContinueMoving(int x, int y)
8588 {
8589   int element = Tile[x][y];
8590   struct ElementInfo *ei = &element_info[element];
8591   int direction = MovDir[x][y];
8592   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8593   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8594   int newx = x + dx, newy = y + dy;
8595   int stored = Store[x][y];
8596   int stored_new = Store[newx][newy];
8597   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8598   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8599   boolean last_line = (newy == lev_fieldy - 1);
8600   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8601
8602   if (pushed_by_player)         // special case: moving object pushed by player
8603   {
8604     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8605   }
8606   else if (use_step_delay)      // special case: moving object has step delay
8607   {
8608     if (!MovDelay[x][y])
8609       MovPos[x][y] += getElementMoveStepsize(x, y);
8610
8611     if (MovDelay[x][y])
8612       MovDelay[x][y]--;
8613     else
8614       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8615
8616     if (MovDelay[x][y])
8617     {
8618       TEST_DrawLevelField(x, y);
8619
8620       return;   // element is still waiting
8621     }
8622   }
8623   else                          // normal case: generically moving object
8624   {
8625     MovPos[x][y] += getElementMoveStepsize(x, y);
8626   }
8627
8628   if (ABS(MovPos[x][y]) < TILEX)
8629   {
8630     TEST_DrawLevelField(x, y);
8631
8632     return;     // element is still moving
8633   }
8634
8635   // element reached destination field
8636
8637   Tile[x][y] = EL_EMPTY;
8638   Tile[newx][newy] = element;
8639   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8640
8641   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8642   {
8643     element = Tile[newx][newy] = EL_ACID;
8644   }
8645   else if (element == EL_MOLE)
8646   {
8647     Tile[x][y] = EL_SAND;
8648
8649     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8650   }
8651   else if (element == EL_QUICKSAND_FILLING)
8652   {
8653     element = Tile[newx][newy] = get_next_element(element);
8654     Store[newx][newy] = Store[x][y];
8655   }
8656   else if (element == EL_QUICKSAND_EMPTYING)
8657   {
8658     Tile[x][y] = get_next_element(element);
8659     element = Tile[newx][newy] = Store[x][y];
8660   }
8661   else if (element == EL_QUICKSAND_FAST_FILLING)
8662   {
8663     element = Tile[newx][newy] = get_next_element(element);
8664     Store[newx][newy] = Store[x][y];
8665   }
8666   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8667   {
8668     Tile[x][y] = get_next_element(element);
8669     element = Tile[newx][newy] = Store[x][y];
8670   }
8671   else if (element == EL_MAGIC_WALL_FILLING)
8672   {
8673     element = Tile[newx][newy] = get_next_element(element);
8674     if (!game.magic_wall_active)
8675       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8676     Store[newx][newy] = Store[x][y];
8677   }
8678   else if (element == EL_MAGIC_WALL_EMPTYING)
8679   {
8680     Tile[x][y] = get_next_element(element);
8681     if (!game.magic_wall_active)
8682       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8683     element = Tile[newx][newy] = Store[x][y];
8684
8685     InitField(newx, newy, FALSE);
8686   }
8687   else if (element == EL_BD_MAGIC_WALL_FILLING)
8688   {
8689     element = Tile[newx][newy] = get_next_element(element);
8690     if (!game.magic_wall_active)
8691       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8692     Store[newx][newy] = Store[x][y];
8693   }
8694   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8695   {
8696     Tile[x][y] = get_next_element(element);
8697     if (!game.magic_wall_active)
8698       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8699     element = Tile[newx][newy] = Store[x][y];
8700
8701     InitField(newx, newy, FALSE);
8702   }
8703   else if (element == EL_DC_MAGIC_WALL_FILLING)
8704   {
8705     element = Tile[newx][newy] = get_next_element(element);
8706     if (!game.magic_wall_active)
8707       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8708     Store[newx][newy] = Store[x][y];
8709   }
8710   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8711   {
8712     Tile[x][y] = get_next_element(element);
8713     if (!game.magic_wall_active)
8714       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8715     element = Tile[newx][newy] = Store[x][y];
8716
8717     InitField(newx, newy, FALSE);
8718   }
8719   else if (element == EL_AMOEBA_DROPPING)
8720   {
8721     Tile[x][y] = get_next_element(element);
8722     element = Tile[newx][newy] = Store[x][y];
8723   }
8724   else if (element == EL_SOKOBAN_OBJECT)
8725   {
8726     if (Back[x][y])
8727       Tile[x][y] = Back[x][y];
8728
8729     if (Back[newx][newy])
8730       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8731
8732     Back[x][y] = Back[newx][newy] = 0;
8733   }
8734
8735   Store[x][y] = EL_EMPTY;
8736   MovPos[x][y] = 0;
8737   MovDir[x][y] = 0;
8738   MovDelay[x][y] = 0;
8739
8740   MovDelay[newx][newy] = 0;
8741
8742   if (CAN_CHANGE_OR_HAS_ACTION(element))
8743   {
8744     // copy element change control values to new field
8745     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8746     ChangePage[newx][newy]  = ChangePage[x][y];
8747     ChangeCount[newx][newy] = ChangeCount[x][y];
8748     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8749   }
8750
8751   CustomValue[newx][newy] = CustomValue[x][y];
8752
8753   ChangeDelay[x][y] = 0;
8754   ChangePage[x][y] = -1;
8755   ChangeCount[x][y] = 0;
8756   ChangeEvent[x][y] = -1;
8757
8758   CustomValue[x][y] = 0;
8759
8760   // copy animation control values to new field
8761   GfxFrame[newx][newy]  = GfxFrame[x][y];
8762   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8763   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8764   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8765
8766   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8767
8768   // some elements can leave other elements behind after moving
8769   if (ei->move_leave_element != EL_EMPTY &&
8770       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8771       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8772   {
8773     int move_leave_element = ei->move_leave_element;
8774
8775     // this makes it possible to leave the removed element again
8776     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8777       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8778
8779     Tile[x][y] = move_leave_element;
8780
8781     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8782       MovDir[x][y] = direction;
8783
8784     InitField(x, y, FALSE);
8785
8786     if (GFX_CRUMBLED(Tile[x][y]))
8787       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8788
8789     if (IS_PLAYER_ELEMENT(move_leave_element))
8790       RelocatePlayer(x, y, move_leave_element);
8791   }
8792
8793   // do this after checking for left-behind element
8794   ResetGfxAnimation(x, y);      // reset animation values for old field
8795
8796   if (!CAN_MOVE(element) ||
8797       (CAN_FALL(element) && direction == MV_DOWN &&
8798        (element == EL_SPRING ||
8799         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8800         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8801     GfxDir[x][y] = MovDir[newx][newy] = 0;
8802
8803   TEST_DrawLevelField(x, y);
8804   TEST_DrawLevelField(newx, newy);
8805
8806   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8807
8808   // prevent pushed element from moving on in pushed direction
8809   if (pushed_by_player && CAN_MOVE(element) &&
8810       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8811       !(element_info[element].move_pattern & direction))
8812     TurnRound(newx, newy);
8813
8814   // prevent elements on conveyor belt from moving on in last direction
8815   if (pushed_by_conveyor && CAN_FALL(element) &&
8816       direction & MV_HORIZONTAL)
8817     MovDir[newx][newy] = 0;
8818
8819   if (!pushed_by_player)
8820   {
8821     int nextx = newx + dx, nexty = newy + dy;
8822     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8823
8824     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8825
8826     if (CAN_FALL(element) && direction == MV_DOWN)
8827       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8828
8829     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8830       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8831
8832     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8833       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8834   }
8835
8836   if (DONT_TOUCH(element))      // object may be nasty to player or others
8837   {
8838     TestIfBadThingTouchesPlayer(newx, newy);
8839     TestIfBadThingTouchesFriend(newx, newy);
8840
8841     if (!IS_CUSTOM_ELEMENT(element))
8842       TestIfBadThingTouchesOtherBadThing(newx, newy);
8843   }
8844   else if (element == EL_PENGUIN)
8845     TestIfFriendTouchesBadThing(newx, newy);
8846
8847   if (DONT_GET_HIT_BY(element))
8848   {
8849     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8850   }
8851
8852   // give the player one last chance (one more frame) to move away
8853   if (CAN_FALL(element) && direction == MV_DOWN &&
8854       (last_line || (!IS_FREE(x, newy + 1) &&
8855                      (!IS_PLAYER(x, newy + 1) ||
8856                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8857     Impact(x, newy);
8858
8859   if (pushed_by_player && !game.use_change_when_pushing_bug)
8860   {
8861     int push_side = MV_DIR_OPPOSITE(direction);
8862     struct PlayerInfo *player = PLAYERINFO(x, y);
8863
8864     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8865                                player->index_bit, push_side);
8866     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8867                                         player->index_bit, push_side);
8868   }
8869
8870   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8871     MovDelay[newx][newy] = 1;
8872
8873   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8874
8875   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8876   TestIfElementHitsCustomElement(newx, newy, direction);
8877   TestIfPlayerTouchesCustomElement(newx, newy);
8878   TestIfElementTouchesCustomElement(newx, newy);
8879
8880   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8881       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8882     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8883                              MV_DIR_OPPOSITE(direction));
8884 }
8885
8886 int AmoebaNeighbourNr(int ax, int ay)
8887 {
8888   int i;
8889   int element = Tile[ax][ay];
8890   int group_nr = 0;
8891   static int xy[4][2] =
8892   {
8893     { 0, -1 },
8894     { -1, 0 },
8895     { +1, 0 },
8896     { 0, +1 }
8897   };
8898
8899   for (i = 0; i < NUM_DIRECTIONS; i++)
8900   {
8901     int x = ax + xy[i][0];
8902     int y = ay + xy[i][1];
8903
8904     if (!IN_LEV_FIELD(x, y))
8905       continue;
8906
8907     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8908       group_nr = AmoebaNr[x][y];
8909   }
8910
8911   return group_nr;
8912 }
8913
8914 static void AmoebaMerge(int ax, int ay)
8915 {
8916   int i, x, y, xx, yy;
8917   int new_group_nr = AmoebaNr[ax][ay];
8918   static int xy[4][2] =
8919   {
8920     { 0, -1 },
8921     { -1, 0 },
8922     { +1, 0 },
8923     { 0, +1 }
8924   };
8925
8926   if (new_group_nr == 0)
8927     return;
8928
8929   for (i = 0; i < NUM_DIRECTIONS; i++)
8930   {
8931     x = ax + xy[i][0];
8932     y = ay + xy[i][1];
8933
8934     if (!IN_LEV_FIELD(x, y))
8935       continue;
8936
8937     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8938          Tile[x][y] == EL_BD_AMOEBA ||
8939          Tile[x][y] == EL_AMOEBA_DEAD) &&
8940         AmoebaNr[x][y] != new_group_nr)
8941     {
8942       int old_group_nr = AmoebaNr[x][y];
8943
8944       if (old_group_nr == 0)
8945         return;
8946
8947       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8948       AmoebaCnt[old_group_nr] = 0;
8949       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8950       AmoebaCnt2[old_group_nr] = 0;
8951
8952       SCAN_PLAYFIELD(xx, yy)
8953       {
8954         if (AmoebaNr[xx][yy] == old_group_nr)
8955           AmoebaNr[xx][yy] = new_group_nr;
8956       }
8957     }
8958   }
8959 }
8960
8961 void AmoebaToDiamond(int ax, int ay)
8962 {
8963   int i, x, y;
8964
8965   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8966   {
8967     int group_nr = AmoebaNr[ax][ay];
8968
8969 #ifdef DEBUG
8970     if (group_nr == 0)
8971     {
8972       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8973       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8974
8975       return;
8976     }
8977 #endif
8978
8979     SCAN_PLAYFIELD(x, y)
8980     {
8981       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8982       {
8983         AmoebaNr[x][y] = 0;
8984         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8985       }
8986     }
8987
8988     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8989                             SND_AMOEBA_TURNING_TO_GEM :
8990                             SND_AMOEBA_TURNING_TO_ROCK));
8991     Bang(ax, ay);
8992   }
8993   else
8994   {
8995     static int xy[4][2] =
8996     {
8997       { 0, -1 },
8998       { -1, 0 },
8999       { +1, 0 },
9000       { 0, +1 }
9001     };
9002
9003     for (i = 0; i < NUM_DIRECTIONS; i++)
9004     {
9005       x = ax + xy[i][0];
9006       y = ay + xy[i][1];
9007
9008       if (!IN_LEV_FIELD(x, y))
9009         continue;
9010
9011       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9012       {
9013         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9014                               SND_AMOEBA_TURNING_TO_GEM :
9015                               SND_AMOEBA_TURNING_TO_ROCK));
9016         Bang(x, y);
9017       }
9018     }
9019   }
9020 }
9021
9022 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9023 {
9024   int x, y;
9025   int group_nr = AmoebaNr[ax][ay];
9026   boolean done = FALSE;
9027
9028 #ifdef DEBUG
9029   if (group_nr == 0)
9030   {
9031     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9032     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9033
9034     return;
9035   }
9036 #endif
9037
9038   SCAN_PLAYFIELD(x, y)
9039   {
9040     if (AmoebaNr[x][y] == group_nr &&
9041         (Tile[x][y] == EL_AMOEBA_DEAD ||
9042          Tile[x][y] == EL_BD_AMOEBA ||
9043          Tile[x][y] == EL_AMOEBA_GROWING))
9044     {
9045       AmoebaNr[x][y] = 0;
9046       Tile[x][y] = new_element;
9047       InitField(x, y, FALSE);
9048       TEST_DrawLevelField(x, y);
9049       done = TRUE;
9050     }
9051   }
9052
9053   if (done)
9054     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9055                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9056                             SND_BD_AMOEBA_TURNING_TO_GEM));
9057 }
9058
9059 static void AmoebaGrowing(int x, int y)
9060 {
9061   static unsigned int sound_delay = 0;
9062   static unsigned int sound_delay_value = 0;
9063
9064   if (!MovDelay[x][y])          // start new growing cycle
9065   {
9066     MovDelay[x][y] = 7;
9067
9068     if (DelayReached(&sound_delay, sound_delay_value))
9069     {
9070       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9071       sound_delay_value = 30;
9072     }
9073   }
9074
9075   if (MovDelay[x][y])           // wait some time before growing bigger
9076   {
9077     MovDelay[x][y]--;
9078     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9079     {
9080       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9081                                            6 - MovDelay[x][y]);
9082
9083       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9084     }
9085
9086     if (!MovDelay[x][y])
9087     {
9088       Tile[x][y] = Store[x][y];
9089       Store[x][y] = 0;
9090       TEST_DrawLevelField(x, y);
9091     }
9092   }
9093 }
9094
9095 static void AmoebaShrinking(int x, int y)
9096 {
9097   static unsigned int sound_delay = 0;
9098   static unsigned int sound_delay_value = 0;
9099
9100   if (!MovDelay[x][y])          // start new shrinking cycle
9101   {
9102     MovDelay[x][y] = 7;
9103
9104     if (DelayReached(&sound_delay, sound_delay_value))
9105       sound_delay_value = 30;
9106   }
9107
9108   if (MovDelay[x][y])           // wait some time before shrinking
9109   {
9110     MovDelay[x][y]--;
9111     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9112     {
9113       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9114                                            6 - MovDelay[x][y]);
9115
9116       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9117     }
9118
9119     if (!MovDelay[x][y])
9120     {
9121       Tile[x][y] = EL_EMPTY;
9122       TEST_DrawLevelField(x, y);
9123
9124       // don't let mole enter this field in this cycle;
9125       // (give priority to objects falling to this field from above)
9126       Stop[x][y] = TRUE;
9127     }
9128   }
9129 }
9130
9131 static void AmoebaReproduce(int ax, int ay)
9132 {
9133   int i;
9134   int element = Tile[ax][ay];
9135   int graphic = el2img(element);
9136   int newax = ax, neway = ay;
9137   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9138   static int xy[4][2] =
9139   {
9140     { 0, -1 },
9141     { -1, 0 },
9142     { +1, 0 },
9143     { 0, +1 }
9144   };
9145
9146   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9147   {
9148     Tile[ax][ay] = EL_AMOEBA_DEAD;
9149     TEST_DrawLevelField(ax, ay);
9150     return;
9151   }
9152
9153   if (IS_ANIMATED(graphic))
9154     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9155
9156   if (!MovDelay[ax][ay])        // start making new amoeba field
9157     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9158
9159   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9160   {
9161     MovDelay[ax][ay]--;
9162     if (MovDelay[ax][ay])
9163       return;
9164   }
9165
9166   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9167   {
9168     int start = RND(4);
9169     int x = ax + xy[start][0];
9170     int y = ay + xy[start][1];
9171
9172     if (!IN_LEV_FIELD(x, y))
9173       return;
9174
9175     if (IS_FREE(x, y) ||
9176         CAN_GROW_INTO(Tile[x][y]) ||
9177         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9178         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9179     {
9180       newax = x;
9181       neway = y;
9182     }
9183
9184     if (newax == ax && neway == ay)
9185       return;
9186   }
9187   else                          // normal or "filled" (BD style) amoeba
9188   {
9189     int start = RND(4);
9190     boolean waiting_for_player = FALSE;
9191
9192     for (i = 0; i < NUM_DIRECTIONS; i++)
9193     {
9194       int j = (start + i) % 4;
9195       int x = ax + xy[j][0];
9196       int y = ay + xy[j][1];
9197
9198       if (!IN_LEV_FIELD(x, y))
9199         continue;
9200
9201       if (IS_FREE(x, y) ||
9202           CAN_GROW_INTO(Tile[x][y]) ||
9203           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9204           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9205       {
9206         newax = x;
9207         neway = y;
9208         break;
9209       }
9210       else if (IS_PLAYER(x, y))
9211         waiting_for_player = TRUE;
9212     }
9213
9214     if (newax == ax && neway == ay)             // amoeba cannot grow
9215     {
9216       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9217       {
9218         Tile[ax][ay] = EL_AMOEBA_DEAD;
9219         TEST_DrawLevelField(ax, ay);
9220         AmoebaCnt[AmoebaNr[ax][ay]]--;
9221
9222         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9223         {
9224           if (element == EL_AMOEBA_FULL)
9225             AmoebaToDiamond(ax, ay);
9226           else if (element == EL_BD_AMOEBA)
9227             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9228         }
9229       }
9230       return;
9231     }
9232     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9233     {
9234       // amoeba gets larger by growing in some direction
9235
9236       int new_group_nr = AmoebaNr[ax][ay];
9237
9238 #ifdef DEBUG
9239   if (new_group_nr == 0)
9240   {
9241     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9242           newax, neway);
9243     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9244
9245     return;
9246   }
9247 #endif
9248
9249       AmoebaNr[newax][neway] = new_group_nr;
9250       AmoebaCnt[new_group_nr]++;
9251       AmoebaCnt2[new_group_nr]++;
9252
9253       // if amoeba touches other amoeba(s) after growing, unify them
9254       AmoebaMerge(newax, neway);
9255
9256       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9257       {
9258         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9259         return;
9260       }
9261     }
9262   }
9263
9264   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9265       (neway == lev_fieldy - 1 && newax != ax))
9266   {
9267     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9268     Store[newax][neway] = element;
9269   }
9270   else if (neway == ay || element == EL_EMC_DRIPPER)
9271   {
9272     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9273
9274     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9275   }
9276   else
9277   {
9278     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9279     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9280     Store[ax][ay] = EL_AMOEBA_DROP;
9281     ContinueMoving(ax, ay);
9282     return;
9283   }
9284
9285   TEST_DrawLevelField(newax, neway);
9286 }
9287
9288 static void Life(int ax, int ay)
9289 {
9290   int x1, y1, x2, y2;
9291   int life_time = 40;
9292   int element = Tile[ax][ay];
9293   int graphic = el2img(element);
9294   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9295                          level.biomaze);
9296   boolean changed = FALSE;
9297
9298   if (IS_ANIMATED(graphic))
9299     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9300
9301   if (Stop[ax][ay])
9302     return;
9303
9304   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9305     MovDelay[ax][ay] = life_time;
9306
9307   if (MovDelay[ax][ay])         // wait some time before next cycle
9308   {
9309     MovDelay[ax][ay]--;
9310     if (MovDelay[ax][ay])
9311       return;
9312   }
9313
9314   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9315   {
9316     int xx = ax+x1, yy = ay+y1;
9317     int old_element = Tile[xx][yy];
9318     int num_neighbours = 0;
9319
9320     if (!IN_LEV_FIELD(xx, yy))
9321       continue;
9322
9323     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9324     {
9325       int x = xx+x2, y = yy+y2;
9326
9327       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9328         continue;
9329
9330       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9331       boolean is_neighbour = FALSE;
9332
9333       if (level.use_life_bugs)
9334         is_neighbour =
9335           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9336            (IS_FREE(x, y)                             &&  Stop[x][y]));
9337       else
9338         is_neighbour =
9339           (Last[x][y] == element || is_player_cell);
9340
9341       if (is_neighbour)
9342         num_neighbours++;
9343     }
9344
9345     boolean is_free = FALSE;
9346
9347     if (level.use_life_bugs)
9348       is_free = (IS_FREE(xx, yy));
9349     else
9350       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9351
9352     if (xx == ax && yy == ay)           // field in the middle
9353     {
9354       if (num_neighbours < life_parameter[0] ||
9355           num_neighbours > life_parameter[1])
9356       {
9357         Tile[xx][yy] = EL_EMPTY;
9358         if (Tile[xx][yy] != old_element)
9359           TEST_DrawLevelField(xx, yy);
9360         Stop[xx][yy] = TRUE;
9361         changed = TRUE;
9362       }
9363     }
9364     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9365     {                                   // free border field
9366       if (num_neighbours >= life_parameter[2] &&
9367           num_neighbours <= life_parameter[3])
9368       {
9369         Tile[xx][yy] = element;
9370         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9371         if (Tile[xx][yy] != old_element)
9372           TEST_DrawLevelField(xx, yy);
9373         Stop[xx][yy] = TRUE;
9374         changed = TRUE;
9375       }
9376     }
9377   }
9378
9379   if (changed)
9380     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9381                    SND_GAME_OF_LIFE_GROWING);
9382 }
9383
9384 static void InitRobotWheel(int x, int y)
9385 {
9386   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9387 }
9388
9389 static void RunRobotWheel(int x, int y)
9390 {
9391   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9392 }
9393
9394 static void StopRobotWheel(int x, int y)
9395 {
9396   if (game.robot_wheel_x == x &&
9397       game.robot_wheel_y == y)
9398   {
9399     game.robot_wheel_x = -1;
9400     game.robot_wheel_y = -1;
9401     game.robot_wheel_active = FALSE;
9402   }
9403 }
9404
9405 static void InitTimegateWheel(int x, int y)
9406 {
9407   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9408 }
9409
9410 static void RunTimegateWheel(int x, int y)
9411 {
9412   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9413 }
9414
9415 static void InitMagicBallDelay(int x, int y)
9416 {
9417   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9418 }
9419
9420 static void ActivateMagicBall(int bx, int by)
9421 {
9422   int x, y;
9423
9424   if (level.ball_random)
9425   {
9426     int pos_border = RND(8);    // select one of the eight border elements
9427     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9428     int xx = pos_content % 3;
9429     int yy = pos_content / 3;
9430
9431     x = bx - 1 + xx;
9432     y = by - 1 + yy;
9433
9434     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9435       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9436   }
9437   else
9438   {
9439     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9440     {
9441       int xx = x - bx + 1;
9442       int yy = y - by + 1;
9443
9444       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9445         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9446     }
9447   }
9448
9449   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9450 }
9451
9452 static void CheckExit(int x, int y)
9453 {
9454   if (game.gems_still_needed > 0 ||
9455       game.sokoban_fields_still_needed > 0 ||
9456       game.sokoban_objects_still_needed > 0 ||
9457       game.lights_still_needed > 0)
9458   {
9459     int element = Tile[x][y];
9460     int graphic = el2img(element);
9461
9462     if (IS_ANIMATED(graphic))
9463       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9464
9465     return;
9466   }
9467
9468   // do not re-open exit door closed after last player
9469   if (game.all_players_gone)
9470     return;
9471
9472   Tile[x][y] = EL_EXIT_OPENING;
9473
9474   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9475 }
9476
9477 static void CheckExitEM(int x, int y)
9478 {
9479   if (game.gems_still_needed > 0 ||
9480       game.sokoban_fields_still_needed > 0 ||
9481       game.sokoban_objects_still_needed > 0 ||
9482       game.lights_still_needed > 0)
9483   {
9484     int element = Tile[x][y];
9485     int graphic = el2img(element);
9486
9487     if (IS_ANIMATED(graphic))
9488       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9489
9490     return;
9491   }
9492
9493   // do not re-open exit door closed after last player
9494   if (game.all_players_gone)
9495     return;
9496
9497   Tile[x][y] = EL_EM_EXIT_OPENING;
9498
9499   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9500 }
9501
9502 static void CheckExitSteel(int x, int y)
9503 {
9504   if (game.gems_still_needed > 0 ||
9505       game.sokoban_fields_still_needed > 0 ||
9506       game.sokoban_objects_still_needed > 0 ||
9507       game.lights_still_needed > 0)
9508   {
9509     int element = Tile[x][y];
9510     int graphic = el2img(element);
9511
9512     if (IS_ANIMATED(graphic))
9513       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9514
9515     return;
9516   }
9517
9518   // do not re-open exit door closed after last player
9519   if (game.all_players_gone)
9520     return;
9521
9522   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9523
9524   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9525 }
9526
9527 static void CheckExitSteelEM(int x, int y)
9528 {
9529   if (game.gems_still_needed > 0 ||
9530       game.sokoban_fields_still_needed > 0 ||
9531       game.sokoban_objects_still_needed > 0 ||
9532       game.lights_still_needed > 0)
9533   {
9534     int element = Tile[x][y];
9535     int graphic = el2img(element);
9536
9537     if (IS_ANIMATED(graphic))
9538       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9539
9540     return;
9541   }
9542
9543   // do not re-open exit door closed after last player
9544   if (game.all_players_gone)
9545     return;
9546
9547   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9548
9549   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9550 }
9551
9552 static void CheckExitSP(int x, int y)
9553 {
9554   if (game.gems_still_needed > 0)
9555   {
9556     int element = Tile[x][y];
9557     int graphic = el2img(element);
9558
9559     if (IS_ANIMATED(graphic))
9560       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9561
9562     return;
9563   }
9564
9565   // do not re-open exit door closed after last player
9566   if (game.all_players_gone)
9567     return;
9568
9569   Tile[x][y] = EL_SP_EXIT_OPENING;
9570
9571   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9572 }
9573
9574 static void CloseAllOpenTimegates(void)
9575 {
9576   int x, y;
9577
9578   SCAN_PLAYFIELD(x, y)
9579   {
9580     int element = Tile[x][y];
9581
9582     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9583     {
9584       Tile[x][y] = EL_TIMEGATE_CLOSING;
9585
9586       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9587     }
9588   }
9589 }
9590
9591 static void DrawTwinkleOnField(int x, int y)
9592 {
9593   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9594     return;
9595
9596   if (Tile[x][y] == EL_BD_DIAMOND)
9597     return;
9598
9599   if (MovDelay[x][y] == 0)      // next animation frame
9600     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9601
9602   if (MovDelay[x][y] != 0)      // wait some time before next frame
9603   {
9604     MovDelay[x][y]--;
9605
9606     DrawLevelElementAnimation(x, y, Tile[x][y]);
9607
9608     if (MovDelay[x][y] != 0)
9609     {
9610       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9611                                            10 - MovDelay[x][y]);
9612
9613       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9614     }
9615   }
9616 }
9617
9618 static void MauerWaechst(int x, int y)
9619 {
9620   int delay = 6;
9621
9622   if (!MovDelay[x][y])          // next animation frame
9623     MovDelay[x][y] = 3 * delay;
9624
9625   if (MovDelay[x][y])           // wait some time before next frame
9626   {
9627     MovDelay[x][y]--;
9628
9629     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9630     {
9631       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9632       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9633
9634       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9635     }
9636
9637     if (!MovDelay[x][y])
9638     {
9639       if (MovDir[x][y] == MV_LEFT)
9640       {
9641         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9642           TEST_DrawLevelField(x - 1, y);
9643       }
9644       else if (MovDir[x][y] == MV_RIGHT)
9645       {
9646         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9647           TEST_DrawLevelField(x + 1, y);
9648       }
9649       else if (MovDir[x][y] == MV_UP)
9650       {
9651         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9652           TEST_DrawLevelField(x, y - 1);
9653       }
9654       else
9655       {
9656         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9657           TEST_DrawLevelField(x, y + 1);
9658       }
9659
9660       Tile[x][y] = Store[x][y];
9661       Store[x][y] = 0;
9662       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9663       TEST_DrawLevelField(x, y);
9664     }
9665   }
9666 }
9667
9668 static void MauerAbleger(int ax, int ay)
9669 {
9670   int element = Tile[ax][ay];
9671   int graphic = el2img(element);
9672   boolean oben_frei = FALSE, unten_frei = FALSE;
9673   boolean links_frei = FALSE, rechts_frei = FALSE;
9674   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9675   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9676   boolean new_wall = FALSE;
9677
9678   if (IS_ANIMATED(graphic))
9679     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9680
9681   if (!MovDelay[ax][ay])        // start building new wall
9682     MovDelay[ax][ay] = 6;
9683
9684   if (MovDelay[ax][ay])         // wait some time before building new wall
9685   {
9686     MovDelay[ax][ay]--;
9687     if (MovDelay[ax][ay])
9688       return;
9689   }
9690
9691   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9692     oben_frei = TRUE;
9693   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9694     unten_frei = TRUE;
9695   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9696     links_frei = TRUE;
9697   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9698     rechts_frei = TRUE;
9699
9700   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9701       element == EL_EXPANDABLE_WALL_ANY)
9702   {
9703     if (oben_frei)
9704     {
9705       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9706       Store[ax][ay-1] = element;
9707       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9708       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9709         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9710                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9711       new_wall = TRUE;
9712     }
9713     if (unten_frei)
9714     {
9715       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9716       Store[ax][ay+1] = element;
9717       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9718       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9719         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9720                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9721       new_wall = TRUE;
9722     }
9723   }
9724
9725   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9726       element == EL_EXPANDABLE_WALL_ANY ||
9727       element == EL_EXPANDABLE_WALL ||
9728       element == EL_BD_EXPANDABLE_WALL)
9729   {
9730     if (links_frei)
9731     {
9732       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9733       Store[ax-1][ay] = element;
9734       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9735       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9736         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9737                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9738       new_wall = TRUE;
9739     }
9740
9741     if (rechts_frei)
9742     {
9743       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9744       Store[ax+1][ay] = element;
9745       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9746       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9747         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9748                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9749       new_wall = TRUE;
9750     }
9751   }
9752
9753   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9754     TEST_DrawLevelField(ax, ay);
9755
9756   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9757     oben_massiv = TRUE;
9758   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9759     unten_massiv = TRUE;
9760   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9761     links_massiv = TRUE;
9762   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9763     rechts_massiv = TRUE;
9764
9765   if (((oben_massiv && unten_massiv) ||
9766        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9767        element == EL_EXPANDABLE_WALL) &&
9768       ((links_massiv && rechts_massiv) ||
9769        element == EL_EXPANDABLE_WALL_VERTICAL))
9770     Tile[ax][ay] = EL_WALL;
9771
9772   if (new_wall)
9773     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9774 }
9775
9776 static void MauerAblegerStahl(int ax, int ay)
9777 {
9778   int element = Tile[ax][ay];
9779   int graphic = el2img(element);
9780   boolean oben_frei = FALSE, unten_frei = FALSE;
9781   boolean links_frei = FALSE, rechts_frei = FALSE;
9782   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9783   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9784   boolean new_wall = FALSE;
9785
9786   if (IS_ANIMATED(graphic))
9787     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9788
9789   if (!MovDelay[ax][ay])        // start building new wall
9790     MovDelay[ax][ay] = 6;
9791
9792   if (MovDelay[ax][ay])         // wait some time before building new wall
9793   {
9794     MovDelay[ax][ay]--;
9795     if (MovDelay[ax][ay])
9796       return;
9797   }
9798
9799   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9800     oben_frei = TRUE;
9801   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9802     unten_frei = TRUE;
9803   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9804     links_frei = TRUE;
9805   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9806     rechts_frei = TRUE;
9807
9808   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9809       element == EL_EXPANDABLE_STEELWALL_ANY)
9810   {
9811     if (oben_frei)
9812     {
9813       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9814       Store[ax][ay-1] = element;
9815       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9816       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9817         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9818                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9819       new_wall = TRUE;
9820     }
9821     if (unten_frei)
9822     {
9823       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9824       Store[ax][ay+1] = element;
9825       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9826       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9827         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9828                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9829       new_wall = TRUE;
9830     }
9831   }
9832
9833   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9834       element == EL_EXPANDABLE_STEELWALL_ANY)
9835   {
9836     if (links_frei)
9837     {
9838       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9839       Store[ax-1][ay] = element;
9840       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9841       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9842         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9843                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9844       new_wall = TRUE;
9845     }
9846
9847     if (rechts_frei)
9848     {
9849       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9850       Store[ax+1][ay] = element;
9851       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9852       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9853         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9854                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9855       new_wall = TRUE;
9856     }
9857   }
9858
9859   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9860     oben_massiv = TRUE;
9861   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9862     unten_massiv = TRUE;
9863   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9864     links_massiv = TRUE;
9865   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9866     rechts_massiv = TRUE;
9867
9868   if (((oben_massiv && unten_massiv) ||
9869        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9870       ((links_massiv && rechts_massiv) ||
9871        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9872     Tile[ax][ay] = EL_STEELWALL;
9873
9874   if (new_wall)
9875     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9876 }
9877
9878 static void CheckForDragon(int x, int y)
9879 {
9880   int i, j;
9881   boolean dragon_found = FALSE;
9882   static int xy[4][2] =
9883   {
9884     { 0, -1 },
9885     { -1, 0 },
9886     { +1, 0 },
9887     { 0, +1 }
9888   };
9889
9890   for (i = 0; i < NUM_DIRECTIONS; i++)
9891   {
9892     for (j = 0; j < 4; j++)
9893     {
9894       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9895
9896       if (IN_LEV_FIELD(xx, yy) &&
9897           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9898       {
9899         if (Tile[xx][yy] == EL_DRAGON)
9900           dragon_found = TRUE;
9901       }
9902       else
9903         break;
9904     }
9905   }
9906
9907   if (!dragon_found)
9908   {
9909     for (i = 0; i < NUM_DIRECTIONS; i++)
9910     {
9911       for (j = 0; j < 3; j++)
9912       {
9913         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9914   
9915         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9916         {
9917           Tile[xx][yy] = EL_EMPTY;
9918           TEST_DrawLevelField(xx, yy);
9919         }
9920         else
9921           break;
9922       }
9923     }
9924   }
9925 }
9926
9927 static void InitBuggyBase(int x, int y)
9928 {
9929   int element = Tile[x][y];
9930   int activating_delay = FRAMES_PER_SECOND / 4;
9931
9932   ChangeDelay[x][y] =
9933     (element == EL_SP_BUGGY_BASE ?
9934      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9935      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9936      activating_delay :
9937      element == EL_SP_BUGGY_BASE_ACTIVE ?
9938      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9939 }
9940
9941 static void WarnBuggyBase(int x, int y)
9942 {
9943   int i;
9944   static int xy[4][2] =
9945   {
9946     { 0, -1 },
9947     { -1, 0 },
9948     { +1, 0 },
9949     { 0, +1 }
9950   };
9951
9952   for (i = 0; i < NUM_DIRECTIONS; i++)
9953   {
9954     int xx = x + xy[i][0];
9955     int yy = y + xy[i][1];
9956
9957     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9958     {
9959       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9960
9961       break;
9962     }
9963   }
9964 }
9965
9966 static void InitTrap(int x, int y)
9967 {
9968   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9969 }
9970
9971 static void ActivateTrap(int x, int y)
9972 {
9973   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9974 }
9975
9976 static void ChangeActiveTrap(int x, int y)
9977 {
9978   int graphic = IMG_TRAP_ACTIVE;
9979
9980   // if new animation frame was drawn, correct crumbled sand border
9981   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9982     TEST_DrawLevelFieldCrumbled(x, y);
9983 }
9984
9985 static int getSpecialActionElement(int element, int number, int base_element)
9986 {
9987   return (element != EL_EMPTY ? element :
9988           number != -1 ? base_element + number - 1 :
9989           EL_EMPTY);
9990 }
9991
9992 static int getModifiedActionNumber(int value_old, int operator, int operand,
9993                                    int value_min, int value_max)
9994 {
9995   int value_new = (operator == CA_MODE_SET      ? operand :
9996                    operator == CA_MODE_ADD      ? value_old + operand :
9997                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9998                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9999                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10000                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10001                    value_old);
10002
10003   return (value_new < value_min ? value_min :
10004           value_new > value_max ? value_max :
10005           value_new);
10006 }
10007
10008 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10009 {
10010   struct ElementInfo *ei = &element_info[element];
10011   struct ElementChangeInfo *change = &ei->change_page[page];
10012   int target_element = change->target_element;
10013   int action_type = change->action_type;
10014   int action_mode = change->action_mode;
10015   int action_arg = change->action_arg;
10016   int action_element = change->action_element;
10017   int i;
10018
10019   if (!change->has_action)
10020     return;
10021
10022   // ---------- determine action paramater values -----------------------------
10023
10024   int level_time_value =
10025     (level.time > 0 ? TimeLeft :
10026      TimePlayed);
10027
10028   int action_arg_element_raw =
10029     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10030      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10031      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10032      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10033      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10034      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10035      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10036      EL_EMPTY);
10037   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10038
10039   int action_arg_direction =
10040     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10041      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10042      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10043      change->actual_trigger_side :
10044      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10045      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10046      MV_NONE);
10047
10048   int action_arg_number_min =
10049     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10050      CA_ARG_MIN);
10051
10052   int action_arg_number_max =
10053     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10054      action_type == CA_SET_LEVEL_GEMS ? 999 :
10055      action_type == CA_SET_LEVEL_TIME ? 9999 :
10056      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10057      action_type == CA_SET_CE_VALUE ? 9999 :
10058      action_type == CA_SET_CE_SCORE ? 9999 :
10059      CA_ARG_MAX);
10060
10061   int action_arg_number_reset =
10062     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10063      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10064      action_type == CA_SET_LEVEL_TIME ? level.time :
10065      action_type == CA_SET_LEVEL_SCORE ? 0 :
10066      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10067      action_type == CA_SET_CE_SCORE ? 0 :
10068      0);
10069
10070   int action_arg_number =
10071     (action_arg <= CA_ARG_MAX ? action_arg :
10072      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10073      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10074      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10075      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10076      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10077      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10078      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10079      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10080      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10081      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10082      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10083      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10084      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10085      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10086      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10087      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10088      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10089      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10090      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10091      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10092      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10093      -1);
10094
10095   int action_arg_number_old =
10096     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10097      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10098      action_type == CA_SET_LEVEL_SCORE ? game.score :
10099      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10100      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10101      0);
10102
10103   int action_arg_number_new =
10104     getModifiedActionNumber(action_arg_number_old,
10105                             action_mode, action_arg_number,
10106                             action_arg_number_min, action_arg_number_max);
10107
10108   int trigger_player_bits =
10109     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10110      change->actual_trigger_player_bits : change->trigger_player);
10111
10112   int action_arg_player_bits =
10113     (action_arg >= CA_ARG_PLAYER_1 &&
10114      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10115      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10116      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10117      PLAYER_BITS_ANY);
10118
10119   // ---------- execute action  -----------------------------------------------
10120
10121   switch (action_type)
10122   {
10123     case CA_NO_ACTION:
10124     {
10125       return;
10126     }
10127
10128     // ---------- level actions  ----------------------------------------------
10129
10130     case CA_RESTART_LEVEL:
10131     {
10132       game.restart_level = TRUE;
10133
10134       break;
10135     }
10136
10137     case CA_SHOW_ENVELOPE:
10138     {
10139       int element = getSpecialActionElement(action_arg_element,
10140                                             action_arg_number, EL_ENVELOPE_1);
10141
10142       if (IS_ENVELOPE(element))
10143         local_player->show_envelope = element;
10144
10145       break;
10146     }
10147
10148     case CA_SET_LEVEL_TIME:
10149     {
10150       if (level.time > 0)       // only modify limited time value
10151       {
10152         TimeLeft = action_arg_number_new;
10153
10154         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10155
10156         DisplayGameControlValues();
10157
10158         if (!TimeLeft && setup.time_limit)
10159           for (i = 0; i < MAX_PLAYERS; i++)
10160             KillPlayer(&stored_player[i]);
10161       }
10162
10163       break;
10164     }
10165
10166     case CA_SET_LEVEL_SCORE:
10167     {
10168       game.score = action_arg_number_new;
10169
10170       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10171
10172       DisplayGameControlValues();
10173
10174       break;
10175     }
10176
10177     case CA_SET_LEVEL_GEMS:
10178     {
10179       game.gems_still_needed = action_arg_number_new;
10180
10181       game.snapshot.collected_item = TRUE;
10182
10183       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10184
10185       DisplayGameControlValues();
10186
10187       break;
10188     }
10189
10190     case CA_SET_LEVEL_WIND:
10191     {
10192       game.wind_direction = action_arg_direction;
10193
10194       break;
10195     }
10196
10197     case CA_SET_LEVEL_RANDOM_SEED:
10198     {
10199       // ensure that setting a new random seed while playing is predictable
10200       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10201
10202       break;
10203     }
10204
10205     // ---------- player actions  ---------------------------------------------
10206
10207     case CA_MOVE_PLAYER:
10208     case CA_MOVE_PLAYER_NEW:
10209     {
10210       // automatically move to the next field in specified direction
10211       for (i = 0; i < MAX_PLAYERS; i++)
10212         if (trigger_player_bits & (1 << i))
10213           if (action_type == CA_MOVE_PLAYER ||
10214               stored_player[i].MovPos == 0)
10215             stored_player[i].programmed_action = action_arg_direction;
10216
10217       break;
10218     }
10219
10220     case CA_EXIT_PLAYER:
10221     {
10222       for (i = 0; i < MAX_PLAYERS; i++)
10223         if (action_arg_player_bits & (1 << i))
10224           ExitPlayer(&stored_player[i]);
10225
10226       if (game.players_still_needed == 0)
10227         LevelSolved();
10228
10229       break;
10230     }
10231
10232     case CA_KILL_PLAYER:
10233     {
10234       for (i = 0; i < MAX_PLAYERS; i++)
10235         if (action_arg_player_bits & (1 << i))
10236           KillPlayer(&stored_player[i]);
10237
10238       break;
10239     }
10240
10241     case CA_SET_PLAYER_KEYS:
10242     {
10243       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10244       int element = getSpecialActionElement(action_arg_element,
10245                                             action_arg_number, EL_KEY_1);
10246
10247       if (IS_KEY(element))
10248       {
10249         for (i = 0; i < MAX_PLAYERS; i++)
10250         {
10251           if (trigger_player_bits & (1 << i))
10252           {
10253             stored_player[i].key[KEY_NR(element)] = key_state;
10254
10255             DrawGameDoorValues();
10256           }
10257         }
10258       }
10259
10260       break;
10261     }
10262
10263     case CA_SET_PLAYER_SPEED:
10264     {
10265       for (i = 0; i < MAX_PLAYERS; i++)
10266       {
10267         if (trigger_player_bits & (1 << i))
10268         {
10269           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10270
10271           if (action_arg == CA_ARG_SPEED_FASTER &&
10272               stored_player[i].cannot_move)
10273           {
10274             action_arg_number = STEPSIZE_VERY_SLOW;
10275           }
10276           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10277                    action_arg == CA_ARG_SPEED_FASTER)
10278           {
10279             action_arg_number = 2;
10280             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10281                            CA_MODE_MULTIPLY);
10282           }
10283           else if (action_arg == CA_ARG_NUMBER_RESET)
10284           {
10285             action_arg_number = level.initial_player_stepsize[i];
10286           }
10287
10288           move_stepsize =
10289             getModifiedActionNumber(move_stepsize,
10290                                     action_mode,
10291                                     action_arg_number,
10292                                     action_arg_number_min,
10293                                     action_arg_number_max);
10294
10295           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10296         }
10297       }
10298
10299       break;
10300     }
10301
10302     case CA_SET_PLAYER_SHIELD:
10303     {
10304       for (i = 0; i < MAX_PLAYERS; i++)
10305       {
10306         if (trigger_player_bits & (1 << i))
10307         {
10308           if (action_arg == CA_ARG_SHIELD_OFF)
10309           {
10310             stored_player[i].shield_normal_time_left = 0;
10311             stored_player[i].shield_deadly_time_left = 0;
10312           }
10313           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10314           {
10315             stored_player[i].shield_normal_time_left = 999999;
10316           }
10317           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10318           {
10319             stored_player[i].shield_normal_time_left = 999999;
10320             stored_player[i].shield_deadly_time_left = 999999;
10321           }
10322         }
10323       }
10324
10325       break;
10326     }
10327
10328     case CA_SET_PLAYER_GRAVITY:
10329     {
10330       for (i = 0; i < MAX_PLAYERS; i++)
10331       {
10332         if (trigger_player_bits & (1 << i))
10333         {
10334           stored_player[i].gravity =
10335             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10336              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10337              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10338              stored_player[i].gravity);
10339         }
10340       }
10341
10342       break;
10343     }
10344
10345     case CA_SET_PLAYER_ARTWORK:
10346     {
10347       for (i = 0; i < MAX_PLAYERS; i++)
10348       {
10349         if (trigger_player_bits & (1 << i))
10350         {
10351           int artwork_element = action_arg_element;
10352
10353           if (action_arg == CA_ARG_ELEMENT_RESET)
10354             artwork_element =
10355               (level.use_artwork_element[i] ? level.artwork_element[i] :
10356                stored_player[i].element_nr);
10357
10358           if (stored_player[i].artwork_element != artwork_element)
10359             stored_player[i].Frame = 0;
10360
10361           stored_player[i].artwork_element = artwork_element;
10362
10363           SetPlayerWaiting(&stored_player[i], FALSE);
10364
10365           // set number of special actions for bored and sleeping animation
10366           stored_player[i].num_special_action_bored =
10367             get_num_special_action(artwork_element,
10368                                    ACTION_BORING_1, ACTION_BORING_LAST);
10369           stored_player[i].num_special_action_sleeping =
10370             get_num_special_action(artwork_element,
10371                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10372         }
10373       }
10374
10375       break;
10376     }
10377
10378     case CA_SET_PLAYER_INVENTORY:
10379     {
10380       for (i = 0; i < MAX_PLAYERS; i++)
10381       {
10382         struct PlayerInfo *player = &stored_player[i];
10383         int j, k;
10384
10385         if (trigger_player_bits & (1 << i))
10386         {
10387           int inventory_element = action_arg_element;
10388
10389           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10390               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10391               action_arg == CA_ARG_ELEMENT_ACTION)
10392           {
10393             int element = inventory_element;
10394             int collect_count = element_info[element].collect_count_initial;
10395
10396             if (!IS_CUSTOM_ELEMENT(element))
10397               collect_count = 1;
10398
10399             if (collect_count == 0)
10400               player->inventory_infinite_element = element;
10401             else
10402               for (k = 0; k < collect_count; k++)
10403                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10404                   player->inventory_element[player->inventory_size++] =
10405                     element;
10406           }
10407           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10408                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10409                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10410           {
10411             if (player->inventory_infinite_element != EL_UNDEFINED &&
10412                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10413                                      action_arg_element_raw))
10414               player->inventory_infinite_element = EL_UNDEFINED;
10415
10416             for (k = 0, j = 0; j < player->inventory_size; j++)
10417             {
10418               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10419                                         action_arg_element_raw))
10420                 player->inventory_element[k++] = player->inventory_element[j];
10421             }
10422
10423             player->inventory_size = k;
10424           }
10425           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10426           {
10427             if (player->inventory_size > 0)
10428             {
10429               for (j = 0; j < player->inventory_size - 1; j++)
10430                 player->inventory_element[j] = player->inventory_element[j + 1];
10431
10432               player->inventory_size--;
10433             }
10434           }
10435           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10436           {
10437             if (player->inventory_size > 0)
10438               player->inventory_size--;
10439           }
10440           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10441           {
10442             player->inventory_infinite_element = EL_UNDEFINED;
10443             player->inventory_size = 0;
10444           }
10445           else if (action_arg == CA_ARG_INVENTORY_RESET)
10446           {
10447             player->inventory_infinite_element = EL_UNDEFINED;
10448             player->inventory_size = 0;
10449
10450             if (level.use_initial_inventory[i])
10451             {
10452               for (j = 0; j < level.initial_inventory_size[i]; j++)
10453               {
10454                 int element = level.initial_inventory_content[i][j];
10455                 int collect_count = element_info[element].collect_count_initial;
10456
10457                 if (!IS_CUSTOM_ELEMENT(element))
10458                   collect_count = 1;
10459
10460                 if (collect_count == 0)
10461                   player->inventory_infinite_element = element;
10462                 else
10463                   for (k = 0; k < collect_count; k++)
10464                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10465                       player->inventory_element[player->inventory_size++] =
10466                         element;
10467               }
10468             }
10469           }
10470         }
10471       }
10472
10473       break;
10474     }
10475
10476     // ---------- CE actions  -------------------------------------------------
10477
10478     case CA_SET_CE_VALUE:
10479     {
10480       int last_ce_value = CustomValue[x][y];
10481
10482       CustomValue[x][y] = action_arg_number_new;
10483
10484       if (CustomValue[x][y] != last_ce_value)
10485       {
10486         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10487         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10488
10489         if (CustomValue[x][y] == 0)
10490         {
10491           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10492           ChangeCount[x][y] = 0;        // allow at least one more change
10493
10494           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10495           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10496         }
10497       }
10498
10499       break;
10500     }
10501
10502     case CA_SET_CE_SCORE:
10503     {
10504       int last_ce_score = ei->collect_score;
10505
10506       ei->collect_score = action_arg_number_new;
10507
10508       if (ei->collect_score != last_ce_score)
10509       {
10510         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10511         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10512
10513         if (ei->collect_score == 0)
10514         {
10515           int xx, yy;
10516
10517           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10518           ChangeCount[x][y] = 0;        // allow at least one more change
10519
10520           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10521           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10522
10523           /*
10524             This is a very special case that seems to be a mixture between
10525             CheckElementChange() and CheckTriggeredElementChange(): while
10526             the first one only affects single elements that are triggered
10527             directly, the second one affects multiple elements in the playfield
10528             that are triggered indirectly by another element. This is a third
10529             case: Changing the CE score always affects multiple identical CEs,
10530             so every affected CE must be checked, not only the single CE for
10531             which the CE score was changed in the first place (as every instance
10532             of that CE shares the same CE score, and therefore also can change)!
10533           */
10534           SCAN_PLAYFIELD(xx, yy)
10535           {
10536             if (Tile[xx][yy] == element)
10537               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10538                                  CE_SCORE_GETS_ZERO);
10539           }
10540         }
10541       }
10542
10543       break;
10544     }
10545
10546     case CA_SET_CE_ARTWORK:
10547     {
10548       int artwork_element = action_arg_element;
10549       boolean reset_frame = FALSE;
10550       int xx, yy;
10551
10552       if (action_arg == CA_ARG_ELEMENT_RESET)
10553         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10554                            element);
10555
10556       if (ei->gfx_element != artwork_element)
10557         reset_frame = TRUE;
10558
10559       ei->gfx_element = artwork_element;
10560
10561       SCAN_PLAYFIELD(xx, yy)
10562       {
10563         if (Tile[xx][yy] == element)
10564         {
10565           if (reset_frame)
10566           {
10567             ResetGfxAnimation(xx, yy);
10568             ResetRandomAnimationValue(xx, yy);
10569           }
10570
10571           TEST_DrawLevelField(xx, yy);
10572         }
10573       }
10574
10575       break;
10576     }
10577
10578     // ---------- engine actions  ---------------------------------------------
10579
10580     case CA_SET_ENGINE_SCAN_MODE:
10581     {
10582       InitPlayfieldScanMode(action_arg);
10583
10584       break;
10585     }
10586
10587     default:
10588       break;
10589   }
10590 }
10591
10592 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10593 {
10594   int old_element = Tile[x][y];
10595   int new_element = GetElementFromGroupElement(element);
10596   int previous_move_direction = MovDir[x][y];
10597   int last_ce_value = CustomValue[x][y];
10598   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10599   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10600   boolean add_player_onto_element = (new_element_is_player &&
10601                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10602                                      IS_WALKABLE(old_element));
10603
10604   if (!add_player_onto_element)
10605   {
10606     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10607       RemoveMovingField(x, y);
10608     else
10609       RemoveField(x, y);
10610
10611     Tile[x][y] = new_element;
10612
10613     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10614       MovDir[x][y] = previous_move_direction;
10615
10616     if (element_info[new_element].use_last_ce_value)
10617       CustomValue[x][y] = last_ce_value;
10618
10619     InitField_WithBug1(x, y, FALSE);
10620
10621     new_element = Tile[x][y];   // element may have changed
10622
10623     ResetGfxAnimation(x, y);
10624     ResetRandomAnimationValue(x, y);
10625
10626     TEST_DrawLevelField(x, y);
10627
10628     if (GFX_CRUMBLED(new_element))
10629       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10630   }
10631
10632   // check if element under the player changes from accessible to unaccessible
10633   // (needed for special case of dropping element which then changes)
10634   // (must be checked after creating new element for walkable group elements)
10635   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10636       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10637   {
10638     Bang(x, y);
10639
10640     return;
10641   }
10642
10643   // "ChangeCount" not set yet to allow "entered by player" change one time
10644   if (new_element_is_player)
10645     RelocatePlayer(x, y, new_element);
10646
10647   if (is_change)
10648     ChangeCount[x][y]++;        // count number of changes in the same frame
10649
10650   TestIfBadThingTouchesPlayer(x, y);
10651   TestIfPlayerTouchesCustomElement(x, y);
10652   TestIfElementTouchesCustomElement(x, y);
10653 }
10654
10655 static void CreateField(int x, int y, int element)
10656 {
10657   CreateFieldExt(x, y, element, FALSE);
10658 }
10659
10660 static void CreateElementFromChange(int x, int y, int element)
10661 {
10662   element = GET_VALID_RUNTIME_ELEMENT(element);
10663
10664   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10665   {
10666     int old_element = Tile[x][y];
10667
10668     // prevent changed element from moving in same engine frame
10669     // unless both old and new element can either fall or move
10670     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10671         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10672       Stop[x][y] = TRUE;
10673   }
10674
10675   CreateFieldExt(x, y, element, TRUE);
10676 }
10677
10678 static boolean ChangeElement(int x, int y, int element, int page)
10679 {
10680   struct ElementInfo *ei = &element_info[element];
10681   struct ElementChangeInfo *change = &ei->change_page[page];
10682   int ce_value = CustomValue[x][y];
10683   int ce_score = ei->collect_score;
10684   int target_element;
10685   int old_element = Tile[x][y];
10686
10687   // always use default change event to prevent running into a loop
10688   if (ChangeEvent[x][y] == -1)
10689     ChangeEvent[x][y] = CE_DELAY;
10690
10691   if (ChangeEvent[x][y] == CE_DELAY)
10692   {
10693     // reset actual trigger element, trigger player and action element
10694     change->actual_trigger_element = EL_EMPTY;
10695     change->actual_trigger_player = EL_EMPTY;
10696     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10697     change->actual_trigger_side = CH_SIDE_NONE;
10698     change->actual_trigger_ce_value = 0;
10699     change->actual_trigger_ce_score = 0;
10700   }
10701
10702   // do not change elements more than a specified maximum number of changes
10703   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10704     return FALSE;
10705
10706   ChangeCount[x][y]++;          // count number of changes in the same frame
10707
10708   if (change->explode)
10709   {
10710     Bang(x, y);
10711
10712     return TRUE;
10713   }
10714
10715   if (change->use_target_content)
10716   {
10717     boolean complete_replace = TRUE;
10718     boolean can_replace[3][3];
10719     int xx, yy;
10720
10721     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10722     {
10723       boolean is_empty;
10724       boolean is_walkable;
10725       boolean is_diggable;
10726       boolean is_collectible;
10727       boolean is_removable;
10728       boolean is_destructible;
10729       int ex = x + xx - 1;
10730       int ey = y + yy - 1;
10731       int content_element = change->target_content.e[xx][yy];
10732       int e;
10733
10734       can_replace[xx][yy] = TRUE;
10735
10736       if (ex == x && ey == y)   // do not check changing element itself
10737         continue;
10738
10739       if (content_element == EL_EMPTY_SPACE)
10740       {
10741         can_replace[xx][yy] = FALSE;    // do not replace border with space
10742
10743         continue;
10744       }
10745
10746       if (!IN_LEV_FIELD(ex, ey))
10747       {
10748         can_replace[xx][yy] = FALSE;
10749         complete_replace = FALSE;
10750
10751         continue;
10752       }
10753
10754       e = Tile[ex][ey];
10755
10756       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10757         e = MovingOrBlocked2Element(ex, ey);
10758
10759       is_empty = (IS_FREE(ex, ey) ||
10760                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10761
10762       is_walkable     = (is_empty || IS_WALKABLE(e));
10763       is_diggable     = (is_empty || IS_DIGGABLE(e));
10764       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10765       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10766       is_removable    = (is_diggable || is_collectible);
10767
10768       can_replace[xx][yy] =
10769         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10770           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10771           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10772           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10773           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10774           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10775          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10776
10777       if (!can_replace[xx][yy])
10778         complete_replace = FALSE;
10779     }
10780
10781     if (!change->only_if_complete || complete_replace)
10782     {
10783       boolean something_has_changed = FALSE;
10784
10785       if (change->only_if_complete && change->use_random_replace &&
10786           RND(100) < change->random_percentage)
10787         return FALSE;
10788
10789       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10790       {
10791         int ex = x + xx - 1;
10792         int ey = y + yy - 1;
10793         int content_element;
10794
10795         if (can_replace[xx][yy] && (!change->use_random_replace ||
10796                                     RND(100) < change->random_percentage))
10797         {
10798           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10799             RemoveMovingField(ex, ey);
10800
10801           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10802
10803           content_element = change->target_content.e[xx][yy];
10804           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10805                                               ce_value, ce_score);
10806
10807           CreateElementFromChange(ex, ey, target_element);
10808
10809           something_has_changed = TRUE;
10810
10811           // for symmetry reasons, freeze newly created border elements
10812           if (ex != x || ey != y)
10813             Stop[ex][ey] = TRUE;        // no more moving in this frame
10814         }
10815       }
10816
10817       if (something_has_changed)
10818       {
10819         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10820         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10821       }
10822     }
10823   }
10824   else
10825   {
10826     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10827                                         ce_value, ce_score);
10828
10829     if (element == EL_DIAGONAL_GROWING ||
10830         element == EL_DIAGONAL_SHRINKING)
10831     {
10832       target_element = Store[x][y];
10833
10834       Store[x][y] = EL_EMPTY;
10835     }
10836
10837     // special case: element changes to player (and may be kept if walkable)
10838     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10839       CreateElementFromChange(x, y, EL_EMPTY);
10840
10841     CreateElementFromChange(x, y, target_element);
10842
10843     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10844     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10845   }
10846
10847   // this uses direct change before indirect change
10848   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10849
10850   return TRUE;
10851 }
10852
10853 static void HandleElementChange(int x, int y, int page)
10854 {
10855   int element = MovingOrBlocked2Element(x, y);
10856   struct ElementInfo *ei = &element_info[element];
10857   struct ElementChangeInfo *change = &ei->change_page[page];
10858   boolean handle_action_before_change = FALSE;
10859
10860 #ifdef DEBUG
10861   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10862       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10863   {
10864     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10865           x, y, element, element_info[element].token_name);
10866     Debug("game:playing:HandleElementChange", "This should never happen!");
10867   }
10868 #endif
10869
10870   // this can happen with classic bombs on walkable, changing elements
10871   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10872   {
10873     return;
10874   }
10875
10876   if (ChangeDelay[x][y] == 0)           // initialize element change
10877   {
10878     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10879
10880     if (change->can_change)
10881     {
10882       // !!! not clear why graphic animation should be reset at all here !!!
10883       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10884       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10885
10886       /*
10887         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10888
10889         When using an animation frame delay of 1 (this only happens with
10890         "sp_zonk.moving.left/right" in the classic graphics), the default
10891         (non-moving) animation shows wrong animation frames (while the
10892         moving animation, like "sp_zonk.moving.left/right", is correct,
10893         so this graphical bug never shows up with the classic graphics).
10894         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10895         be drawn instead of the correct frames 0,1,2,3. This is caused by
10896         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10897         an element change: First when the change delay ("ChangeDelay[][]")
10898         counter has reached zero after decrementing, then a second time in
10899         the next frame (after "GfxFrame[][]" was already incremented) when
10900         "ChangeDelay[][]" is reset to the initial delay value again.
10901
10902         This causes frame 0 to be drawn twice, while the last frame won't
10903         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10904
10905         As some animations may already be cleverly designed around this bug
10906         (at least the "Snake Bite" snake tail animation does this), it cannot
10907         simply be fixed here without breaking such existing animations.
10908         Unfortunately, it cannot easily be detected if a graphics set was
10909         designed "before" or "after" the bug was fixed. As a workaround,
10910         a new graphics set option "game.graphics_engine_version" was added
10911         to be able to specify the game's major release version for which the
10912         graphics set was designed, which can then be used to decide if the
10913         bugfix should be used (version 4 and above) or not (version 3 or
10914         below, or if no version was specified at all, as with old sets).
10915
10916         (The wrong/fixed animation frames can be tested with the test level set
10917         "test_gfxframe" and level "000", which contains a specially prepared
10918         custom element at level position (x/y) == (11/9) which uses the zonk
10919         animation mentioned above. Using "game.graphics_engine_version: 4"
10920         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10921         This can also be seen from the debug output for this test element.)
10922       */
10923
10924       // when a custom element is about to change (for example by change delay),
10925       // do not reset graphic animation when the custom element is moving
10926       if (game.graphics_engine_version < 4 &&
10927           !IS_MOVING(x, y))
10928       {
10929         ResetGfxAnimation(x, y);
10930         ResetRandomAnimationValue(x, y);
10931       }
10932
10933       if (change->pre_change_function)
10934         change->pre_change_function(x, y);
10935     }
10936   }
10937
10938   ChangeDelay[x][y]--;
10939
10940   if (ChangeDelay[x][y] != 0)           // continue element change
10941   {
10942     if (change->can_change)
10943     {
10944       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10945
10946       if (IS_ANIMATED(graphic))
10947         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10948
10949       if (change->change_function)
10950         change->change_function(x, y);
10951     }
10952   }
10953   else                                  // finish element change
10954   {
10955     if (ChangePage[x][y] != -1)         // remember page from delayed change
10956     {
10957       page = ChangePage[x][y];
10958       ChangePage[x][y] = -1;
10959
10960       change = &ei->change_page[page];
10961     }
10962
10963     if (IS_MOVING(x, y))                // never change a running system ;-)
10964     {
10965       ChangeDelay[x][y] = 1;            // try change after next move step
10966       ChangePage[x][y] = page;          // remember page to use for change
10967
10968       return;
10969     }
10970
10971     // special case: set new level random seed before changing element
10972     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10973       handle_action_before_change = TRUE;
10974
10975     if (change->has_action && handle_action_before_change)
10976       ExecuteCustomElementAction(x, y, element, page);
10977
10978     if (change->can_change)
10979     {
10980       if (ChangeElement(x, y, element, page))
10981       {
10982         if (change->post_change_function)
10983           change->post_change_function(x, y);
10984       }
10985     }
10986
10987     if (change->has_action && !handle_action_before_change)
10988       ExecuteCustomElementAction(x, y, element, page);
10989   }
10990 }
10991
10992 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10993                                               int trigger_element,
10994                                               int trigger_event,
10995                                               int trigger_player,
10996                                               int trigger_side,
10997                                               int trigger_page)
10998 {
10999   boolean change_done_any = FALSE;
11000   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11001   int i;
11002
11003   if (!(trigger_events[trigger_element][trigger_event]))
11004     return FALSE;
11005
11006   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11007
11008   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11009   {
11010     int element = EL_CUSTOM_START + i;
11011     boolean change_done = FALSE;
11012     int p;
11013
11014     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11015         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11016       continue;
11017
11018     for (p = 0; p < element_info[element].num_change_pages; p++)
11019     {
11020       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11021
11022       if (change->can_change_or_has_action &&
11023           change->has_event[trigger_event] &&
11024           change->trigger_side & trigger_side &&
11025           change->trigger_player & trigger_player &&
11026           change->trigger_page & trigger_page_bits &&
11027           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11028       {
11029         change->actual_trigger_element = trigger_element;
11030         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11031         change->actual_trigger_player_bits = trigger_player;
11032         change->actual_trigger_side = trigger_side;
11033         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11034         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11035
11036         if ((change->can_change && !change_done) || change->has_action)
11037         {
11038           int x, y;
11039
11040           SCAN_PLAYFIELD(x, y)
11041           {
11042             if (Tile[x][y] == element)
11043             {
11044               if (change->can_change && !change_done)
11045               {
11046                 // if element already changed in this frame, not only prevent
11047                 // another element change (checked in ChangeElement()), but
11048                 // also prevent additional element actions for this element
11049
11050                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11051                     !level.use_action_after_change_bug)
11052                   continue;
11053
11054                 ChangeDelay[x][y] = 1;
11055                 ChangeEvent[x][y] = trigger_event;
11056
11057                 HandleElementChange(x, y, p);
11058               }
11059               else if (change->has_action)
11060               {
11061                 // if element already changed in this frame, not only prevent
11062                 // another element change (checked in ChangeElement()), but
11063                 // also prevent additional element actions for this element
11064
11065                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11066                     !level.use_action_after_change_bug)
11067                   continue;
11068
11069                 ExecuteCustomElementAction(x, y, element, p);
11070                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11071               }
11072             }
11073           }
11074
11075           if (change->can_change)
11076           {
11077             change_done = TRUE;
11078             change_done_any = TRUE;
11079           }
11080         }
11081       }
11082     }
11083   }
11084
11085   RECURSION_LOOP_DETECTION_END();
11086
11087   return change_done_any;
11088 }
11089
11090 static boolean CheckElementChangeExt(int x, int y,
11091                                      int element,
11092                                      int trigger_element,
11093                                      int trigger_event,
11094                                      int trigger_player,
11095                                      int trigger_side)
11096 {
11097   boolean change_done = FALSE;
11098   int p;
11099
11100   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11101       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11102     return FALSE;
11103
11104   if (Tile[x][y] == EL_BLOCKED)
11105   {
11106     Blocked2Moving(x, y, &x, &y);
11107     element = Tile[x][y];
11108   }
11109
11110   // check if element has already changed or is about to change after moving
11111   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11112        Tile[x][y] != element) ||
11113
11114       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11115        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11116         ChangePage[x][y] != -1)))
11117     return FALSE;
11118
11119   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11120
11121   for (p = 0; p < element_info[element].num_change_pages; p++)
11122   {
11123     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11124
11125     /* check trigger element for all events where the element that is checked
11126        for changing interacts with a directly adjacent element -- this is
11127        different to element changes that affect other elements to change on the
11128        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11129     boolean check_trigger_element =
11130       (trigger_event == CE_TOUCHING_X ||
11131        trigger_event == CE_HITTING_X ||
11132        trigger_event == CE_HIT_BY_X ||
11133        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11134
11135     if (change->can_change_or_has_action &&
11136         change->has_event[trigger_event] &&
11137         change->trigger_side & trigger_side &&
11138         change->trigger_player & trigger_player &&
11139         (!check_trigger_element ||
11140          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11141     {
11142       change->actual_trigger_element = trigger_element;
11143       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11144       change->actual_trigger_player_bits = trigger_player;
11145       change->actual_trigger_side = trigger_side;
11146       change->actual_trigger_ce_value = CustomValue[x][y];
11147       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11148
11149       // special case: trigger element not at (x,y) position for some events
11150       if (check_trigger_element)
11151       {
11152         static struct
11153         {
11154           int dx, dy;
11155         } move_xy[] =
11156           {
11157             {  0,  0 },
11158             { -1,  0 },
11159             { +1,  0 },
11160             {  0,  0 },
11161             {  0, -1 },
11162             {  0,  0 }, { 0, 0 }, { 0, 0 },
11163             {  0, +1 }
11164           };
11165
11166         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11167         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11168
11169         change->actual_trigger_ce_value = CustomValue[xx][yy];
11170         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11171       }
11172
11173       if (change->can_change && !change_done)
11174       {
11175         ChangeDelay[x][y] = 1;
11176         ChangeEvent[x][y] = trigger_event;
11177
11178         HandleElementChange(x, y, p);
11179
11180         change_done = TRUE;
11181       }
11182       else if (change->has_action)
11183       {
11184         ExecuteCustomElementAction(x, y, element, p);
11185         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11186       }
11187     }
11188   }
11189
11190   RECURSION_LOOP_DETECTION_END();
11191
11192   return change_done;
11193 }
11194
11195 static void PlayPlayerSound(struct PlayerInfo *player)
11196 {
11197   int jx = player->jx, jy = player->jy;
11198   int sound_element = player->artwork_element;
11199   int last_action = player->last_action_waiting;
11200   int action = player->action_waiting;
11201
11202   if (player->is_waiting)
11203   {
11204     if (action != last_action)
11205       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11206     else
11207       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11208   }
11209   else
11210   {
11211     if (action != last_action)
11212       StopSound(element_info[sound_element].sound[last_action]);
11213
11214     if (last_action == ACTION_SLEEPING)
11215       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11216   }
11217 }
11218
11219 static void PlayAllPlayersSound(void)
11220 {
11221   int i;
11222
11223   for (i = 0; i < MAX_PLAYERS; i++)
11224     if (stored_player[i].active)
11225       PlayPlayerSound(&stored_player[i]);
11226 }
11227
11228 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11229 {
11230   boolean last_waiting = player->is_waiting;
11231   int move_dir = player->MovDir;
11232
11233   player->dir_waiting = move_dir;
11234   player->last_action_waiting = player->action_waiting;
11235
11236   if (is_waiting)
11237   {
11238     if (!last_waiting)          // not waiting -> waiting
11239     {
11240       player->is_waiting = TRUE;
11241
11242       player->frame_counter_bored =
11243         FrameCounter +
11244         game.player_boring_delay_fixed +
11245         GetSimpleRandom(game.player_boring_delay_random);
11246       player->frame_counter_sleeping =
11247         FrameCounter +
11248         game.player_sleeping_delay_fixed +
11249         GetSimpleRandom(game.player_sleeping_delay_random);
11250
11251       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11252     }
11253
11254     if (game.player_sleeping_delay_fixed +
11255         game.player_sleeping_delay_random > 0 &&
11256         player->anim_delay_counter == 0 &&
11257         player->post_delay_counter == 0 &&
11258         FrameCounter >= player->frame_counter_sleeping)
11259       player->is_sleeping = TRUE;
11260     else if (game.player_boring_delay_fixed +
11261              game.player_boring_delay_random > 0 &&
11262              FrameCounter >= player->frame_counter_bored)
11263       player->is_bored = TRUE;
11264
11265     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11266                               player->is_bored ? ACTION_BORING :
11267                               ACTION_WAITING);
11268
11269     if (player->is_sleeping && player->use_murphy)
11270     {
11271       // special case for sleeping Murphy when leaning against non-free tile
11272
11273       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11274           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11275            !IS_MOVING(player->jx - 1, player->jy)))
11276         move_dir = MV_LEFT;
11277       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11278                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11279                 !IS_MOVING(player->jx + 1, player->jy)))
11280         move_dir = MV_RIGHT;
11281       else
11282         player->is_sleeping = FALSE;
11283
11284       player->dir_waiting = move_dir;
11285     }
11286
11287     if (player->is_sleeping)
11288     {
11289       if (player->num_special_action_sleeping > 0)
11290       {
11291         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11292         {
11293           int last_special_action = player->special_action_sleeping;
11294           int num_special_action = player->num_special_action_sleeping;
11295           int special_action =
11296             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11297              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11298              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11299              last_special_action + 1 : ACTION_SLEEPING);
11300           int special_graphic =
11301             el_act_dir2img(player->artwork_element, special_action, move_dir);
11302
11303           player->anim_delay_counter =
11304             graphic_info[special_graphic].anim_delay_fixed +
11305             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11306           player->post_delay_counter =
11307             graphic_info[special_graphic].post_delay_fixed +
11308             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11309
11310           player->special_action_sleeping = special_action;
11311         }
11312
11313         if (player->anim_delay_counter > 0)
11314         {
11315           player->action_waiting = player->special_action_sleeping;
11316           player->anim_delay_counter--;
11317         }
11318         else if (player->post_delay_counter > 0)
11319         {
11320           player->post_delay_counter--;
11321         }
11322       }
11323     }
11324     else if (player->is_bored)
11325     {
11326       if (player->num_special_action_bored > 0)
11327       {
11328         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11329         {
11330           int special_action =
11331             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11332           int special_graphic =
11333             el_act_dir2img(player->artwork_element, special_action, move_dir);
11334
11335           player->anim_delay_counter =
11336             graphic_info[special_graphic].anim_delay_fixed +
11337             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11338           player->post_delay_counter =
11339             graphic_info[special_graphic].post_delay_fixed +
11340             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11341
11342           player->special_action_bored = special_action;
11343         }
11344
11345         if (player->anim_delay_counter > 0)
11346         {
11347           player->action_waiting = player->special_action_bored;
11348           player->anim_delay_counter--;
11349         }
11350         else if (player->post_delay_counter > 0)
11351         {
11352           player->post_delay_counter--;
11353         }
11354       }
11355     }
11356   }
11357   else if (last_waiting)        // waiting -> not waiting
11358   {
11359     player->is_waiting = FALSE;
11360     player->is_bored = FALSE;
11361     player->is_sleeping = FALSE;
11362
11363     player->frame_counter_bored = -1;
11364     player->frame_counter_sleeping = -1;
11365
11366     player->anim_delay_counter = 0;
11367     player->post_delay_counter = 0;
11368
11369     player->dir_waiting = player->MovDir;
11370     player->action_waiting = ACTION_DEFAULT;
11371
11372     player->special_action_bored = ACTION_DEFAULT;
11373     player->special_action_sleeping = ACTION_DEFAULT;
11374   }
11375 }
11376
11377 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11378 {
11379   if ((!player->is_moving  && player->was_moving) ||
11380       (player->MovPos == 0 && player->was_moving) ||
11381       (player->is_snapping && !player->was_snapping) ||
11382       (player->is_dropping && !player->was_dropping))
11383   {
11384     if (!CheckSaveEngineSnapshotToList())
11385       return;
11386
11387     player->was_moving = FALSE;
11388     player->was_snapping = TRUE;
11389     player->was_dropping = TRUE;
11390   }
11391   else
11392   {
11393     if (player->is_moving)
11394       player->was_moving = TRUE;
11395
11396     if (!player->is_snapping)
11397       player->was_snapping = FALSE;
11398
11399     if (!player->is_dropping)
11400       player->was_dropping = FALSE;
11401   }
11402
11403   static struct MouseActionInfo mouse_action_last = { 0 };
11404   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11405   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11406
11407   if (new_released)
11408     CheckSaveEngineSnapshotToList();
11409
11410   mouse_action_last = mouse_action;
11411 }
11412
11413 static void CheckSingleStepMode(struct PlayerInfo *player)
11414 {
11415   if (tape.single_step && tape.recording && !tape.pausing)
11416   {
11417     // as it is called "single step mode", just return to pause mode when the
11418     // player stopped moving after one tile (or never starts moving at all)
11419     // (reverse logic needed here in case single step mode used in team mode)
11420     if (player->is_moving ||
11421         player->is_pushing ||
11422         player->is_dropping_pressed ||
11423         player->effective_mouse_action.button)
11424       game.enter_single_step_mode = FALSE;
11425   }
11426
11427   CheckSaveEngineSnapshot(player);
11428 }
11429
11430 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11431 {
11432   int left      = player_action & JOY_LEFT;
11433   int right     = player_action & JOY_RIGHT;
11434   int up        = player_action & JOY_UP;
11435   int down      = player_action & JOY_DOWN;
11436   int button1   = player_action & JOY_BUTTON_1;
11437   int button2   = player_action & JOY_BUTTON_2;
11438   int dx        = (left ? -1 : right ? 1 : 0);
11439   int dy        = (up   ? -1 : down  ? 1 : 0);
11440
11441   if (!player->active || tape.pausing)
11442     return 0;
11443
11444   if (player_action)
11445   {
11446     if (button1)
11447       SnapField(player, dx, dy);
11448     else
11449     {
11450       if (button2)
11451         DropElement(player);
11452
11453       MovePlayer(player, dx, dy);
11454     }
11455
11456     CheckSingleStepMode(player);
11457
11458     SetPlayerWaiting(player, FALSE);
11459
11460     return player_action;
11461   }
11462   else
11463   {
11464     // no actions for this player (no input at player's configured device)
11465
11466     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11467     SnapField(player, 0, 0);
11468     CheckGravityMovementWhenNotMoving(player);
11469
11470     if (player->MovPos == 0)
11471       SetPlayerWaiting(player, TRUE);
11472
11473     if (player->MovPos == 0)    // needed for tape.playing
11474       player->is_moving = FALSE;
11475
11476     player->is_dropping = FALSE;
11477     player->is_dropping_pressed = FALSE;
11478     player->drop_pressed_delay = 0;
11479
11480     CheckSingleStepMode(player);
11481
11482     return 0;
11483   }
11484 }
11485
11486 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11487                                          byte *tape_action)
11488 {
11489   if (!tape.use_mouse_actions)
11490     return;
11491
11492   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11493   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11494   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11495 }
11496
11497 static void SetTapeActionFromMouseAction(byte *tape_action,
11498                                          struct MouseActionInfo *mouse_action)
11499 {
11500   if (!tape.use_mouse_actions)
11501     return;
11502
11503   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11504   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11505   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11506 }
11507
11508 static void CheckLevelSolved(void)
11509 {
11510   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11511   {
11512     if (game_em.level_solved &&
11513         !game_em.game_over)                             // game won
11514     {
11515       LevelSolved();
11516
11517       game_em.game_over = TRUE;
11518
11519       game.all_players_gone = TRUE;
11520     }
11521
11522     if (game_em.game_over)                              // game lost
11523       game.all_players_gone = TRUE;
11524   }
11525   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11526   {
11527     if (game_sp.level_solved &&
11528         !game_sp.game_over)                             // game won
11529     {
11530       LevelSolved();
11531
11532       game_sp.game_over = TRUE;
11533
11534       game.all_players_gone = TRUE;
11535     }
11536
11537     if (game_sp.game_over)                              // game lost
11538       game.all_players_gone = TRUE;
11539   }
11540   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11541   {
11542     if (game_mm.level_solved &&
11543         !game_mm.game_over)                             // game won
11544     {
11545       LevelSolved();
11546
11547       game_mm.game_over = TRUE;
11548
11549       game.all_players_gone = TRUE;
11550     }
11551
11552     if (game_mm.game_over)                              // game lost
11553       game.all_players_gone = TRUE;
11554   }
11555 }
11556
11557 static void CheckLevelTime(void)
11558 {
11559   int i;
11560
11561   if (TimeFrames >= FRAMES_PER_SECOND)
11562   {
11563     TimeFrames = 0;
11564     TapeTime++;
11565
11566     for (i = 0; i < MAX_PLAYERS; i++)
11567     {
11568       struct PlayerInfo *player = &stored_player[i];
11569
11570       if (SHIELD_ON(player))
11571       {
11572         player->shield_normal_time_left--;
11573
11574         if (player->shield_deadly_time_left > 0)
11575           player->shield_deadly_time_left--;
11576       }
11577     }
11578
11579     if (!game.LevelSolved && !level.use_step_counter)
11580     {
11581       TimePlayed++;
11582
11583       if (TimeLeft > 0)
11584       {
11585         TimeLeft--;
11586
11587         if (TimeLeft <= 10 && setup.time_limit)
11588           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11589
11590         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11591            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11592
11593         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11594
11595         if (!TimeLeft && setup.time_limit)
11596         {
11597           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11598             game_em.lev->killed_out_of_time = TRUE;
11599           else
11600             for (i = 0; i < MAX_PLAYERS; i++)
11601               KillPlayer(&stored_player[i]);
11602         }
11603       }
11604       else if (game.no_time_limit && !game.all_players_gone)
11605       {
11606         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11607       }
11608
11609       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11610     }
11611
11612     if (tape.recording || tape.playing)
11613       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11614   }
11615
11616   if (tape.recording || tape.playing)
11617     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11618
11619   UpdateAndDisplayGameControlValues();
11620 }
11621
11622 void AdvanceFrameAndPlayerCounters(int player_nr)
11623 {
11624   int i;
11625
11626   // advance frame counters (global frame counter and time frame counter)
11627   FrameCounter++;
11628   TimeFrames++;
11629
11630   // advance player counters (counters for move delay, move animation etc.)
11631   for (i = 0; i < MAX_PLAYERS; i++)
11632   {
11633     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11634     int move_delay_value = stored_player[i].move_delay_value;
11635     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11636
11637     if (!advance_player_counters)       // not all players may be affected
11638       continue;
11639
11640     if (move_frames == 0)       // less than one move per game frame
11641     {
11642       int stepsize = TILEX / move_delay_value;
11643       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11644       int count = (stored_player[i].is_moving ?
11645                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11646
11647       if (count % delay == 0)
11648         move_frames = 1;
11649     }
11650
11651     stored_player[i].Frame += move_frames;
11652
11653     if (stored_player[i].MovPos != 0)
11654       stored_player[i].StepFrame += move_frames;
11655
11656     if (stored_player[i].move_delay > 0)
11657       stored_player[i].move_delay--;
11658
11659     // due to bugs in previous versions, counter must count up, not down
11660     if (stored_player[i].push_delay != -1)
11661       stored_player[i].push_delay++;
11662
11663     if (stored_player[i].drop_delay > 0)
11664       stored_player[i].drop_delay--;
11665
11666     if (stored_player[i].is_dropping_pressed)
11667       stored_player[i].drop_pressed_delay++;
11668   }
11669 }
11670
11671 void StartGameActions(boolean init_network_game, boolean record_tape,
11672                       int random_seed)
11673 {
11674   unsigned int new_random_seed = InitRND(random_seed);
11675
11676   if (record_tape)
11677     TapeStartRecording(new_random_seed);
11678
11679   if (init_network_game)
11680   {
11681     SendToServer_LevelFile();
11682     SendToServer_StartPlaying();
11683
11684     return;
11685   }
11686
11687   InitGame();
11688 }
11689
11690 static void GameActionsExt(void)
11691 {
11692 #if 0
11693   static unsigned int game_frame_delay = 0;
11694 #endif
11695   unsigned int game_frame_delay_value;
11696   byte *recorded_player_action;
11697   byte summarized_player_action = 0;
11698   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11699   int i;
11700
11701   // detect endless loops, caused by custom element programming
11702   if (recursion_loop_detected && recursion_loop_depth == 0)
11703   {
11704     char *message = getStringCat3("Internal Error! Element ",
11705                                   EL_NAME(recursion_loop_element),
11706                                   " caused endless loop! Quit the game?");
11707
11708     Warn("element '%s' caused endless loop in game engine",
11709          EL_NAME(recursion_loop_element));
11710
11711     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11712
11713     recursion_loop_detected = FALSE;    // if game should be continued
11714
11715     free(message);
11716
11717     return;
11718   }
11719
11720   if (game.restart_level)
11721     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11722
11723   CheckLevelSolved();
11724
11725   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11726     GameWon();
11727
11728   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11729     TapeStop();
11730
11731   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11732     return;
11733
11734   game_frame_delay_value =
11735     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11736
11737   if (tape.playing && tape.warp_forward && !tape.pausing)
11738     game_frame_delay_value = 0;
11739
11740   SetVideoFrameDelay(game_frame_delay_value);
11741
11742   // (de)activate virtual buttons depending on current game status
11743   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11744   {
11745     if (game.all_players_gone)  // if no players there to be controlled anymore
11746       SetOverlayActive(FALSE);
11747     else if (!tape.playing)     // if game continues after tape stopped playing
11748       SetOverlayActive(TRUE);
11749   }
11750
11751 #if 0
11752 #if 0
11753   // ---------- main game synchronization point ----------
11754
11755   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11756
11757   Debug("game:playing:skip", "skip == %d", skip);
11758
11759 #else
11760   // ---------- main game synchronization point ----------
11761
11762   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11763 #endif
11764 #endif
11765
11766   if (network_playing && !network_player_action_received)
11767   {
11768     // try to get network player actions in time
11769
11770     // last chance to get network player actions without main loop delay
11771     HandleNetworking();
11772
11773     // game was quit by network peer
11774     if (game_status != GAME_MODE_PLAYING)
11775       return;
11776
11777     // check if network player actions still missing and game still running
11778     if (!network_player_action_received && !checkGameEnded())
11779       return;           // failed to get network player actions in time
11780
11781     // do not yet reset "network_player_action_received" (for tape.pausing)
11782   }
11783
11784   if (tape.pausing)
11785     return;
11786
11787   // at this point we know that we really continue executing the game
11788
11789   network_player_action_received = FALSE;
11790
11791   // when playing tape, read previously recorded player input from tape data
11792   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11793
11794   local_player->effective_mouse_action = local_player->mouse_action;
11795
11796   if (recorded_player_action != NULL)
11797     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11798                                  recorded_player_action);
11799
11800   // TapePlayAction() may return NULL when toggling to "pause before death"
11801   if (tape.pausing)
11802     return;
11803
11804   if (tape.set_centered_player)
11805   {
11806     game.centered_player_nr_next = tape.centered_player_nr_next;
11807     game.set_centered_player = TRUE;
11808   }
11809
11810   for (i = 0; i < MAX_PLAYERS; i++)
11811   {
11812     summarized_player_action |= stored_player[i].action;
11813
11814     if (!network_playing && (game.team_mode || tape.playing))
11815       stored_player[i].effective_action = stored_player[i].action;
11816   }
11817
11818   if (network_playing && !checkGameEnded())
11819     SendToServer_MovePlayer(summarized_player_action);
11820
11821   // summarize all actions at local players mapped input device position
11822   // (this allows using different input devices in single player mode)
11823   if (!network.enabled && !game.team_mode)
11824     stored_player[map_player_action[local_player->index_nr]].effective_action =
11825       summarized_player_action;
11826
11827   // summarize all actions at centered player in local team mode
11828   if (tape.recording &&
11829       setup.team_mode && !network.enabled &&
11830       setup.input_on_focus &&
11831       game.centered_player_nr != -1)
11832   {
11833     for (i = 0; i < MAX_PLAYERS; i++)
11834       stored_player[map_player_action[i]].effective_action =
11835         (i == game.centered_player_nr ? summarized_player_action : 0);
11836   }
11837
11838   if (recorded_player_action != NULL)
11839     for (i = 0; i < MAX_PLAYERS; i++)
11840       stored_player[i].effective_action = recorded_player_action[i];
11841
11842   for (i = 0; i < MAX_PLAYERS; i++)
11843   {
11844     tape_action[i] = stored_player[i].effective_action;
11845
11846     /* (this may happen in the RND game engine if a player was not present on
11847        the playfield on level start, but appeared later from a custom element */
11848     if (setup.team_mode &&
11849         tape.recording &&
11850         tape_action[i] &&
11851         !tape.player_participates[i])
11852       tape.player_participates[i] = TRUE;
11853   }
11854
11855   SetTapeActionFromMouseAction(tape_action,
11856                                &local_player->effective_mouse_action);
11857
11858   // only record actions from input devices, but not programmed actions
11859   if (tape.recording)
11860     TapeRecordAction(tape_action);
11861
11862   // remember if game was played (especially after tape stopped playing)
11863   if (!tape.playing && summarized_player_action)
11864     game.GamePlayed = TRUE;
11865
11866 #if USE_NEW_PLAYER_ASSIGNMENTS
11867   // !!! also map player actions in single player mode !!!
11868   // if (game.team_mode)
11869   if (1)
11870   {
11871     byte mapped_action[MAX_PLAYERS];
11872
11873 #if DEBUG_PLAYER_ACTIONS
11874     for (i = 0; i < MAX_PLAYERS; i++)
11875       DebugContinued("", "%d, ", stored_player[i].effective_action);
11876 #endif
11877
11878     for (i = 0; i < MAX_PLAYERS; i++)
11879       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11880
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882       stored_player[i].effective_action = mapped_action[i];
11883
11884 #if DEBUG_PLAYER_ACTIONS
11885     DebugContinued("", "=> ");
11886     for (i = 0; i < MAX_PLAYERS; i++)
11887       DebugContinued("", "%d, ", stored_player[i].effective_action);
11888     DebugContinued("game:playing:player", "\n");
11889 #endif
11890   }
11891 #if DEBUG_PLAYER_ACTIONS
11892   else
11893   {
11894     for (i = 0; i < MAX_PLAYERS; i++)
11895       DebugContinued("", "%d, ", stored_player[i].effective_action);
11896     DebugContinued("game:playing:player", "\n");
11897   }
11898 #endif
11899 #endif
11900
11901   for (i = 0; i < MAX_PLAYERS; i++)
11902   {
11903     // allow engine snapshot in case of changed movement attempt
11904     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11905         (stored_player[i].effective_action & KEY_MOTION))
11906       game.snapshot.changed_action = TRUE;
11907
11908     // allow engine snapshot in case of snapping/dropping attempt
11909     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11910         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11911       game.snapshot.changed_action = TRUE;
11912
11913     game.snapshot.last_action[i] = stored_player[i].effective_action;
11914   }
11915
11916   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11917   {
11918     GameActions_EM_Main();
11919   }
11920   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11921   {
11922     GameActions_SP_Main();
11923   }
11924   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11925   {
11926     GameActions_MM_Main();
11927   }
11928   else
11929   {
11930     GameActions_RND_Main();
11931   }
11932
11933   BlitScreenToBitmap(backbuffer);
11934
11935   CheckLevelSolved();
11936   CheckLevelTime();
11937
11938   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11939
11940   if (global.show_frames_per_second)
11941   {
11942     static unsigned int fps_counter = 0;
11943     static int fps_frames = 0;
11944     unsigned int fps_delay_ms = Counter() - fps_counter;
11945
11946     fps_frames++;
11947
11948     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11949     {
11950       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11951
11952       fps_frames = 0;
11953       fps_counter = Counter();
11954
11955       // always draw FPS to screen after FPS value was updated
11956       redraw_mask |= REDRAW_FPS;
11957     }
11958
11959     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11960     if (GetDrawDeactivationMask() == REDRAW_NONE)
11961       redraw_mask |= REDRAW_FPS;
11962   }
11963 }
11964
11965 static void GameActions_CheckSaveEngineSnapshot(void)
11966 {
11967   if (!game.snapshot.save_snapshot)
11968     return;
11969
11970   // clear flag for saving snapshot _before_ saving snapshot
11971   game.snapshot.save_snapshot = FALSE;
11972
11973   SaveEngineSnapshotToList();
11974 }
11975
11976 void GameActions(void)
11977 {
11978   GameActionsExt();
11979
11980   GameActions_CheckSaveEngineSnapshot();
11981 }
11982
11983 void GameActions_EM_Main(void)
11984 {
11985   byte effective_action[MAX_PLAYERS];
11986   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11987   int i;
11988
11989   for (i = 0; i < MAX_PLAYERS; i++)
11990     effective_action[i] = stored_player[i].effective_action;
11991
11992   GameActions_EM(effective_action, warp_mode);
11993 }
11994
11995 void GameActions_SP_Main(void)
11996 {
11997   byte effective_action[MAX_PLAYERS];
11998   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11999   int i;
12000
12001   for (i = 0; i < MAX_PLAYERS; i++)
12002     effective_action[i] = stored_player[i].effective_action;
12003
12004   GameActions_SP(effective_action, warp_mode);
12005
12006   for (i = 0; i < MAX_PLAYERS; i++)
12007   {
12008     if (stored_player[i].force_dropping)
12009       stored_player[i].action |= KEY_BUTTON_DROP;
12010
12011     stored_player[i].force_dropping = FALSE;
12012   }
12013 }
12014
12015 void GameActions_MM_Main(void)
12016 {
12017   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12018
12019   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12020 }
12021
12022 void GameActions_RND_Main(void)
12023 {
12024   GameActions_RND();
12025 }
12026
12027 void GameActions_RND(void)
12028 {
12029   static struct MouseActionInfo mouse_action_last = { 0 };
12030   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12031   int magic_wall_x = 0, magic_wall_y = 0;
12032   int i, x, y, element, graphic, last_gfx_frame;
12033
12034   InitPlayfieldScanModeVars();
12035
12036   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12037   {
12038     SCAN_PLAYFIELD(x, y)
12039     {
12040       ChangeCount[x][y] = 0;
12041       ChangeEvent[x][y] = -1;
12042     }
12043   }
12044
12045   if (game.set_centered_player)
12046   {
12047     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12048
12049     // switching to "all players" only possible if all players fit to screen
12050     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12051     {
12052       game.centered_player_nr_next = game.centered_player_nr;
12053       game.set_centered_player = FALSE;
12054     }
12055
12056     // do not switch focus to non-existing (or non-active) player
12057     if (game.centered_player_nr_next >= 0 &&
12058         !stored_player[game.centered_player_nr_next].active)
12059     {
12060       game.centered_player_nr_next = game.centered_player_nr;
12061       game.set_centered_player = FALSE;
12062     }
12063   }
12064
12065   if (game.set_centered_player &&
12066       ScreenMovPos == 0)        // screen currently aligned at tile position
12067   {
12068     int sx, sy;
12069
12070     if (game.centered_player_nr_next == -1)
12071     {
12072       setScreenCenteredToAllPlayers(&sx, &sy);
12073     }
12074     else
12075     {
12076       sx = stored_player[game.centered_player_nr_next].jx;
12077       sy = stored_player[game.centered_player_nr_next].jy;
12078     }
12079
12080     game.centered_player_nr = game.centered_player_nr_next;
12081     game.set_centered_player = FALSE;
12082
12083     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12084     DrawGameDoorValues();
12085   }
12086
12087   // check single step mode (set flag and clear again if any player is active)
12088   game.enter_single_step_mode =
12089     (tape.single_step && tape.recording && !tape.pausing);
12090
12091   for (i = 0; i < MAX_PLAYERS; i++)
12092   {
12093     int actual_player_action = stored_player[i].effective_action;
12094
12095 #if 1
12096     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12097        - rnd_equinox_tetrachloride 048
12098        - rnd_equinox_tetrachloride_ii 096
12099        - rnd_emanuel_schmieg 002
12100        - doctor_sloan_ww 001, 020
12101     */
12102     if (stored_player[i].MovPos == 0)
12103       CheckGravityMovement(&stored_player[i]);
12104 #endif
12105
12106     // overwrite programmed action with tape action
12107     if (stored_player[i].programmed_action)
12108       actual_player_action = stored_player[i].programmed_action;
12109
12110     PlayerActions(&stored_player[i], actual_player_action);
12111
12112     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12113   }
12114
12115   // single step pause mode may already have been toggled by "ScrollPlayer()"
12116   if (game.enter_single_step_mode && !tape.pausing)
12117     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12118
12119   ScrollScreen(NULL, SCROLL_GO_ON);
12120
12121   /* for backwards compatibility, the following code emulates a fixed bug that
12122      occured when pushing elements (causing elements that just made their last
12123      pushing step to already (if possible) make their first falling step in the
12124      same game frame, which is bad); this code is also needed to use the famous
12125      "spring push bug" which is used in older levels and might be wanted to be
12126      used also in newer levels, but in this case the buggy pushing code is only
12127      affecting the "spring" element and no other elements */
12128
12129   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12130   {
12131     for (i = 0; i < MAX_PLAYERS; i++)
12132     {
12133       struct PlayerInfo *player = &stored_player[i];
12134       int x = player->jx;
12135       int y = player->jy;
12136
12137       if (player->active && player->is_pushing && player->is_moving &&
12138           IS_MOVING(x, y) &&
12139           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12140            Tile[x][y] == EL_SPRING))
12141       {
12142         ContinueMoving(x, y);
12143
12144         // continue moving after pushing (this is actually a bug)
12145         if (!IS_MOVING(x, y))
12146           Stop[x][y] = FALSE;
12147       }
12148     }
12149   }
12150
12151   SCAN_PLAYFIELD(x, y)
12152   {
12153     Last[x][y] = Tile[x][y];
12154
12155     ChangeCount[x][y] = 0;
12156     ChangeEvent[x][y] = -1;
12157
12158     // this must be handled before main playfield loop
12159     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12160     {
12161       MovDelay[x][y]--;
12162       if (MovDelay[x][y] <= 0)
12163         RemoveField(x, y);
12164     }
12165
12166     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12167     {
12168       MovDelay[x][y]--;
12169       if (MovDelay[x][y] <= 0)
12170       {
12171         int element = Store[x][y];
12172         int move_direction = MovDir[x][y];
12173         int player_index_bit = Store2[x][y];
12174
12175         Store[x][y] = 0;
12176         Store2[x][y] = 0;
12177
12178         RemoveField(x, y);
12179         TEST_DrawLevelField(x, y);
12180
12181         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12182
12183         if (IS_ENVELOPE(element))
12184           local_player->show_envelope = element;
12185       }
12186     }
12187
12188 #if DEBUG
12189     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12190     {
12191       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12192             x, y);
12193       Debug("game:playing:GameActions_RND", "This should never happen!");
12194
12195       ChangePage[x][y] = -1;
12196     }
12197 #endif
12198
12199     Stop[x][y] = FALSE;
12200     if (WasJustMoving[x][y] > 0)
12201       WasJustMoving[x][y]--;
12202     if (WasJustFalling[x][y] > 0)
12203       WasJustFalling[x][y]--;
12204     if (CheckCollision[x][y] > 0)
12205       CheckCollision[x][y]--;
12206     if (CheckImpact[x][y] > 0)
12207       CheckImpact[x][y]--;
12208
12209     GfxFrame[x][y]++;
12210
12211     /* reset finished pushing action (not done in ContinueMoving() to allow
12212        continuous pushing animation for elements with zero push delay) */
12213     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12214     {
12215       ResetGfxAnimation(x, y);
12216       TEST_DrawLevelField(x, y);
12217     }
12218
12219 #if DEBUG
12220     if (IS_BLOCKED(x, y))
12221     {
12222       int oldx, oldy;
12223
12224       Blocked2Moving(x, y, &oldx, &oldy);
12225       if (!IS_MOVING(oldx, oldy))
12226       {
12227         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12228         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12229         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12230         Debug("game:playing:GameActions_RND", "This should never happen!");
12231       }
12232     }
12233 #endif
12234   }
12235
12236   if (mouse_action.button)
12237   {
12238     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12239     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12240
12241     x = mouse_action.lx;
12242     y = mouse_action.ly;
12243     element = Tile[x][y];
12244
12245     if (new_button)
12246     {
12247       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12248       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12249                                          ch_button);
12250     }
12251
12252     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12253     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12254                                        ch_button);
12255   }
12256
12257   SCAN_PLAYFIELD(x, y)
12258   {
12259     element = Tile[x][y];
12260     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12261     last_gfx_frame = GfxFrame[x][y];
12262
12263     ResetGfxFrame(x, y);
12264
12265     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12266       DrawLevelGraphicAnimation(x, y, graphic);
12267
12268     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12269         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12270       ResetRandomAnimationValue(x, y);
12271
12272     SetRandomAnimationValue(x, y);
12273
12274     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12275
12276     if (IS_INACTIVE(element))
12277     {
12278       if (IS_ANIMATED(graphic))
12279         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12280
12281       continue;
12282     }
12283
12284     // this may take place after moving, so 'element' may have changed
12285     if (IS_CHANGING(x, y) &&
12286         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12287     {
12288       int page = element_info[element].event_page_nr[CE_DELAY];
12289
12290       HandleElementChange(x, y, page);
12291
12292       element = Tile[x][y];
12293       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12294     }
12295
12296     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12297     {
12298       StartMoving(x, y);
12299
12300       element = Tile[x][y];
12301       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12302
12303       if (IS_ANIMATED(graphic) &&
12304           !IS_MOVING(x, y) &&
12305           !Stop[x][y])
12306         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12307
12308       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12309         TEST_DrawTwinkleOnField(x, y);
12310     }
12311     else if (element == EL_ACID)
12312     {
12313       if (!Stop[x][y])
12314         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12315     }
12316     else if ((element == EL_EXIT_OPEN ||
12317               element == EL_EM_EXIT_OPEN ||
12318               element == EL_SP_EXIT_OPEN ||
12319               element == EL_STEEL_EXIT_OPEN ||
12320               element == EL_EM_STEEL_EXIT_OPEN ||
12321               element == EL_SP_TERMINAL ||
12322               element == EL_SP_TERMINAL_ACTIVE ||
12323               element == EL_EXTRA_TIME ||
12324               element == EL_SHIELD_NORMAL ||
12325               element == EL_SHIELD_DEADLY) &&
12326              IS_ANIMATED(graphic))
12327       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12328     else if (IS_MOVING(x, y))
12329       ContinueMoving(x, y);
12330     else if (IS_ACTIVE_BOMB(element))
12331       CheckDynamite(x, y);
12332     else if (element == EL_AMOEBA_GROWING)
12333       AmoebaGrowing(x, y);
12334     else if (element == EL_AMOEBA_SHRINKING)
12335       AmoebaShrinking(x, y);
12336
12337 #if !USE_NEW_AMOEBA_CODE
12338     else if (IS_AMOEBALIVE(element))
12339       AmoebaReproduce(x, y);
12340 #endif
12341
12342     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12343       Life(x, y);
12344     else if (element == EL_EXIT_CLOSED)
12345       CheckExit(x, y);
12346     else if (element == EL_EM_EXIT_CLOSED)
12347       CheckExitEM(x, y);
12348     else if (element == EL_STEEL_EXIT_CLOSED)
12349       CheckExitSteel(x, y);
12350     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12351       CheckExitSteelEM(x, y);
12352     else if (element == EL_SP_EXIT_CLOSED)
12353       CheckExitSP(x, y);
12354     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12355              element == EL_EXPANDABLE_STEELWALL_GROWING)
12356       MauerWaechst(x, y);
12357     else if (element == EL_EXPANDABLE_WALL ||
12358              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12359              element == EL_EXPANDABLE_WALL_VERTICAL ||
12360              element == EL_EXPANDABLE_WALL_ANY ||
12361              element == EL_BD_EXPANDABLE_WALL)
12362       MauerAbleger(x, y);
12363     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12364              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12365              element == EL_EXPANDABLE_STEELWALL_ANY)
12366       MauerAblegerStahl(x, y);
12367     else if (element == EL_FLAMES)
12368       CheckForDragon(x, y);
12369     else if (element == EL_EXPLOSION)
12370       ; // drawing of correct explosion animation is handled separately
12371     else if (element == EL_ELEMENT_SNAPPING ||
12372              element == EL_DIAGONAL_SHRINKING ||
12373              element == EL_DIAGONAL_GROWING)
12374     {
12375       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12376
12377       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12378     }
12379     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12380       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12381
12382     if (IS_BELT_ACTIVE(element))
12383       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12384
12385     if (game.magic_wall_active)
12386     {
12387       int jx = local_player->jx, jy = local_player->jy;
12388
12389       // play the element sound at the position nearest to the player
12390       if ((element == EL_MAGIC_WALL_FULL ||
12391            element == EL_MAGIC_WALL_ACTIVE ||
12392            element == EL_MAGIC_WALL_EMPTYING ||
12393            element == EL_BD_MAGIC_WALL_FULL ||
12394            element == EL_BD_MAGIC_WALL_ACTIVE ||
12395            element == EL_BD_MAGIC_WALL_EMPTYING ||
12396            element == EL_DC_MAGIC_WALL_FULL ||
12397            element == EL_DC_MAGIC_WALL_ACTIVE ||
12398            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12399           ABS(x - jx) + ABS(y - jy) <
12400           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12401       {
12402         magic_wall_x = x;
12403         magic_wall_y = y;
12404       }
12405     }
12406   }
12407
12408 #if USE_NEW_AMOEBA_CODE
12409   // new experimental amoeba growth stuff
12410   if (!(FrameCounter % 8))
12411   {
12412     static unsigned int random = 1684108901;
12413
12414     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12415     {
12416       x = RND(lev_fieldx);
12417       y = RND(lev_fieldy);
12418       element = Tile[x][y];
12419
12420       if (!IS_PLAYER(x,y) &&
12421           (element == EL_EMPTY ||
12422            CAN_GROW_INTO(element) ||
12423            element == EL_QUICKSAND_EMPTY ||
12424            element == EL_QUICKSAND_FAST_EMPTY ||
12425            element == EL_ACID_SPLASH_LEFT ||
12426            element == EL_ACID_SPLASH_RIGHT))
12427       {
12428         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12429             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12430             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12431             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12432           Tile[x][y] = EL_AMOEBA_DROP;
12433       }
12434
12435       random = random * 129 + 1;
12436     }
12437   }
12438 #endif
12439
12440   game.explosions_delayed = FALSE;
12441
12442   SCAN_PLAYFIELD(x, y)
12443   {
12444     element = Tile[x][y];
12445
12446     if (ExplodeField[x][y])
12447       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12448     else if (element == EL_EXPLOSION)
12449       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12450
12451     ExplodeField[x][y] = EX_TYPE_NONE;
12452   }
12453
12454   game.explosions_delayed = TRUE;
12455
12456   if (game.magic_wall_active)
12457   {
12458     if (!(game.magic_wall_time_left % 4))
12459     {
12460       int element = Tile[magic_wall_x][magic_wall_y];
12461
12462       if (element == EL_BD_MAGIC_WALL_FULL ||
12463           element == EL_BD_MAGIC_WALL_ACTIVE ||
12464           element == EL_BD_MAGIC_WALL_EMPTYING)
12465         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12466       else if (element == EL_DC_MAGIC_WALL_FULL ||
12467                element == EL_DC_MAGIC_WALL_ACTIVE ||
12468                element == EL_DC_MAGIC_WALL_EMPTYING)
12469         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12470       else
12471         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12472     }
12473
12474     if (game.magic_wall_time_left > 0)
12475     {
12476       game.magic_wall_time_left--;
12477
12478       if (!game.magic_wall_time_left)
12479       {
12480         SCAN_PLAYFIELD(x, y)
12481         {
12482           element = Tile[x][y];
12483
12484           if (element == EL_MAGIC_WALL_ACTIVE ||
12485               element == EL_MAGIC_WALL_FULL)
12486           {
12487             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12488             TEST_DrawLevelField(x, y);
12489           }
12490           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12491                    element == EL_BD_MAGIC_WALL_FULL)
12492           {
12493             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12494             TEST_DrawLevelField(x, y);
12495           }
12496           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12497                    element == EL_DC_MAGIC_WALL_FULL)
12498           {
12499             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12500             TEST_DrawLevelField(x, y);
12501           }
12502         }
12503
12504         game.magic_wall_active = FALSE;
12505       }
12506     }
12507   }
12508
12509   if (game.light_time_left > 0)
12510   {
12511     game.light_time_left--;
12512
12513     if (game.light_time_left == 0)
12514       RedrawAllLightSwitchesAndInvisibleElements();
12515   }
12516
12517   if (game.timegate_time_left > 0)
12518   {
12519     game.timegate_time_left--;
12520
12521     if (game.timegate_time_left == 0)
12522       CloseAllOpenTimegates();
12523   }
12524
12525   if (game.lenses_time_left > 0)
12526   {
12527     game.lenses_time_left--;
12528
12529     if (game.lenses_time_left == 0)
12530       RedrawAllInvisibleElementsForLenses();
12531   }
12532
12533   if (game.magnify_time_left > 0)
12534   {
12535     game.magnify_time_left--;
12536
12537     if (game.magnify_time_left == 0)
12538       RedrawAllInvisibleElementsForMagnifier();
12539   }
12540
12541   for (i = 0; i < MAX_PLAYERS; i++)
12542   {
12543     struct PlayerInfo *player = &stored_player[i];
12544
12545     if (SHIELD_ON(player))
12546     {
12547       if (player->shield_deadly_time_left)
12548         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12549       else if (player->shield_normal_time_left)
12550         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12551     }
12552   }
12553
12554 #if USE_DELAYED_GFX_REDRAW
12555   SCAN_PLAYFIELD(x, y)
12556   {
12557     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12558     {
12559       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12560          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12561
12562       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12563         DrawLevelField(x, y);
12564
12565       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12566         DrawLevelFieldCrumbled(x, y);
12567
12568       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12569         DrawLevelFieldCrumbledNeighbours(x, y);
12570
12571       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12572         DrawTwinkleOnField(x, y);
12573     }
12574
12575     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12576   }
12577 #endif
12578
12579   DrawAllPlayers();
12580   PlayAllPlayersSound();
12581
12582   for (i = 0; i < MAX_PLAYERS; i++)
12583   {
12584     struct PlayerInfo *player = &stored_player[i];
12585
12586     if (player->show_envelope != 0 && (!player->active ||
12587                                        player->MovPos == 0))
12588     {
12589       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12590
12591       player->show_envelope = 0;
12592     }
12593   }
12594
12595   // use random number generator in every frame to make it less predictable
12596   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12597     RND(1);
12598
12599   mouse_action_last = mouse_action;
12600 }
12601
12602 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12603 {
12604   int min_x = x, min_y = y, max_x = x, max_y = y;
12605   int scr_fieldx = getScreenFieldSizeX();
12606   int scr_fieldy = getScreenFieldSizeY();
12607   int i;
12608
12609   for (i = 0; i < MAX_PLAYERS; i++)
12610   {
12611     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12612
12613     if (!stored_player[i].active || &stored_player[i] == player)
12614       continue;
12615
12616     min_x = MIN(min_x, jx);
12617     min_y = MIN(min_y, jy);
12618     max_x = MAX(max_x, jx);
12619     max_y = MAX(max_y, jy);
12620   }
12621
12622   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12623 }
12624
12625 static boolean AllPlayersInVisibleScreen(void)
12626 {
12627   int i;
12628
12629   for (i = 0; i < MAX_PLAYERS; i++)
12630   {
12631     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12632
12633     if (!stored_player[i].active)
12634       continue;
12635
12636     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12637       return FALSE;
12638   }
12639
12640   return TRUE;
12641 }
12642
12643 void ScrollLevel(int dx, int dy)
12644 {
12645   int scroll_offset = 2 * TILEX_VAR;
12646   int x, y;
12647
12648   BlitBitmap(drawto_field, drawto_field,
12649              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12650              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12651              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12652              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12653              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12654              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12655
12656   if (dx != 0)
12657   {
12658     x = (dx == 1 ? BX1 : BX2);
12659     for (y = BY1; y <= BY2; y++)
12660       DrawScreenField(x, y);
12661   }
12662
12663   if (dy != 0)
12664   {
12665     y = (dy == 1 ? BY1 : BY2);
12666     for (x = BX1; x <= BX2; x++)
12667       DrawScreenField(x, y);
12668   }
12669
12670   redraw_mask |= REDRAW_FIELD;
12671 }
12672
12673 static boolean canFallDown(struct PlayerInfo *player)
12674 {
12675   int jx = player->jx, jy = player->jy;
12676
12677   return (IN_LEV_FIELD(jx, jy + 1) &&
12678           (IS_FREE(jx, jy + 1) ||
12679            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12680           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12681           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12682 }
12683
12684 static boolean canPassField(int x, int y, int move_dir)
12685 {
12686   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12687   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12688   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12689   int nextx = x + dx;
12690   int nexty = y + dy;
12691   int element = Tile[x][y];
12692
12693   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12694           !CAN_MOVE(element) &&
12695           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12696           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12697           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12698 }
12699
12700 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12701 {
12702   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12703   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12704   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12705   int newx = x + dx;
12706   int newy = y + dy;
12707
12708   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12709           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12710           (IS_DIGGABLE(Tile[newx][newy]) ||
12711            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12712            canPassField(newx, newy, move_dir)));
12713 }
12714
12715 static void CheckGravityMovement(struct PlayerInfo *player)
12716 {
12717   if (player->gravity && !player->programmed_action)
12718   {
12719     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12720     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12721     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12722     int jx = player->jx, jy = player->jy;
12723     boolean player_is_moving_to_valid_field =
12724       (!player_is_snapping &&
12725        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12726         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12727     boolean player_can_fall_down = canFallDown(player);
12728
12729     if (player_can_fall_down &&
12730         !player_is_moving_to_valid_field)
12731       player->programmed_action = MV_DOWN;
12732   }
12733 }
12734
12735 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12736 {
12737   return CheckGravityMovement(player);
12738
12739   if (player->gravity && !player->programmed_action)
12740   {
12741     int jx = player->jx, jy = player->jy;
12742     boolean field_under_player_is_free =
12743       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12744     boolean player_is_standing_on_valid_field =
12745       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12746        (IS_WALKABLE(Tile[jx][jy]) &&
12747         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12748
12749     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12750       player->programmed_action = MV_DOWN;
12751   }
12752 }
12753
12754 /*
12755   MovePlayerOneStep()
12756   -----------------------------------------------------------------------------
12757   dx, dy:               direction (non-diagonal) to try to move the player to
12758   real_dx, real_dy:     direction as read from input device (can be diagonal)
12759 */
12760
12761 boolean MovePlayerOneStep(struct PlayerInfo *player,
12762                           int dx, int dy, int real_dx, int real_dy)
12763 {
12764   int jx = player->jx, jy = player->jy;
12765   int new_jx = jx + dx, new_jy = jy + dy;
12766   int can_move;
12767   boolean player_can_move = !player->cannot_move;
12768
12769   if (!player->active || (!dx && !dy))
12770     return MP_NO_ACTION;
12771
12772   player->MovDir = (dx < 0 ? MV_LEFT :
12773                     dx > 0 ? MV_RIGHT :
12774                     dy < 0 ? MV_UP :
12775                     dy > 0 ? MV_DOWN :  MV_NONE);
12776
12777   if (!IN_LEV_FIELD(new_jx, new_jy))
12778     return MP_NO_ACTION;
12779
12780   if (!player_can_move)
12781   {
12782     if (player->MovPos == 0)
12783     {
12784       player->is_moving = FALSE;
12785       player->is_digging = FALSE;
12786       player->is_collecting = FALSE;
12787       player->is_snapping = FALSE;
12788       player->is_pushing = FALSE;
12789     }
12790   }
12791
12792   if (!network.enabled && game.centered_player_nr == -1 &&
12793       !AllPlayersInSight(player, new_jx, new_jy))
12794     return MP_NO_ACTION;
12795
12796   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12797   if (can_move != MP_MOVING)
12798     return can_move;
12799
12800   // check if DigField() has caused relocation of the player
12801   if (player->jx != jx || player->jy != jy)
12802     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12803
12804   StorePlayer[jx][jy] = 0;
12805   player->last_jx = jx;
12806   player->last_jy = jy;
12807   player->jx = new_jx;
12808   player->jy = new_jy;
12809   StorePlayer[new_jx][new_jy] = player->element_nr;
12810
12811   if (player->move_delay_value_next != -1)
12812   {
12813     player->move_delay_value = player->move_delay_value_next;
12814     player->move_delay_value_next = -1;
12815   }
12816
12817   player->MovPos =
12818     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12819
12820   player->step_counter++;
12821
12822   PlayerVisit[jx][jy] = FrameCounter;
12823
12824   player->is_moving = TRUE;
12825
12826 #if 1
12827   // should better be called in MovePlayer(), but this breaks some tapes
12828   ScrollPlayer(player, SCROLL_INIT);
12829 #endif
12830
12831   return MP_MOVING;
12832 }
12833
12834 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12835 {
12836   int jx = player->jx, jy = player->jy;
12837   int old_jx = jx, old_jy = jy;
12838   int moved = MP_NO_ACTION;
12839
12840   if (!player->active)
12841     return FALSE;
12842
12843   if (!dx && !dy)
12844   {
12845     if (player->MovPos == 0)
12846     {
12847       player->is_moving = FALSE;
12848       player->is_digging = FALSE;
12849       player->is_collecting = FALSE;
12850       player->is_snapping = FALSE;
12851       player->is_pushing = FALSE;
12852     }
12853
12854     return FALSE;
12855   }
12856
12857   if (player->move_delay > 0)
12858     return FALSE;
12859
12860   player->move_delay = -1;              // set to "uninitialized" value
12861
12862   // store if player is automatically moved to next field
12863   player->is_auto_moving = (player->programmed_action != MV_NONE);
12864
12865   // remove the last programmed player action
12866   player->programmed_action = 0;
12867
12868   if (player->MovPos)
12869   {
12870     // should only happen if pre-1.2 tape recordings are played
12871     // this is only for backward compatibility
12872
12873     int original_move_delay_value = player->move_delay_value;
12874
12875 #if DEBUG
12876     Debug("game:playing:MovePlayer",
12877           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12878           tape.counter);
12879 #endif
12880
12881     // scroll remaining steps with finest movement resolution
12882     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12883
12884     while (player->MovPos)
12885     {
12886       ScrollPlayer(player, SCROLL_GO_ON);
12887       ScrollScreen(NULL, SCROLL_GO_ON);
12888
12889       AdvanceFrameAndPlayerCounters(player->index_nr);
12890
12891       DrawAllPlayers();
12892       BackToFront_WithFrameDelay(0);
12893     }
12894
12895     player->move_delay_value = original_move_delay_value;
12896   }
12897
12898   player->is_active = FALSE;
12899
12900   if (player->last_move_dir & MV_HORIZONTAL)
12901   {
12902     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12903       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12904   }
12905   else
12906   {
12907     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12908       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12909   }
12910
12911   if (!moved && !player->is_active)
12912   {
12913     player->is_moving = FALSE;
12914     player->is_digging = FALSE;
12915     player->is_collecting = FALSE;
12916     player->is_snapping = FALSE;
12917     player->is_pushing = FALSE;
12918   }
12919
12920   jx = player->jx;
12921   jy = player->jy;
12922
12923   if (moved & MP_MOVING && !ScreenMovPos &&
12924       (player->index_nr == game.centered_player_nr ||
12925        game.centered_player_nr == -1))
12926   {
12927     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12928
12929     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12930     {
12931       // actual player has left the screen -- scroll in that direction
12932       if (jx != old_jx)         // player has moved horizontally
12933         scroll_x += (jx - old_jx);
12934       else                      // player has moved vertically
12935         scroll_y += (jy - old_jy);
12936     }
12937     else
12938     {
12939       int offset_raw = game.scroll_delay_value;
12940
12941       if (jx != old_jx)         // player has moved horizontally
12942       {
12943         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12944         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12945         int new_scroll_x = jx - MIDPOSX + offset_x;
12946
12947         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12948             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12949           scroll_x = new_scroll_x;
12950
12951         // don't scroll over playfield boundaries
12952         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12953
12954         // don't scroll more than one field at a time
12955         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12956
12957         // don't scroll against the player's moving direction
12958         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12959             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12960           scroll_x = old_scroll_x;
12961       }
12962       else                      // player has moved vertically
12963       {
12964         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12965         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12966         int new_scroll_y = jy - MIDPOSY + offset_y;
12967
12968         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12969             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12970           scroll_y = new_scroll_y;
12971
12972         // don't scroll over playfield boundaries
12973         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12974
12975         // don't scroll more than one field at a time
12976         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12977
12978         // don't scroll against the player's moving direction
12979         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12980             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12981           scroll_y = old_scroll_y;
12982       }
12983     }
12984
12985     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12986     {
12987       if (!network.enabled && game.centered_player_nr == -1 &&
12988           !AllPlayersInVisibleScreen())
12989       {
12990         scroll_x = old_scroll_x;
12991         scroll_y = old_scroll_y;
12992       }
12993       else
12994       {
12995         ScrollScreen(player, SCROLL_INIT);
12996         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12997       }
12998     }
12999   }
13000
13001   player->StepFrame = 0;
13002
13003   if (moved & MP_MOVING)
13004   {
13005     if (old_jx != jx && old_jy == jy)
13006       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13007     else if (old_jx == jx && old_jy != jy)
13008       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13009
13010     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13011
13012     player->last_move_dir = player->MovDir;
13013     player->is_moving = TRUE;
13014     player->is_snapping = FALSE;
13015     player->is_switching = FALSE;
13016     player->is_dropping = FALSE;
13017     player->is_dropping_pressed = FALSE;
13018     player->drop_pressed_delay = 0;
13019
13020 #if 0
13021     // should better be called here than above, but this breaks some tapes
13022     ScrollPlayer(player, SCROLL_INIT);
13023 #endif
13024   }
13025   else
13026   {
13027     CheckGravityMovementWhenNotMoving(player);
13028
13029     player->is_moving = FALSE;
13030
13031     /* at this point, the player is allowed to move, but cannot move right now
13032        (e.g. because of something blocking the way) -- ensure that the player
13033        is also allowed to move in the next frame (in old versions before 3.1.1,
13034        the player was forced to wait again for eight frames before next try) */
13035
13036     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13037       player->move_delay = 0;   // allow direct movement in the next frame
13038   }
13039
13040   if (player->move_delay == -1)         // not yet initialized by DigField()
13041     player->move_delay = player->move_delay_value;
13042
13043   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13044   {
13045     TestIfPlayerTouchesBadThing(jx, jy);
13046     TestIfPlayerTouchesCustomElement(jx, jy);
13047   }
13048
13049   if (!player->active)
13050     RemovePlayer(player);
13051
13052   return moved;
13053 }
13054
13055 void ScrollPlayer(struct PlayerInfo *player, int mode)
13056 {
13057   int jx = player->jx, jy = player->jy;
13058   int last_jx = player->last_jx, last_jy = player->last_jy;
13059   int move_stepsize = TILEX / player->move_delay_value;
13060
13061   if (!player->active)
13062     return;
13063
13064   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13065     return;
13066
13067   if (mode == SCROLL_INIT)
13068   {
13069     player->actual_frame_counter = FrameCounter;
13070     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13071
13072     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13073         Tile[last_jx][last_jy] == EL_EMPTY)
13074     {
13075       int last_field_block_delay = 0;   // start with no blocking at all
13076       int block_delay_adjustment = player->block_delay_adjustment;
13077
13078       // if player blocks last field, add delay for exactly one move
13079       if (player->block_last_field)
13080       {
13081         last_field_block_delay += player->move_delay_value;
13082
13083         // when blocking enabled, prevent moving up despite gravity
13084         if (player->gravity && player->MovDir == MV_UP)
13085           block_delay_adjustment = -1;
13086       }
13087
13088       // add block delay adjustment (also possible when not blocking)
13089       last_field_block_delay += block_delay_adjustment;
13090
13091       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13092       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13093     }
13094
13095     if (player->MovPos != 0)    // player has not yet reached destination
13096       return;
13097   }
13098   else if (!FrameReached(&player->actual_frame_counter, 1))
13099     return;
13100
13101   if (player->MovPos != 0)
13102   {
13103     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13104     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13105
13106     // before DrawPlayer() to draw correct player graphic for this case
13107     if (player->MovPos == 0)
13108       CheckGravityMovement(player);
13109   }
13110
13111   if (player->MovPos == 0)      // player reached destination field
13112   {
13113     if (player->move_delay_reset_counter > 0)
13114     {
13115       player->move_delay_reset_counter--;
13116
13117       if (player->move_delay_reset_counter == 0)
13118       {
13119         // continue with normal speed after quickly moving through gate
13120         HALVE_PLAYER_SPEED(player);
13121
13122         // be able to make the next move without delay
13123         player->move_delay = 0;
13124       }
13125     }
13126
13127     player->last_jx = jx;
13128     player->last_jy = jy;
13129
13130     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13131         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13132         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13133         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13134         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13135         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13136         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13137         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13138     {
13139       ExitPlayer(player);
13140
13141       if (game.players_still_needed == 0 &&
13142           (game.friends_still_needed == 0 ||
13143            IS_SP_ELEMENT(Tile[jx][jy])))
13144         LevelSolved();
13145     }
13146
13147     // this breaks one level: "machine", level 000
13148     {
13149       int move_direction = player->MovDir;
13150       int enter_side = MV_DIR_OPPOSITE(move_direction);
13151       int leave_side = move_direction;
13152       int old_jx = last_jx;
13153       int old_jy = last_jy;
13154       int old_element = Tile[old_jx][old_jy];
13155       int new_element = Tile[jx][jy];
13156
13157       if (IS_CUSTOM_ELEMENT(old_element))
13158         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13159                                    CE_LEFT_BY_PLAYER,
13160                                    player->index_bit, leave_side);
13161
13162       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13163                                           CE_PLAYER_LEAVES_X,
13164                                           player->index_bit, leave_side);
13165
13166       if (IS_CUSTOM_ELEMENT(new_element))
13167         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13168                                    player->index_bit, enter_side);
13169
13170       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13171                                           CE_PLAYER_ENTERS_X,
13172                                           player->index_bit, enter_side);
13173
13174       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13175                                         CE_MOVE_OF_X, move_direction);
13176     }
13177
13178     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13179     {
13180       TestIfPlayerTouchesBadThing(jx, jy);
13181       TestIfPlayerTouchesCustomElement(jx, jy);
13182
13183       /* needed because pushed element has not yet reached its destination,
13184          so it would trigger a change event at its previous field location */
13185       if (!player->is_pushing)
13186         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13187
13188       if (level.finish_dig_collect &&
13189           (player->is_digging || player->is_collecting))
13190       {
13191         int last_element = player->last_removed_element;
13192         int move_direction = player->MovDir;
13193         int enter_side = MV_DIR_OPPOSITE(move_direction);
13194         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13195                             CE_PLAYER_COLLECTS_X);
13196
13197         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13198                                             player->index_bit, enter_side);
13199
13200         player->last_removed_element = EL_UNDEFINED;
13201       }
13202
13203       if (!player->active)
13204         RemovePlayer(player);
13205     }
13206
13207     if (level.use_step_counter)
13208     {
13209       int i;
13210
13211       TimePlayed++;
13212
13213       if (TimeLeft > 0)
13214       {
13215         TimeLeft--;
13216
13217         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13218           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13219
13220         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13221
13222         DisplayGameControlValues();
13223
13224         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13225           for (i = 0; i < MAX_PLAYERS; i++)
13226             KillPlayer(&stored_player[i]);
13227       }
13228       else if (game.no_time_limit && !game.all_players_gone)
13229       {
13230         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13231
13232         DisplayGameControlValues();
13233       }
13234     }
13235
13236     if (tape.single_step && tape.recording && !tape.pausing &&
13237         !player->programmed_action)
13238       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13239
13240     if (!player->programmed_action)
13241       CheckSaveEngineSnapshot(player);
13242   }
13243 }
13244
13245 void ScrollScreen(struct PlayerInfo *player, int mode)
13246 {
13247   static unsigned int screen_frame_counter = 0;
13248
13249   if (mode == SCROLL_INIT)
13250   {
13251     // set scrolling step size according to actual player's moving speed
13252     ScrollStepSize = TILEX / player->move_delay_value;
13253
13254     screen_frame_counter = FrameCounter;
13255     ScreenMovDir = player->MovDir;
13256     ScreenMovPos = player->MovPos;
13257     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13258     return;
13259   }
13260   else if (!FrameReached(&screen_frame_counter, 1))
13261     return;
13262
13263   if (ScreenMovPos)
13264   {
13265     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13266     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13267     redraw_mask |= REDRAW_FIELD;
13268   }
13269   else
13270     ScreenMovDir = MV_NONE;
13271 }
13272
13273 void TestIfPlayerTouchesCustomElement(int x, int y)
13274 {
13275   static int xy[4][2] =
13276   {
13277     { 0, -1 },
13278     { -1, 0 },
13279     { +1, 0 },
13280     { 0, +1 }
13281   };
13282   static int trigger_sides[4][2] =
13283   {
13284     // center side       border side
13285     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13286     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13287     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13288     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13289   };
13290   static int touch_dir[4] =
13291   {
13292     MV_LEFT | MV_RIGHT,
13293     MV_UP   | MV_DOWN,
13294     MV_UP   | MV_DOWN,
13295     MV_LEFT | MV_RIGHT
13296   };
13297   int center_element = Tile[x][y];      // should always be non-moving!
13298   int i;
13299
13300   for (i = 0; i < NUM_DIRECTIONS; i++)
13301   {
13302     int xx = x + xy[i][0];
13303     int yy = y + xy[i][1];
13304     int center_side = trigger_sides[i][0];
13305     int border_side = trigger_sides[i][1];
13306     int border_element;
13307
13308     if (!IN_LEV_FIELD(xx, yy))
13309       continue;
13310
13311     if (IS_PLAYER(x, y))                // player found at center element
13312     {
13313       struct PlayerInfo *player = PLAYERINFO(x, y);
13314
13315       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13316         border_element = Tile[xx][yy];          // may be moving!
13317       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13318         border_element = Tile[xx][yy];
13319       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13320         border_element = MovingOrBlocked2Element(xx, yy);
13321       else
13322         continue;               // center and border element do not touch
13323
13324       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13325                                  player->index_bit, border_side);
13326       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13327                                           CE_PLAYER_TOUCHES_X,
13328                                           player->index_bit, border_side);
13329
13330       {
13331         /* use player element that is initially defined in the level playfield,
13332            not the player element that corresponds to the runtime player number
13333            (example: a level that contains EL_PLAYER_3 as the only player would
13334            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13335         int player_element = PLAYERINFO(x, y)->initial_element;
13336
13337         CheckElementChangeBySide(xx, yy, border_element, player_element,
13338                                  CE_TOUCHING_X, border_side);
13339       }
13340     }
13341     else if (IS_PLAYER(xx, yy))         // player found at border element
13342     {
13343       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13344
13345       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13346       {
13347         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13348           continue;             // center and border element do not touch
13349       }
13350
13351       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13352                                  player->index_bit, center_side);
13353       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13354                                           CE_PLAYER_TOUCHES_X,
13355                                           player->index_bit, center_side);
13356
13357       {
13358         /* use player element that is initially defined in the level playfield,
13359            not the player element that corresponds to the runtime player number
13360            (example: a level that contains EL_PLAYER_3 as the only player would
13361            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13362         int player_element = PLAYERINFO(xx, yy)->initial_element;
13363
13364         CheckElementChangeBySide(x, y, center_element, player_element,
13365                                  CE_TOUCHING_X, center_side);
13366       }
13367
13368       break;
13369     }
13370   }
13371 }
13372
13373 void TestIfElementTouchesCustomElement(int x, int y)
13374 {
13375   static int xy[4][2] =
13376   {
13377     { 0, -1 },
13378     { -1, 0 },
13379     { +1, 0 },
13380     { 0, +1 }
13381   };
13382   static int trigger_sides[4][2] =
13383   {
13384     // center side      border side
13385     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13386     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13387     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13388     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13389   };
13390   static int touch_dir[4] =
13391   {
13392     MV_LEFT | MV_RIGHT,
13393     MV_UP   | MV_DOWN,
13394     MV_UP   | MV_DOWN,
13395     MV_LEFT | MV_RIGHT
13396   };
13397   boolean change_center_element = FALSE;
13398   int center_element = Tile[x][y];      // should always be non-moving!
13399   int border_element_old[NUM_DIRECTIONS];
13400   int i;
13401
13402   for (i = 0; i < NUM_DIRECTIONS; i++)
13403   {
13404     int xx = x + xy[i][0];
13405     int yy = y + xy[i][1];
13406     int border_element;
13407
13408     border_element_old[i] = -1;
13409
13410     if (!IN_LEV_FIELD(xx, yy))
13411       continue;
13412
13413     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13414       border_element = Tile[xx][yy];    // may be moving!
13415     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13416       border_element = Tile[xx][yy];
13417     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13418       border_element = MovingOrBlocked2Element(xx, yy);
13419     else
13420       continue;                 // center and border element do not touch
13421
13422     border_element_old[i] = border_element;
13423   }
13424
13425   for (i = 0; i < NUM_DIRECTIONS; i++)
13426   {
13427     int xx = x + xy[i][0];
13428     int yy = y + xy[i][1];
13429     int center_side = trigger_sides[i][0];
13430     int border_element = border_element_old[i];
13431
13432     if (border_element == -1)
13433       continue;
13434
13435     // check for change of border element
13436     CheckElementChangeBySide(xx, yy, border_element, center_element,
13437                              CE_TOUCHING_X, center_side);
13438
13439     // (center element cannot be player, so we dont have to check this here)
13440   }
13441
13442   for (i = 0; i < NUM_DIRECTIONS; i++)
13443   {
13444     int xx = x + xy[i][0];
13445     int yy = y + xy[i][1];
13446     int border_side = trigger_sides[i][1];
13447     int border_element = border_element_old[i];
13448
13449     if (border_element == -1)
13450       continue;
13451
13452     // check for change of center element (but change it only once)
13453     if (!change_center_element)
13454       change_center_element =
13455         CheckElementChangeBySide(x, y, center_element, border_element,
13456                                  CE_TOUCHING_X, border_side);
13457
13458     if (IS_PLAYER(xx, yy))
13459     {
13460       /* use player element that is initially defined in the level playfield,
13461          not the player element that corresponds to the runtime player number
13462          (example: a level that contains EL_PLAYER_3 as the only player would
13463          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13464       int player_element = PLAYERINFO(xx, yy)->initial_element;
13465
13466       CheckElementChangeBySide(x, y, center_element, player_element,
13467                                CE_TOUCHING_X, border_side);
13468     }
13469   }
13470 }
13471
13472 void TestIfElementHitsCustomElement(int x, int y, int direction)
13473 {
13474   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13475   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13476   int hitx = x + dx, hity = y + dy;
13477   int hitting_element = Tile[x][y];
13478   int touched_element;
13479
13480   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13481     return;
13482
13483   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13484                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13485
13486   if (IN_LEV_FIELD(hitx, hity))
13487   {
13488     int opposite_direction = MV_DIR_OPPOSITE(direction);
13489     int hitting_side = direction;
13490     int touched_side = opposite_direction;
13491     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13492                           MovDir[hitx][hity] != direction ||
13493                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13494
13495     object_hit = TRUE;
13496
13497     if (object_hit)
13498     {
13499       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13500                                CE_HITTING_X, touched_side);
13501
13502       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13503                                CE_HIT_BY_X, hitting_side);
13504
13505       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13506                                CE_HIT_BY_SOMETHING, opposite_direction);
13507
13508       if (IS_PLAYER(hitx, hity))
13509       {
13510         /* use player element that is initially defined in the level playfield,
13511            not the player element that corresponds to the runtime player number
13512            (example: a level that contains EL_PLAYER_3 as the only player would
13513            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13514         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13515
13516         CheckElementChangeBySide(x, y, hitting_element, player_element,
13517                                  CE_HITTING_X, touched_side);
13518       }
13519     }
13520   }
13521
13522   // "hitting something" is also true when hitting the playfield border
13523   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13524                            CE_HITTING_SOMETHING, direction);
13525 }
13526
13527 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13528 {
13529   int i, kill_x = -1, kill_y = -1;
13530
13531   int bad_element = -1;
13532   static int test_xy[4][2] =
13533   {
13534     { 0, -1 },
13535     { -1, 0 },
13536     { +1, 0 },
13537     { 0, +1 }
13538   };
13539   static int test_dir[4] =
13540   {
13541     MV_UP,
13542     MV_LEFT,
13543     MV_RIGHT,
13544     MV_DOWN
13545   };
13546
13547   for (i = 0; i < NUM_DIRECTIONS; i++)
13548   {
13549     int test_x, test_y, test_move_dir, test_element;
13550
13551     test_x = good_x + test_xy[i][0];
13552     test_y = good_y + test_xy[i][1];
13553
13554     if (!IN_LEV_FIELD(test_x, test_y))
13555       continue;
13556
13557     test_move_dir =
13558       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13559
13560     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13561
13562     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13563        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13564     */
13565     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13566         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13567     {
13568       kill_x = test_x;
13569       kill_y = test_y;
13570       bad_element = test_element;
13571
13572       break;
13573     }
13574   }
13575
13576   if (kill_x != -1 || kill_y != -1)
13577   {
13578     if (IS_PLAYER(good_x, good_y))
13579     {
13580       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13581
13582       if (player->shield_deadly_time_left > 0 &&
13583           !IS_INDESTRUCTIBLE(bad_element))
13584         Bang(kill_x, kill_y);
13585       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13586         KillPlayer(player);
13587     }
13588     else
13589       Bang(good_x, good_y);
13590   }
13591 }
13592
13593 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13594 {
13595   int i, kill_x = -1, kill_y = -1;
13596   int bad_element = Tile[bad_x][bad_y];
13597   static int test_xy[4][2] =
13598   {
13599     { 0, -1 },
13600     { -1, 0 },
13601     { +1, 0 },
13602     { 0, +1 }
13603   };
13604   static int touch_dir[4] =
13605   {
13606     MV_LEFT | MV_RIGHT,
13607     MV_UP   | MV_DOWN,
13608     MV_UP   | MV_DOWN,
13609     MV_LEFT | MV_RIGHT
13610   };
13611   static int test_dir[4] =
13612   {
13613     MV_UP,
13614     MV_LEFT,
13615     MV_RIGHT,
13616     MV_DOWN
13617   };
13618
13619   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13620     return;
13621
13622   for (i = 0; i < NUM_DIRECTIONS; i++)
13623   {
13624     int test_x, test_y, test_move_dir, test_element;
13625
13626     test_x = bad_x + test_xy[i][0];
13627     test_y = bad_y + test_xy[i][1];
13628
13629     if (!IN_LEV_FIELD(test_x, test_y))
13630       continue;
13631
13632     test_move_dir =
13633       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13634
13635     test_element = Tile[test_x][test_y];
13636
13637     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13638        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13639     */
13640     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13641         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13642     {
13643       // good thing is player or penguin that does not move away
13644       if (IS_PLAYER(test_x, test_y))
13645       {
13646         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13647
13648         if (bad_element == EL_ROBOT && player->is_moving)
13649           continue;     // robot does not kill player if he is moving
13650
13651         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13652         {
13653           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13654             continue;           // center and border element do not touch
13655         }
13656
13657         kill_x = test_x;
13658         kill_y = test_y;
13659
13660         break;
13661       }
13662       else if (test_element == EL_PENGUIN)
13663       {
13664         kill_x = test_x;
13665         kill_y = test_y;
13666
13667         break;
13668       }
13669     }
13670   }
13671
13672   if (kill_x != -1 || kill_y != -1)
13673   {
13674     if (IS_PLAYER(kill_x, kill_y))
13675     {
13676       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13677
13678       if (player->shield_deadly_time_left > 0 &&
13679           !IS_INDESTRUCTIBLE(bad_element))
13680         Bang(bad_x, bad_y);
13681       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13682         KillPlayer(player);
13683     }
13684     else
13685       Bang(kill_x, kill_y);
13686   }
13687 }
13688
13689 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13690 {
13691   int bad_element = Tile[bad_x][bad_y];
13692   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13693   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13694   int test_x = bad_x + dx, test_y = bad_y + dy;
13695   int test_move_dir, test_element;
13696   int kill_x = -1, kill_y = -1;
13697
13698   if (!IN_LEV_FIELD(test_x, test_y))
13699     return;
13700
13701   test_move_dir =
13702     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13703
13704   test_element = Tile[test_x][test_y];
13705
13706   if (test_move_dir != bad_move_dir)
13707   {
13708     // good thing can be player or penguin that does not move away
13709     if (IS_PLAYER(test_x, test_y))
13710     {
13711       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13712
13713       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13714          player as being hit when he is moving towards the bad thing, because
13715          the "get hit by" condition would be lost after the player stops) */
13716       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13717         return;         // player moves away from bad thing
13718
13719       kill_x = test_x;
13720       kill_y = test_y;
13721     }
13722     else if (test_element == EL_PENGUIN)
13723     {
13724       kill_x = test_x;
13725       kill_y = test_y;
13726     }
13727   }
13728
13729   if (kill_x != -1 || kill_y != -1)
13730   {
13731     if (IS_PLAYER(kill_x, kill_y))
13732     {
13733       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13734
13735       if (player->shield_deadly_time_left > 0 &&
13736           !IS_INDESTRUCTIBLE(bad_element))
13737         Bang(bad_x, bad_y);
13738       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13739         KillPlayer(player);
13740     }
13741     else
13742       Bang(kill_x, kill_y);
13743   }
13744 }
13745
13746 void TestIfPlayerTouchesBadThing(int x, int y)
13747 {
13748   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13749 }
13750
13751 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13752 {
13753   TestIfGoodThingHitsBadThing(x, y, move_dir);
13754 }
13755
13756 void TestIfBadThingTouchesPlayer(int x, int y)
13757 {
13758   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13759 }
13760
13761 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13762 {
13763   TestIfBadThingHitsGoodThing(x, y, move_dir);
13764 }
13765
13766 void TestIfFriendTouchesBadThing(int x, int y)
13767 {
13768   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13769 }
13770
13771 void TestIfBadThingTouchesFriend(int x, int y)
13772 {
13773   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13774 }
13775
13776 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13777 {
13778   int i, kill_x = bad_x, kill_y = bad_y;
13779   static int xy[4][2] =
13780   {
13781     { 0, -1 },
13782     { -1, 0 },
13783     { +1, 0 },
13784     { 0, +1 }
13785   };
13786
13787   for (i = 0; i < NUM_DIRECTIONS; i++)
13788   {
13789     int x, y, element;
13790
13791     x = bad_x + xy[i][0];
13792     y = bad_y + xy[i][1];
13793     if (!IN_LEV_FIELD(x, y))
13794       continue;
13795
13796     element = Tile[x][y];
13797     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13798         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13799     {
13800       kill_x = x;
13801       kill_y = y;
13802       break;
13803     }
13804   }
13805
13806   if (kill_x != bad_x || kill_y != bad_y)
13807     Bang(bad_x, bad_y);
13808 }
13809
13810 void KillPlayer(struct PlayerInfo *player)
13811 {
13812   int jx = player->jx, jy = player->jy;
13813
13814   if (!player->active)
13815     return;
13816
13817 #if 0
13818   Debug("game:playing:KillPlayer",
13819         "0: killed == %d, active == %d, reanimated == %d",
13820         player->killed, player->active, player->reanimated);
13821 #endif
13822
13823   /* the following code was introduced to prevent an infinite loop when calling
13824      -> Bang()
13825      -> CheckTriggeredElementChangeExt()
13826      -> ExecuteCustomElementAction()
13827      -> KillPlayer()
13828      -> (infinitely repeating the above sequence of function calls)
13829      which occurs when killing the player while having a CE with the setting
13830      "kill player X when explosion of <player X>"; the solution using a new
13831      field "player->killed" was chosen for backwards compatibility, although
13832      clever use of the fields "player->active" etc. would probably also work */
13833 #if 1
13834   if (player->killed)
13835     return;
13836 #endif
13837
13838   player->killed = TRUE;
13839
13840   // remove accessible field at the player's position
13841   Tile[jx][jy] = EL_EMPTY;
13842
13843   // deactivate shield (else Bang()/Explode() would not work right)
13844   player->shield_normal_time_left = 0;
13845   player->shield_deadly_time_left = 0;
13846
13847 #if 0
13848   Debug("game:playing:KillPlayer",
13849         "1: killed == %d, active == %d, reanimated == %d",
13850         player->killed, player->active, player->reanimated);
13851 #endif
13852
13853   Bang(jx, jy);
13854
13855 #if 0
13856   Debug("game:playing:KillPlayer",
13857         "2: killed == %d, active == %d, reanimated == %d",
13858         player->killed, player->active, player->reanimated);
13859 #endif
13860
13861   if (player->reanimated)       // killed player may have been reanimated
13862     player->killed = player->reanimated = FALSE;
13863   else
13864     BuryPlayer(player);
13865 }
13866
13867 static void KillPlayerUnlessEnemyProtected(int x, int y)
13868 {
13869   if (!PLAYER_ENEMY_PROTECTED(x, y))
13870     KillPlayer(PLAYERINFO(x, y));
13871 }
13872
13873 static void KillPlayerUnlessExplosionProtected(int x, int y)
13874 {
13875   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13876     KillPlayer(PLAYERINFO(x, y));
13877 }
13878
13879 void BuryPlayer(struct PlayerInfo *player)
13880 {
13881   int jx = player->jx, jy = player->jy;
13882
13883   if (!player->active)
13884     return;
13885
13886   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13887   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13888
13889   RemovePlayer(player);
13890
13891   player->buried = TRUE;
13892
13893   if (game.all_players_gone)
13894     game.GameOver = TRUE;
13895 }
13896
13897 void RemovePlayer(struct PlayerInfo *player)
13898 {
13899   int jx = player->jx, jy = player->jy;
13900   int i, found = FALSE;
13901
13902   player->present = FALSE;
13903   player->active = FALSE;
13904
13905   // required for some CE actions (even if the player is not active anymore)
13906   player->MovPos = 0;
13907
13908   if (!ExplodeField[jx][jy])
13909     StorePlayer[jx][jy] = 0;
13910
13911   if (player->is_moving)
13912     TEST_DrawLevelField(player->last_jx, player->last_jy);
13913
13914   for (i = 0; i < MAX_PLAYERS; i++)
13915     if (stored_player[i].active)
13916       found = TRUE;
13917
13918   if (!found)
13919   {
13920     game.all_players_gone = TRUE;
13921     game.GameOver = TRUE;
13922   }
13923
13924   game.exit_x = game.robot_wheel_x = jx;
13925   game.exit_y = game.robot_wheel_y = jy;
13926 }
13927
13928 void ExitPlayer(struct PlayerInfo *player)
13929 {
13930   DrawPlayer(player);   // needed here only to cleanup last field
13931   RemovePlayer(player);
13932
13933   if (game.players_still_needed > 0)
13934     game.players_still_needed--;
13935 }
13936
13937 static void SetFieldForSnapping(int x, int y, int element, int direction,
13938                                 int player_index_bit)
13939 {
13940   struct ElementInfo *ei = &element_info[element];
13941   int direction_bit = MV_DIR_TO_BIT(direction);
13942   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13943   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13944                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13945
13946   Tile[x][y] = EL_ELEMENT_SNAPPING;
13947   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13948   MovDir[x][y] = direction;
13949   Store[x][y] = element;
13950   Store2[x][y] = player_index_bit;
13951
13952   ResetGfxAnimation(x, y);
13953
13954   GfxElement[x][y] = element;
13955   GfxAction[x][y] = action;
13956   GfxDir[x][y] = direction;
13957   GfxFrame[x][y] = -1;
13958 }
13959
13960 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13961                                    int player_index_bit)
13962 {
13963   TestIfElementTouchesCustomElement(x, y);      // for empty space
13964
13965   if (level.finish_dig_collect)
13966   {
13967     int dig_side = MV_DIR_OPPOSITE(direction);
13968
13969     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13970                                         player_index_bit, dig_side);
13971   }
13972 }
13973
13974 /*
13975   =============================================================================
13976   checkDiagonalPushing()
13977   -----------------------------------------------------------------------------
13978   check if diagonal input device direction results in pushing of object
13979   (by checking if the alternative direction is walkable, diggable, ...)
13980   =============================================================================
13981 */
13982
13983 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13984                                     int x, int y, int real_dx, int real_dy)
13985 {
13986   int jx, jy, dx, dy, xx, yy;
13987
13988   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13989     return TRUE;
13990
13991   // diagonal direction: check alternative direction
13992   jx = player->jx;
13993   jy = player->jy;
13994   dx = x - jx;
13995   dy = y - jy;
13996   xx = jx + (dx == 0 ? real_dx : 0);
13997   yy = jy + (dy == 0 ? real_dy : 0);
13998
13999   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14000 }
14001
14002 /*
14003   =============================================================================
14004   DigField()
14005   -----------------------------------------------------------------------------
14006   x, y:                 field next to player (non-diagonal) to try to dig to
14007   real_dx, real_dy:     direction as read from input device (can be diagonal)
14008   =============================================================================
14009 */
14010
14011 static int DigField(struct PlayerInfo *player,
14012                     int oldx, int oldy, int x, int y,
14013                     int real_dx, int real_dy, int mode)
14014 {
14015   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14016   boolean player_was_pushing = player->is_pushing;
14017   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14018   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14019   int jx = oldx, jy = oldy;
14020   int dx = x - jx, dy = y - jy;
14021   int nextx = x + dx, nexty = y + dy;
14022   int move_direction = (dx == -1 ? MV_LEFT  :
14023                         dx == +1 ? MV_RIGHT :
14024                         dy == -1 ? MV_UP    :
14025                         dy == +1 ? MV_DOWN  : MV_NONE);
14026   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14027   int dig_side = MV_DIR_OPPOSITE(move_direction);
14028   int old_element = Tile[jx][jy];
14029   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14030   int collect_count;
14031
14032   if (is_player)                // function can also be called by EL_PENGUIN
14033   {
14034     if (player->MovPos == 0)
14035     {
14036       player->is_digging = FALSE;
14037       player->is_collecting = FALSE;
14038     }
14039
14040     if (player->MovPos == 0)    // last pushing move finished
14041       player->is_pushing = FALSE;
14042
14043     if (mode == DF_NO_PUSH)     // player just stopped pushing
14044     {
14045       player->is_switching = FALSE;
14046       player->push_delay = -1;
14047
14048       return MP_NO_ACTION;
14049     }
14050   }
14051
14052   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14053     old_element = Back[jx][jy];
14054
14055   // in case of element dropped at player position, check background
14056   else if (Back[jx][jy] != EL_EMPTY &&
14057            game.engine_version >= VERSION_IDENT(2,2,0,0))
14058     old_element = Back[jx][jy];
14059
14060   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14061     return MP_NO_ACTION;        // field has no opening in this direction
14062
14063   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14064     return MP_NO_ACTION;        // field has no opening in this direction
14065
14066   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14067   {
14068     SplashAcid(x, y);
14069
14070     Tile[jx][jy] = player->artwork_element;
14071     InitMovingField(jx, jy, MV_DOWN);
14072     Store[jx][jy] = EL_ACID;
14073     ContinueMoving(jx, jy);
14074     BuryPlayer(player);
14075
14076     return MP_DONT_RUN_INTO;
14077   }
14078
14079   if (player_can_move && DONT_RUN_INTO(element))
14080   {
14081     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14082
14083     return MP_DONT_RUN_INTO;
14084   }
14085
14086   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14087     return MP_NO_ACTION;
14088
14089   collect_count = element_info[element].collect_count_initial;
14090
14091   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14092     return MP_NO_ACTION;
14093
14094   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14095     player_can_move = player_can_move_or_snap;
14096
14097   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14098       game.engine_version >= VERSION_IDENT(2,2,0,0))
14099   {
14100     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14101                                player->index_bit, dig_side);
14102     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14103                                         player->index_bit, dig_side);
14104
14105     if (element == EL_DC_LANDMINE)
14106       Bang(x, y);
14107
14108     if (Tile[x][y] != element)          // field changed by snapping
14109       return MP_ACTION;
14110
14111     return MP_NO_ACTION;
14112   }
14113
14114   if (player->gravity && is_player && !player->is_auto_moving &&
14115       canFallDown(player) && move_direction != MV_DOWN &&
14116       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14117     return MP_NO_ACTION;        // player cannot walk here due to gravity
14118
14119   if (player_can_move &&
14120       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14121   {
14122     int sound_element = SND_ELEMENT(element);
14123     int sound_action = ACTION_WALKING;
14124
14125     if (IS_RND_GATE(element))
14126     {
14127       if (!player->key[RND_GATE_NR(element)])
14128         return MP_NO_ACTION;
14129     }
14130     else if (IS_RND_GATE_GRAY(element))
14131     {
14132       if (!player->key[RND_GATE_GRAY_NR(element)])
14133         return MP_NO_ACTION;
14134     }
14135     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14136     {
14137       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14138         return MP_NO_ACTION;
14139     }
14140     else if (element == EL_EXIT_OPEN ||
14141              element == EL_EM_EXIT_OPEN ||
14142              element == EL_EM_EXIT_OPENING ||
14143              element == EL_STEEL_EXIT_OPEN ||
14144              element == EL_EM_STEEL_EXIT_OPEN ||
14145              element == EL_EM_STEEL_EXIT_OPENING ||
14146              element == EL_SP_EXIT_OPEN ||
14147              element == EL_SP_EXIT_OPENING)
14148     {
14149       sound_action = ACTION_PASSING;    // player is passing exit
14150     }
14151     else if (element == EL_EMPTY)
14152     {
14153       sound_action = ACTION_MOVING;             // nothing to walk on
14154     }
14155
14156     // play sound from background or player, whatever is available
14157     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14158       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14159     else
14160       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14161   }
14162   else if (player_can_move &&
14163            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14164   {
14165     if (!ACCESS_FROM(element, opposite_direction))
14166       return MP_NO_ACTION;      // field not accessible from this direction
14167
14168     if (CAN_MOVE(element))      // only fixed elements can be passed!
14169       return MP_NO_ACTION;
14170
14171     if (IS_EM_GATE(element))
14172     {
14173       if (!player->key[EM_GATE_NR(element)])
14174         return MP_NO_ACTION;
14175     }
14176     else if (IS_EM_GATE_GRAY(element))
14177     {
14178       if (!player->key[EM_GATE_GRAY_NR(element)])
14179         return MP_NO_ACTION;
14180     }
14181     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14182     {
14183       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14184         return MP_NO_ACTION;
14185     }
14186     else if (IS_EMC_GATE(element))
14187     {
14188       if (!player->key[EMC_GATE_NR(element)])
14189         return MP_NO_ACTION;
14190     }
14191     else if (IS_EMC_GATE_GRAY(element))
14192     {
14193       if (!player->key[EMC_GATE_GRAY_NR(element)])
14194         return MP_NO_ACTION;
14195     }
14196     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14197     {
14198       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14199         return MP_NO_ACTION;
14200     }
14201     else if (element == EL_DC_GATE_WHITE ||
14202              element == EL_DC_GATE_WHITE_GRAY ||
14203              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14204     {
14205       if (player->num_white_keys == 0)
14206         return MP_NO_ACTION;
14207
14208       player->num_white_keys--;
14209     }
14210     else if (IS_SP_PORT(element))
14211     {
14212       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14213           element == EL_SP_GRAVITY_PORT_RIGHT ||
14214           element == EL_SP_GRAVITY_PORT_UP ||
14215           element == EL_SP_GRAVITY_PORT_DOWN)
14216         player->gravity = !player->gravity;
14217       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14218                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14219                element == EL_SP_GRAVITY_ON_PORT_UP ||
14220                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14221         player->gravity = TRUE;
14222       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14223                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14224                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14225                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14226         player->gravity = FALSE;
14227     }
14228
14229     // automatically move to the next field with double speed
14230     player->programmed_action = move_direction;
14231
14232     if (player->move_delay_reset_counter == 0)
14233     {
14234       player->move_delay_reset_counter = 2;     // two double speed steps
14235
14236       DOUBLE_PLAYER_SPEED(player);
14237     }
14238
14239     PlayLevelSoundAction(x, y, ACTION_PASSING);
14240   }
14241   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14242   {
14243     RemoveField(x, y);
14244
14245     if (mode != DF_SNAP)
14246     {
14247       GfxElement[x][y] = GFX_ELEMENT(element);
14248       player->is_digging = TRUE;
14249     }
14250
14251     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14252
14253     // use old behaviour for old levels (digging)
14254     if (!level.finish_dig_collect)
14255     {
14256       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14257                                           player->index_bit, dig_side);
14258
14259       // if digging triggered player relocation, finish digging tile
14260       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14261         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14262     }
14263
14264     if (mode == DF_SNAP)
14265     {
14266       if (level.block_snap_field)
14267         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14268       else
14269         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14270
14271       // use old behaviour for old levels (snapping)
14272       if (!level.finish_dig_collect)
14273         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14274                                             player->index_bit, dig_side);
14275     }
14276   }
14277   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14278   {
14279     RemoveField(x, y);
14280
14281     if (is_player && mode != DF_SNAP)
14282     {
14283       GfxElement[x][y] = element;
14284       player->is_collecting = TRUE;
14285     }
14286
14287     if (element == EL_SPEED_PILL)
14288     {
14289       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14290     }
14291     else if (element == EL_EXTRA_TIME && level.time > 0)
14292     {
14293       TimeLeft += level.extra_time;
14294
14295       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14296
14297       DisplayGameControlValues();
14298     }
14299     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14300     {
14301       player->shield_normal_time_left += level.shield_normal_time;
14302       if (element == EL_SHIELD_DEADLY)
14303         player->shield_deadly_time_left += level.shield_deadly_time;
14304     }
14305     else if (element == EL_DYNAMITE ||
14306              element == EL_EM_DYNAMITE ||
14307              element == EL_SP_DISK_RED)
14308     {
14309       if (player->inventory_size < MAX_INVENTORY_SIZE)
14310         player->inventory_element[player->inventory_size++] = element;
14311
14312       DrawGameDoorValues();
14313     }
14314     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14315     {
14316       player->dynabomb_count++;
14317       player->dynabombs_left++;
14318     }
14319     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14320     {
14321       player->dynabomb_size++;
14322     }
14323     else if (element == EL_DYNABOMB_INCREASE_POWER)
14324     {
14325       player->dynabomb_xl = TRUE;
14326     }
14327     else if (IS_KEY(element))
14328     {
14329       player->key[KEY_NR(element)] = TRUE;
14330
14331       DrawGameDoorValues();
14332     }
14333     else if (element == EL_DC_KEY_WHITE)
14334     {
14335       player->num_white_keys++;
14336
14337       // display white keys?
14338       // DrawGameDoorValues();
14339     }
14340     else if (IS_ENVELOPE(element))
14341     {
14342       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14343
14344       if (!wait_for_snapping)
14345         player->show_envelope = element;
14346     }
14347     else if (element == EL_EMC_LENSES)
14348     {
14349       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14350
14351       RedrawAllInvisibleElementsForLenses();
14352     }
14353     else if (element == EL_EMC_MAGNIFIER)
14354     {
14355       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14356
14357       RedrawAllInvisibleElementsForMagnifier();
14358     }
14359     else if (IS_DROPPABLE(element) ||
14360              IS_THROWABLE(element))     // can be collected and dropped
14361     {
14362       int i;
14363
14364       if (collect_count == 0)
14365         player->inventory_infinite_element = element;
14366       else
14367         for (i = 0; i < collect_count; i++)
14368           if (player->inventory_size < MAX_INVENTORY_SIZE)
14369             player->inventory_element[player->inventory_size++] = element;
14370
14371       DrawGameDoorValues();
14372     }
14373     else if (collect_count > 0)
14374     {
14375       game.gems_still_needed -= collect_count;
14376       if (game.gems_still_needed < 0)
14377         game.gems_still_needed = 0;
14378
14379       game.snapshot.collected_item = TRUE;
14380
14381       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14382
14383       DisplayGameControlValues();
14384     }
14385
14386     RaiseScoreElement(element);
14387     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14388
14389     // use old behaviour for old levels (collecting)
14390     if (!level.finish_dig_collect && is_player)
14391     {
14392       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14393                                           player->index_bit, dig_side);
14394
14395       // if collecting triggered player relocation, finish collecting tile
14396       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14397         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14398     }
14399
14400     if (mode == DF_SNAP)
14401     {
14402       if (level.block_snap_field)
14403         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14404       else
14405         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14406
14407       // use old behaviour for old levels (snapping)
14408       if (!level.finish_dig_collect)
14409         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14410                                             player->index_bit, dig_side);
14411     }
14412   }
14413   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14414   {
14415     if (mode == DF_SNAP && element != EL_BD_ROCK)
14416       return MP_NO_ACTION;
14417
14418     if (CAN_FALL(element) && dy)
14419       return MP_NO_ACTION;
14420
14421     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14422         !(element == EL_SPRING && level.use_spring_bug))
14423       return MP_NO_ACTION;
14424
14425     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14426         ((move_direction & MV_VERTICAL &&
14427           ((element_info[element].move_pattern & MV_LEFT &&
14428             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14429            (element_info[element].move_pattern & MV_RIGHT &&
14430             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14431          (move_direction & MV_HORIZONTAL &&
14432           ((element_info[element].move_pattern & MV_UP &&
14433             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14434            (element_info[element].move_pattern & MV_DOWN &&
14435             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14436       return MP_NO_ACTION;
14437
14438     // do not push elements already moving away faster than player
14439     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14440         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14441       return MP_NO_ACTION;
14442
14443     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14444     {
14445       if (player->push_delay_value == -1 || !player_was_pushing)
14446         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14447     }
14448     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14449     {
14450       if (player->push_delay_value == -1)
14451         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14452     }
14453     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14454     {
14455       if (!player->is_pushing)
14456         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14457     }
14458
14459     player->is_pushing = TRUE;
14460     player->is_active = TRUE;
14461
14462     if (!(IN_LEV_FIELD(nextx, nexty) &&
14463           (IS_FREE(nextx, nexty) ||
14464            (IS_SB_ELEMENT(element) &&
14465             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14466            (IS_CUSTOM_ELEMENT(element) &&
14467             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14468       return MP_NO_ACTION;
14469
14470     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14471       return MP_NO_ACTION;
14472
14473     if (player->push_delay == -1)       // new pushing; restart delay
14474       player->push_delay = 0;
14475
14476     if (player->push_delay < player->push_delay_value &&
14477         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14478         element != EL_SPRING && element != EL_BALLOON)
14479     {
14480       // make sure that there is no move delay before next try to push
14481       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14482         player->move_delay = 0;
14483
14484       return MP_NO_ACTION;
14485     }
14486
14487     if (IS_CUSTOM_ELEMENT(element) &&
14488         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14489     {
14490       if (!DigFieldByCE(nextx, nexty, element))
14491         return MP_NO_ACTION;
14492     }
14493
14494     if (IS_SB_ELEMENT(element))
14495     {
14496       boolean sokoban_task_solved = FALSE;
14497
14498       if (element == EL_SOKOBAN_FIELD_FULL)
14499       {
14500         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14501
14502         IncrementSokobanFieldsNeeded();
14503         IncrementSokobanObjectsNeeded();
14504       }
14505
14506       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14507       {
14508         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14509
14510         DecrementSokobanFieldsNeeded();
14511         DecrementSokobanObjectsNeeded();
14512
14513         // sokoban object was pushed from empty field to sokoban field
14514         if (Back[x][y] == EL_EMPTY)
14515           sokoban_task_solved = TRUE;
14516       }
14517
14518       Tile[x][y] = EL_SOKOBAN_OBJECT;
14519
14520       if (Back[x][y] == Back[nextx][nexty])
14521         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14522       else if (Back[x][y] != 0)
14523         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14524                                     ACTION_EMPTYING);
14525       else
14526         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14527                                     ACTION_FILLING);
14528
14529       if (sokoban_task_solved &&
14530           game.sokoban_fields_still_needed == 0 &&
14531           game.sokoban_objects_still_needed == 0 &&
14532           level.auto_exit_sokoban)
14533       {
14534         game.players_still_needed = 0;
14535
14536         LevelSolved();
14537
14538         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14539       }
14540     }
14541     else
14542       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14543
14544     InitMovingField(x, y, move_direction);
14545     GfxAction[x][y] = ACTION_PUSHING;
14546
14547     if (mode == DF_SNAP)
14548       ContinueMoving(x, y);
14549     else
14550       MovPos[x][y] = (dx != 0 ? dx : dy);
14551
14552     Pushed[x][y] = TRUE;
14553     Pushed[nextx][nexty] = TRUE;
14554
14555     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14556       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14557     else
14558       player->push_delay_value = -1;    // get new value later
14559
14560     // check for element change _after_ element has been pushed
14561     if (game.use_change_when_pushing_bug)
14562     {
14563       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14564                                  player->index_bit, dig_side);
14565       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14566                                           player->index_bit, dig_side);
14567     }
14568   }
14569   else if (IS_SWITCHABLE(element))
14570   {
14571     if (PLAYER_SWITCHING(player, x, y))
14572     {
14573       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14574                                           player->index_bit, dig_side);
14575
14576       return MP_ACTION;
14577     }
14578
14579     player->is_switching = TRUE;
14580     player->switch_x = x;
14581     player->switch_y = y;
14582
14583     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14584
14585     if (element == EL_ROBOT_WHEEL)
14586     {
14587       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14588
14589       game.robot_wheel_x = x;
14590       game.robot_wheel_y = y;
14591       game.robot_wheel_active = TRUE;
14592
14593       TEST_DrawLevelField(x, y);
14594     }
14595     else if (element == EL_SP_TERMINAL)
14596     {
14597       int xx, yy;
14598
14599       SCAN_PLAYFIELD(xx, yy)
14600       {
14601         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14602         {
14603           Bang(xx, yy);
14604         }
14605         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14606         {
14607           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14608
14609           ResetGfxAnimation(xx, yy);
14610           TEST_DrawLevelField(xx, yy);
14611         }
14612       }
14613     }
14614     else if (IS_BELT_SWITCH(element))
14615     {
14616       ToggleBeltSwitch(x, y);
14617     }
14618     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14619              element == EL_SWITCHGATE_SWITCH_DOWN ||
14620              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14621              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14622     {
14623       ToggleSwitchgateSwitch(x, y);
14624     }
14625     else if (element == EL_LIGHT_SWITCH ||
14626              element == EL_LIGHT_SWITCH_ACTIVE)
14627     {
14628       ToggleLightSwitch(x, y);
14629     }
14630     else if (element == EL_TIMEGATE_SWITCH ||
14631              element == EL_DC_TIMEGATE_SWITCH)
14632     {
14633       ActivateTimegateSwitch(x, y);
14634     }
14635     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14636              element == EL_BALLOON_SWITCH_RIGHT ||
14637              element == EL_BALLOON_SWITCH_UP    ||
14638              element == EL_BALLOON_SWITCH_DOWN  ||
14639              element == EL_BALLOON_SWITCH_NONE  ||
14640              element == EL_BALLOON_SWITCH_ANY)
14641     {
14642       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14643                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14644                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14645                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14646                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14647                              move_direction);
14648     }
14649     else if (element == EL_LAMP)
14650     {
14651       Tile[x][y] = EL_LAMP_ACTIVE;
14652       game.lights_still_needed--;
14653
14654       ResetGfxAnimation(x, y);
14655       TEST_DrawLevelField(x, y);
14656     }
14657     else if (element == EL_TIME_ORB_FULL)
14658     {
14659       Tile[x][y] = EL_TIME_ORB_EMPTY;
14660
14661       if (level.time > 0 || level.use_time_orb_bug)
14662       {
14663         TimeLeft += level.time_orb_time;
14664         game.no_time_limit = FALSE;
14665
14666         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14667
14668         DisplayGameControlValues();
14669       }
14670
14671       ResetGfxAnimation(x, y);
14672       TEST_DrawLevelField(x, y);
14673     }
14674     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14675              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14676     {
14677       int xx, yy;
14678
14679       game.ball_active = !game.ball_active;
14680
14681       SCAN_PLAYFIELD(xx, yy)
14682       {
14683         int e = Tile[xx][yy];
14684
14685         if (game.ball_active)
14686         {
14687           if (e == EL_EMC_MAGIC_BALL)
14688             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14689           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14690             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14691         }
14692         else
14693         {
14694           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14695             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14696           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14697             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14698         }
14699       }
14700     }
14701
14702     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14703                                         player->index_bit, dig_side);
14704
14705     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14706                                         player->index_bit, dig_side);
14707
14708     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14709                                         player->index_bit, dig_side);
14710
14711     return MP_ACTION;
14712   }
14713   else
14714   {
14715     if (!PLAYER_SWITCHING(player, x, y))
14716     {
14717       player->is_switching = TRUE;
14718       player->switch_x = x;
14719       player->switch_y = y;
14720
14721       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14722                                  player->index_bit, dig_side);
14723       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14724                                           player->index_bit, dig_side);
14725
14726       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14727                                  player->index_bit, dig_side);
14728       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14729                                           player->index_bit, dig_side);
14730     }
14731
14732     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14733                                player->index_bit, dig_side);
14734     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14735                                         player->index_bit, dig_side);
14736
14737     return MP_NO_ACTION;
14738   }
14739
14740   player->push_delay = -1;
14741
14742   if (is_player)                // function can also be called by EL_PENGUIN
14743   {
14744     if (Tile[x][y] != element)          // really digged/collected something
14745     {
14746       player->is_collecting = !player->is_digging;
14747       player->is_active = TRUE;
14748
14749       player->last_removed_element = element;
14750     }
14751   }
14752
14753   return MP_MOVING;
14754 }
14755
14756 static boolean DigFieldByCE(int x, int y, int digging_element)
14757 {
14758   int element = Tile[x][y];
14759
14760   if (!IS_FREE(x, y))
14761   {
14762     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14763                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14764                   ACTION_BREAKING);
14765
14766     // no element can dig solid indestructible elements
14767     if (IS_INDESTRUCTIBLE(element) &&
14768         !IS_DIGGABLE(element) &&
14769         !IS_COLLECTIBLE(element))
14770       return FALSE;
14771
14772     if (AmoebaNr[x][y] &&
14773         (element == EL_AMOEBA_FULL ||
14774          element == EL_BD_AMOEBA ||
14775          element == EL_AMOEBA_GROWING))
14776     {
14777       AmoebaCnt[AmoebaNr[x][y]]--;
14778       AmoebaCnt2[AmoebaNr[x][y]]--;
14779     }
14780
14781     if (IS_MOVING(x, y))
14782       RemoveMovingField(x, y);
14783     else
14784     {
14785       RemoveField(x, y);
14786       TEST_DrawLevelField(x, y);
14787     }
14788
14789     // if digged element was about to explode, prevent the explosion
14790     ExplodeField[x][y] = EX_TYPE_NONE;
14791
14792     PlayLevelSoundAction(x, y, action);
14793   }
14794
14795   Store[x][y] = EL_EMPTY;
14796
14797   // this makes it possible to leave the removed element again
14798   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14799     Store[x][y] = element;
14800
14801   return TRUE;
14802 }
14803
14804 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14805 {
14806   int jx = player->jx, jy = player->jy;
14807   int x = jx + dx, y = jy + dy;
14808   int snap_direction = (dx == -1 ? MV_LEFT  :
14809                         dx == +1 ? MV_RIGHT :
14810                         dy == -1 ? MV_UP    :
14811                         dy == +1 ? MV_DOWN  : MV_NONE);
14812   boolean can_continue_snapping = (level.continuous_snapping &&
14813                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14814
14815   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14816     return FALSE;
14817
14818   if (!player->active || !IN_LEV_FIELD(x, y))
14819     return FALSE;
14820
14821   if (dx && dy)
14822     return FALSE;
14823
14824   if (!dx && !dy)
14825   {
14826     if (player->MovPos == 0)
14827       player->is_pushing = FALSE;
14828
14829     player->is_snapping = FALSE;
14830
14831     if (player->MovPos == 0)
14832     {
14833       player->is_moving = FALSE;
14834       player->is_digging = FALSE;
14835       player->is_collecting = FALSE;
14836     }
14837
14838     return FALSE;
14839   }
14840
14841   // prevent snapping with already pressed snap key when not allowed
14842   if (player->is_snapping && !can_continue_snapping)
14843     return FALSE;
14844
14845   player->MovDir = snap_direction;
14846
14847   if (player->MovPos == 0)
14848   {
14849     player->is_moving = FALSE;
14850     player->is_digging = FALSE;
14851     player->is_collecting = FALSE;
14852   }
14853
14854   player->is_dropping = FALSE;
14855   player->is_dropping_pressed = FALSE;
14856   player->drop_pressed_delay = 0;
14857
14858   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14859     return FALSE;
14860
14861   player->is_snapping = TRUE;
14862   player->is_active = TRUE;
14863
14864   if (player->MovPos == 0)
14865   {
14866     player->is_moving = FALSE;
14867     player->is_digging = FALSE;
14868     player->is_collecting = FALSE;
14869   }
14870
14871   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14872     TEST_DrawLevelField(player->last_jx, player->last_jy);
14873
14874   TEST_DrawLevelField(x, y);
14875
14876   return TRUE;
14877 }
14878
14879 static boolean DropElement(struct PlayerInfo *player)
14880 {
14881   int old_element, new_element;
14882   int dropx = player->jx, dropy = player->jy;
14883   int drop_direction = player->MovDir;
14884   int drop_side = drop_direction;
14885   int drop_element = get_next_dropped_element(player);
14886
14887   /* do not drop an element on top of another element; when holding drop key
14888      pressed without moving, dropped element must move away before the next
14889      element can be dropped (this is especially important if the next element
14890      is dynamite, which can be placed on background for historical reasons) */
14891   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14892     return MP_ACTION;
14893
14894   if (IS_THROWABLE(drop_element))
14895   {
14896     dropx += GET_DX_FROM_DIR(drop_direction);
14897     dropy += GET_DY_FROM_DIR(drop_direction);
14898
14899     if (!IN_LEV_FIELD(dropx, dropy))
14900       return FALSE;
14901   }
14902
14903   old_element = Tile[dropx][dropy];     // old element at dropping position
14904   new_element = drop_element;           // default: no change when dropping
14905
14906   // check if player is active, not moving and ready to drop
14907   if (!player->active || player->MovPos || player->drop_delay > 0)
14908     return FALSE;
14909
14910   // check if player has anything that can be dropped
14911   if (new_element == EL_UNDEFINED)
14912     return FALSE;
14913
14914   // only set if player has anything that can be dropped
14915   player->is_dropping_pressed = TRUE;
14916
14917   // check if drop key was pressed long enough for EM style dynamite
14918   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14919     return FALSE;
14920
14921   // check if anything can be dropped at the current position
14922   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14923     return FALSE;
14924
14925   // collected custom elements can only be dropped on empty fields
14926   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14927     return FALSE;
14928
14929   if (old_element != EL_EMPTY)
14930     Back[dropx][dropy] = old_element;   // store old element on this field
14931
14932   ResetGfxAnimation(dropx, dropy);
14933   ResetRandomAnimationValue(dropx, dropy);
14934
14935   if (player->inventory_size > 0 ||
14936       player->inventory_infinite_element != EL_UNDEFINED)
14937   {
14938     if (player->inventory_size > 0)
14939     {
14940       player->inventory_size--;
14941
14942       DrawGameDoorValues();
14943
14944       if (new_element == EL_DYNAMITE)
14945         new_element = EL_DYNAMITE_ACTIVE;
14946       else if (new_element == EL_EM_DYNAMITE)
14947         new_element = EL_EM_DYNAMITE_ACTIVE;
14948       else if (new_element == EL_SP_DISK_RED)
14949         new_element = EL_SP_DISK_RED_ACTIVE;
14950     }
14951
14952     Tile[dropx][dropy] = new_element;
14953
14954     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14955       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14956                           el2img(Tile[dropx][dropy]), 0);
14957
14958     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14959
14960     // needed if previous element just changed to "empty" in the last frame
14961     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14962
14963     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14964                                player->index_bit, drop_side);
14965     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14966                                         CE_PLAYER_DROPS_X,
14967                                         player->index_bit, drop_side);
14968
14969     TestIfElementTouchesCustomElement(dropx, dropy);
14970   }
14971   else          // player is dropping a dyna bomb
14972   {
14973     player->dynabombs_left--;
14974
14975     Tile[dropx][dropy] = new_element;
14976
14977     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14978       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14979                           el2img(Tile[dropx][dropy]), 0);
14980
14981     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14982   }
14983
14984   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14985     InitField_WithBug1(dropx, dropy, FALSE);
14986
14987   new_element = Tile[dropx][dropy];     // element might have changed
14988
14989   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14990       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14991   {
14992     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14993       MovDir[dropx][dropy] = drop_direction;
14994
14995     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14996
14997     // do not cause impact style collision by dropping elements that can fall
14998     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14999   }
15000
15001   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15002   player->is_dropping = TRUE;
15003
15004   player->drop_pressed_delay = 0;
15005   player->is_dropping_pressed = FALSE;
15006
15007   player->drop_x = dropx;
15008   player->drop_y = dropy;
15009
15010   return TRUE;
15011 }
15012
15013 // ----------------------------------------------------------------------------
15014 // game sound playing functions
15015 // ----------------------------------------------------------------------------
15016
15017 static int *loop_sound_frame = NULL;
15018 static int *loop_sound_volume = NULL;
15019
15020 void InitPlayLevelSound(void)
15021 {
15022   int num_sounds = getSoundListSize();
15023
15024   checked_free(loop_sound_frame);
15025   checked_free(loop_sound_volume);
15026
15027   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15028   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15029 }
15030
15031 static void PlayLevelSound(int x, int y, int nr)
15032 {
15033   int sx = SCREENX(x), sy = SCREENY(y);
15034   int volume, stereo_position;
15035   int max_distance = 8;
15036   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15037
15038   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15039       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15040     return;
15041
15042   if (!IN_LEV_FIELD(x, y) ||
15043       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15044       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15045     return;
15046
15047   volume = SOUND_MAX_VOLUME;
15048
15049   if (!IN_SCR_FIELD(sx, sy))
15050   {
15051     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15052     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15053
15054     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15055   }
15056
15057   stereo_position = (SOUND_MAX_LEFT +
15058                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15059                      (SCR_FIELDX + 2 * max_distance));
15060
15061   if (IS_LOOP_SOUND(nr))
15062   {
15063     /* This assures that quieter loop sounds do not overwrite louder ones,
15064        while restarting sound volume comparison with each new game frame. */
15065
15066     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15067       return;
15068
15069     loop_sound_volume[nr] = volume;
15070     loop_sound_frame[nr] = FrameCounter;
15071   }
15072
15073   PlaySoundExt(nr, volume, stereo_position, type);
15074 }
15075
15076 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15077 {
15078   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15079                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15080                  y < LEVELY(BY1) ? LEVELY(BY1) :
15081                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15082                  sound_action);
15083 }
15084
15085 static void PlayLevelSoundAction(int x, int y, int action)
15086 {
15087   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15088 }
15089
15090 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15091 {
15092   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15093
15094   if (sound_effect != SND_UNDEFINED)
15095     PlayLevelSound(x, y, sound_effect);
15096 }
15097
15098 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15099                                               int action)
15100 {
15101   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15102
15103   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15104     PlayLevelSound(x, y, sound_effect);
15105 }
15106
15107 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15108 {
15109   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15110
15111   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15112     PlayLevelSound(x, y, sound_effect);
15113 }
15114
15115 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15116 {
15117   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15118
15119   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15120     StopSound(sound_effect);
15121 }
15122
15123 static int getLevelMusicNr(void)
15124 {
15125   if (levelset.music[level_nr] != MUS_UNDEFINED)
15126     return levelset.music[level_nr];            // from config file
15127   else
15128     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15129 }
15130
15131 static void FadeLevelSounds(void)
15132 {
15133   FadeSounds();
15134 }
15135
15136 static void FadeLevelMusic(void)
15137 {
15138   int music_nr = getLevelMusicNr();
15139   char *curr_music = getCurrentlyPlayingMusicFilename();
15140   char *next_music = getMusicInfoEntryFilename(music_nr);
15141
15142   if (!strEqual(curr_music, next_music))
15143     FadeMusic();
15144 }
15145
15146 void FadeLevelSoundsAndMusic(void)
15147 {
15148   FadeLevelSounds();
15149   FadeLevelMusic();
15150 }
15151
15152 static void PlayLevelMusic(void)
15153 {
15154   int music_nr = getLevelMusicNr();
15155   char *curr_music = getCurrentlyPlayingMusicFilename();
15156   char *next_music = getMusicInfoEntryFilename(music_nr);
15157
15158   if (!strEqual(curr_music, next_music))
15159     PlayMusicLoop(music_nr);
15160 }
15161
15162 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15163 {
15164   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15165   int offset = 0;
15166   int x = xx - offset;
15167   int y = yy - offset;
15168
15169   switch (sample)
15170   {
15171     case SOUND_blank:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15173       break;
15174
15175     case SOUND_roll:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15177       break;
15178
15179     case SOUND_stone:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15181       break;
15182
15183     case SOUND_nut:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15185       break;
15186
15187     case SOUND_crack:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15189       break;
15190
15191     case SOUND_bug:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15193       break;
15194
15195     case SOUND_tank:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15197       break;
15198
15199     case SOUND_android_clone:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15201       break;
15202
15203     case SOUND_android_move:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15205       break;
15206
15207     case SOUND_spring:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15209       break;
15210
15211     case SOUND_slurp:
15212       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15213       break;
15214
15215     case SOUND_eater:
15216       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15217       break;
15218
15219     case SOUND_eater_eat:
15220       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15221       break;
15222
15223     case SOUND_alien:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15225       break;
15226
15227     case SOUND_collect:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15229       break;
15230
15231     case SOUND_diamond:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15233       break;
15234
15235     case SOUND_squash:
15236       // !!! CHECK THIS !!!
15237 #if 1
15238       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15239 #else
15240       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15241 #endif
15242       break;
15243
15244     case SOUND_wonderfall:
15245       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15246       break;
15247
15248     case SOUND_drip:
15249       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15250       break;
15251
15252     case SOUND_push:
15253       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15254       break;
15255
15256     case SOUND_dirt:
15257       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15258       break;
15259
15260     case SOUND_acid:
15261       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15262       break;
15263
15264     case SOUND_ball:
15265       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15266       break;
15267
15268     case SOUND_slide:
15269       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15270       break;
15271
15272     case SOUND_wonder:
15273       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15274       break;
15275
15276     case SOUND_door:
15277       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15278       break;
15279
15280     case SOUND_exit_open:
15281       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15282       break;
15283
15284     case SOUND_exit_leave:
15285       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15286       break;
15287
15288     case SOUND_dynamite:
15289       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15290       break;
15291
15292     case SOUND_tick:
15293       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15294       break;
15295
15296     case SOUND_press:
15297       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15298       break;
15299
15300     case SOUND_wheel:
15301       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15302       break;
15303
15304     case SOUND_boom:
15305       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15306       break;
15307
15308     case SOUND_die:
15309       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15310       break;
15311
15312     case SOUND_time:
15313       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15314       break;
15315
15316     default:
15317       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15318       break;
15319   }
15320 }
15321
15322 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15323 {
15324   int element = map_element_SP_to_RND(element_sp);
15325   int action = map_action_SP_to_RND(action_sp);
15326   int offset = (setup.sp_show_border_elements ? 0 : 1);
15327   int x = xx - offset;
15328   int y = yy - offset;
15329
15330   PlayLevelSoundElementAction(x, y, element, action);
15331 }
15332
15333 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15334 {
15335   int element = map_element_MM_to_RND(element_mm);
15336   int action = map_action_MM_to_RND(action_mm);
15337   int offset = 0;
15338   int x = xx - offset;
15339   int y = yy - offset;
15340
15341   if (!IS_MM_ELEMENT(element))
15342     element = EL_MM_DEFAULT;
15343
15344   PlayLevelSoundElementAction(x, y, element, action);
15345 }
15346
15347 void PlaySound_MM(int sound_mm)
15348 {
15349   int sound = map_sound_MM_to_RND(sound_mm);
15350
15351   if (sound == SND_UNDEFINED)
15352     return;
15353
15354   PlaySound(sound);
15355 }
15356
15357 void PlaySoundLoop_MM(int sound_mm)
15358 {
15359   int sound = map_sound_MM_to_RND(sound_mm);
15360
15361   if (sound == SND_UNDEFINED)
15362     return;
15363
15364   PlaySoundLoop(sound);
15365 }
15366
15367 void StopSound_MM(int sound_mm)
15368 {
15369   int sound = map_sound_MM_to_RND(sound_mm);
15370
15371   if (sound == SND_UNDEFINED)
15372     return;
15373
15374   StopSound(sound);
15375 }
15376
15377 void RaiseScore(int value)
15378 {
15379   game.score += value;
15380
15381   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15382
15383   DisplayGameControlValues();
15384 }
15385
15386 void RaiseScoreElement(int element)
15387 {
15388   switch (element)
15389   {
15390     case EL_EMERALD:
15391     case EL_BD_DIAMOND:
15392     case EL_EMERALD_YELLOW:
15393     case EL_EMERALD_RED:
15394     case EL_EMERALD_PURPLE:
15395     case EL_SP_INFOTRON:
15396       RaiseScore(level.score[SC_EMERALD]);
15397       break;
15398     case EL_DIAMOND:
15399       RaiseScore(level.score[SC_DIAMOND]);
15400       break;
15401     case EL_CRYSTAL:
15402       RaiseScore(level.score[SC_CRYSTAL]);
15403       break;
15404     case EL_PEARL:
15405       RaiseScore(level.score[SC_PEARL]);
15406       break;
15407     case EL_BUG:
15408     case EL_BD_BUTTERFLY:
15409     case EL_SP_ELECTRON:
15410       RaiseScore(level.score[SC_BUG]);
15411       break;
15412     case EL_SPACESHIP:
15413     case EL_BD_FIREFLY:
15414     case EL_SP_SNIKSNAK:
15415       RaiseScore(level.score[SC_SPACESHIP]);
15416       break;
15417     case EL_YAMYAM:
15418     case EL_DARK_YAMYAM:
15419       RaiseScore(level.score[SC_YAMYAM]);
15420       break;
15421     case EL_ROBOT:
15422       RaiseScore(level.score[SC_ROBOT]);
15423       break;
15424     case EL_PACMAN:
15425       RaiseScore(level.score[SC_PACMAN]);
15426       break;
15427     case EL_NUT:
15428       RaiseScore(level.score[SC_NUT]);
15429       break;
15430     case EL_DYNAMITE:
15431     case EL_EM_DYNAMITE:
15432     case EL_SP_DISK_RED:
15433     case EL_DYNABOMB_INCREASE_NUMBER:
15434     case EL_DYNABOMB_INCREASE_SIZE:
15435     case EL_DYNABOMB_INCREASE_POWER:
15436       RaiseScore(level.score[SC_DYNAMITE]);
15437       break;
15438     case EL_SHIELD_NORMAL:
15439     case EL_SHIELD_DEADLY:
15440       RaiseScore(level.score[SC_SHIELD]);
15441       break;
15442     case EL_EXTRA_TIME:
15443       RaiseScore(level.extra_time_score);
15444       break;
15445     case EL_KEY_1:
15446     case EL_KEY_2:
15447     case EL_KEY_3:
15448     case EL_KEY_4:
15449     case EL_EM_KEY_1:
15450     case EL_EM_KEY_2:
15451     case EL_EM_KEY_3:
15452     case EL_EM_KEY_4:
15453     case EL_EMC_KEY_5:
15454     case EL_EMC_KEY_6:
15455     case EL_EMC_KEY_7:
15456     case EL_EMC_KEY_8:
15457     case EL_DC_KEY_WHITE:
15458       RaiseScore(level.score[SC_KEY]);
15459       break;
15460     default:
15461       RaiseScore(element_info[element].collect_score);
15462       break;
15463   }
15464 }
15465
15466 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15467 {
15468   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15469   {
15470     if (!quick_quit)
15471     {
15472       // prevent short reactivation of overlay buttons while closing door
15473       SetOverlayActive(FALSE);
15474
15475       // door may still be open due to skipped or envelope style request
15476       CloseDoor(DOOR_CLOSE_1);
15477     }
15478
15479     if (network.enabled)
15480       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15481     else
15482     {
15483       if (quick_quit)
15484         FadeSkipNextFadeIn();
15485
15486       SetGameStatus(GAME_MODE_MAIN);
15487
15488       DrawMainMenu();
15489     }
15490   }
15491   else          // continue playing the game
15492   {
15493     if (tape.playing && tape.deactivate_display)
15494       TapeDeactivateDisplayOff(TRUE);
15495
15496     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15497
15498     if (tape.playing && tape.deactivate_display)
15499       TapeDeactivateDisplayOn();
15500   }
15501 }
15502
15503 void RequestQuitGame(boolean escape_key_pressed)
15504 {
15505   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15506   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15507                         level_editor_test_game);
15508   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15509                           quick_quit);
15510
15511   RequestQuitGameExt(skip_request, quick_quit,
15512                      "Do you really want to quit the game?");
15513 }
15514
15515 void RequestRestartGame(char *message)
15516 {
15517   game.restart_game_message = NULL;
15518
15519   boolean has_started_game = hasStartedNetworkGame();
15520   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15521
15522   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15523   {
15524     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15525   }
15526   else
15527   {
15528     // needed in case of envelope request to close game panel
15529     CloseDoor(DOOR_CLOSE_1);
15530
15531     SetGameStatus(GAME_MODE_MAIN);
15532
15533     DrawMainMenu();
15534   }
15535 }
15536
15537 void CheckGameOver(void)
15538 {
15539   static boolean last_game_over = FALSE;
15540   static int game_over_delay = 0;
15541   int game_over_delay_value = 50;
15542   boolean game_over = checkGameFailed();
15543
15544   // do not handle game over if request dialog is already active
15545   if (game.request_active)
15546     return;
15547
15548   // do not ask to play again if game was never actually played
15549   if (!game.GamePlayed)
15550     return;
15551
15552   if (!game_over)
15553   {
15554     last_game_over = FALSE;
15555     game_over_delay = game_over_delay_value;
15556
15557     return;
15558   }
15559
15560   if (game_over_delay > 0)
15561   {
15562     game_over_delay--;
15563
15564     return;
15565   }
15566
15567   if (last_game_over != game_over)
15568     game.restart_game_message = (hasStartedNetworkGame() ?
15569                                  "Game over! Play it again?" :
15570                                  "Game over!");
15571
15572   last_game_over = game_over;
15573 }
15574
15575 boolean checkGameSolved(void)
15576 {
15577   // set for all game engines if level was solved
15578   return game.LevelSolved_GameEnd;
15579 }
15580
15581 boolean checkGameFailed(void)
15582 {
15583   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15584     return (game_em.game_over && !game_em.level_solved);
15585   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15586     return (game_sp.game_over && !game_sp.level_solved);
15587   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15588     return (game_mm.game_over && !game_mm.level_solved);
15589   else                          // GAME_ENGINE_TYPE_RND
15590     return (game.GameOver && !game.LevelSolved);
15591 }
15592
15593 boolean checkGameEnded(void)
15594 {
15595   return (checkGameSolved() || checkGameFailed());
15596 }
15597
15598
15599 // ----------------------------------------------------------------------------
15600 // random generator functions
15601 // ----------------------------------------------------------------------------
15602
15603 unsigned int InitEngineRandom_RND(int seed)
15604 {
15605   game.num_random_calls = 0;
15606
15607   return InitEngineRandom(seed);
15608 }
15609
15610 unsigned int RND(int max)
15611 {
15612   if (max > 0)
15613   {
15614     game.num_random_calls++;
15615
15616     return GetEngineRandom(max);
15617   }
15618
15619   return 0;
15620 }
15621
15622
15623 // ----------------------------------------------------------------------------
15624 // game engine snapshot handling functions
15625 // ----------------------------------------------------------------------------
15626
15627 struct EngineSnapshotInfo
15628 {
15629   // runtime values for custom element collect score
15630   int collect_score[NUM_CUSTOM_ELEMENTS];
15631
15632   // runtime values for group element choice position
15633   int choice_pos[NUM_GROUP_ELEMENTS];
15634
15635   // runtime values for belt position animations
15636   int belt_graphic[4][NUM_BELT_PARTS];
15637   int belt_anim_mode[4][NUM_BELT_PARTS];
15638 };
15639
15640 static struct EngineSnapshotInfo engine_snapshot_rnd;
15641 static char *snapshot_level_identifier = NULL;
15642 static int snapshot_level_nr = -1;
15643
15644 static void SaveEngineSnapshotValues_RND(void)
15645 {
15646   static int belt_base_active_element[4] =
15647   {
15648     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15649     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15650     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15651     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15652   };
15653   int i, j;
15654
15655   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15656   {
15657     int element = EL_CUSTOM_START + i;
15658
15659     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15660   }
15661
15662   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15663   {
15664     int element = EL_GROUP_START + i;
15665
15666     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15667   }
15668
15669   for (i = 0; i < 4; i++)
15670   {
15671     for (j = 0; j < NUM_BELT_PARTS; j++)
15672     {
15673       int element = belt_base_active_element[i] + j;
15674       int graphic = el2img(element);
15675       int anim_mode = graphic_info[graphic].anim_mode;
15676
15677       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15678       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15679     }
15680   }
15681 }
15682
15683 static void LoadEngineSnapshotValues_RND(void)
15684 {
15685   unsigned int num_random_calls = game.num_random_calls;
15686   int i, j;
15687
15688   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15689   {
15690     int element = EL_CUSTOM_START + i;
15691
15692     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15693   }
15694
15695   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15696   {
15697     int element = EL_GROUP_START + i;
15698
15699     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15700   }
15701
15702   for (i = 0; i < 4; i++)
15703   {
15704     for (j = 0; j < NUM_BELT_PARTS; j++)
15705     {
15706       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15707       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15708
15709       graphic_info[graphic].anim_mode = anim_mode;
15710     }
15711   }
15712
15713   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15714   {
15715     InitRND(tape.random_seed);
15716     for (i = 0; i < num_random_calls; i++)
15717       RND(1);
15718   }
15719
15720   if (game.num_random_calls != num_random_calls)
15721   {
15722     Error("number of random calls out of sync");
15723     Error("number of random calls should be %d", num_random_calls);
15724     Error("number of random calls is %d", game.num_random_calls);
15725
15726     Fail("this should not happen -- please debug");
15727   }
15728 }
15729
15730 void FreeEngineSnapshotSingle(void)
15731 {
15732   FreeSnapshotSingle();
15733
15734   setString(&snapshot_level_identifier, NULL);
15735   snapshot_level_nr = -1;
15736 }
15737
15738 void FreeEngineSnapshotList(void)
15739 {
15740   FreeSnapshotList();
15741 }
15742
15743 static ListNode *SaveEngineSnapshotBuffers(void)
15744 {
15745   ListNode *buffers = NULL;
15746
15747   // copy some special values to a structure better suited for the snapshot
15748
15749   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15750     SaveEngineSnapshotValues_RND();
15751   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15752     SaveEngineSnapshotValues_EM();
15753   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15754     SaveEngineSnapshotValues_SP(&buffers);
15755   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15756     SaveEngineSnapshotValues_MM(&buffers);
15757
15758   // save values stored in special snapshot structure
15759
15760   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15761     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15762   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15763     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15764   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15765     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15766   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15767     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15768
15769   // save further RND engine values
15770
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15774
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15780
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15784
15785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15786
15787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15789
15790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15805   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15806   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15808
15809   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15810   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15811
15812   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15813   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15814   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15815
15816   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15817   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15818
15819   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15820   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15821   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15822   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15823   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15824
15825   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15826   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15827
15828 #if 0
15829   ListNode *node = engine_snapshot_list_rnd;
15830   int num_bytes = 0;
15831
15832   while (node != NULL)
15833   {
15834     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15835
15836     node = node->next;
15837   }
15838
15839   Debug("game:playing:SaveEngineSnapshotBuffers",
15840         "size of engine snapshot: %d bytes", num_bytes);
15841 #endif
15842
15843   return buffers;
15844 }
15845
15846 void SaveEngineSnapshotSingle(void)
15847 {
15848   ListNode *buffers = SaveEngineSnapshotBuffers();
15849
15850   // finally save all snapshot buffers to single snapshot
15851   SaveSnapshotSingle(buffers);
15852
15853   // save level identification information
15854   setString(&snapshot_level_identifier, leveldir_current->identifier);
15855   snapshot_level_nr = level_nr;
15856 }
15857
15858 boolean CheckSaveEngineSnapshotToList(void)
15859 {
15860   boolean save_snapshot =
15861     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15862      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15863       game.snapshot.changed_action) ||
15864      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15865       game.snapshot.collected_item));
15866
15867   game.snapshot.changed_action = FALSE;
15868   game.snapshot.collected_item = FALSE;
15869   game.snapshot.save_snapshot = save_snapshot;
15870
15871   return save_snapshot;
15872 }
15873
15874 void SaveEngineSnapshotToList(void)
15875 {
15876   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15877       tape.quick_resume)
15878     return;
15879
15880   ListNode *buffers = SaveEngineSnapshotBuffers();
15881
15882   // finally save all snapshot buffers to snapshot list
15883   SaveSnapshotToList(buffers);
15884 }
15885
15886 void SaveEngineSnapshotToListInitial(void)
15887 {
15888   FreeEngineSnapshotList();
15889
15890   SaveEngineSnapshotToList();
15891 }
15892
15893 static void LoadEngineSnapshotValues(void)
15894 {
15895   // restore special values from snapshot structure
15896
15897   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15898     LoadEngineSnapshotValues_RND();
15899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15900     LoadEngineSnapshotValues_EM();
15901   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15902     LoadEngineSnapshotValues_SP();
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15904     LoadEngineSnapshotValues_MM();
15905 }
15906
15907 void LoadEngineSnapshotSingle(void)
15908 {
15909   LoadSnapshotSingle();
15910
15911   LoadEngineSnapshotValues();
15912 }
15913
15914 static void LoadEngineSnapshot_Undo(int steps)
15915 {
15916   LoadSnapshotFromList_Older(steps);
15917
15918   LoadEngineSnapshotValues();
15919 }
15920
15921 static void LoadEngineSnapshot_Redo(int steps)
15922 {
15923   LoadSnapshotFromList_Newer(steps);
15924
15925   LoadEngineSnapshotValues();
15926 }
15927
15928 boolean CheckEngineSnapshotSingle(void)
15929 {
15930   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15931           snapshot_level_nr == level_nr);
15932 }
15933
15934 boolean CheckEngineSnapshotList(void)
15935 {
15936   return CheckSnapshotList();
15937 }
15938
15939
15940 // ---------- new game button stuff -------------------------------------------
15941
15942 static struct
15943 {
15944   int graphic;
15945   struct XY *pos;
15946   int gadget_id;
15947   boolean *setup_value;
15948   boolean allowed_on_tape;
15949   boolean is_touch_button;
15950   char *infotext;
15951 } gamebutton_info[NUM_GAME_BUTTONS] =
15952 {
15953   {
15954     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15955     GAME_CTRL_ID_STOP,                          NULL,
15956     TRUE, FALSE,                                "stop game"
15957   },
15958   {
15959     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15960     GAME_CTRL_ID_PAUSE,                         NULL,
15961     TRUE, FALSE,                                "pause game"
15962   },
15963   {
15964     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15965     GAME_CTRL_ID_PLAY,                          NULL,
15966     TRUE, FALSE,                                "play game"
15967   },
15968   {
15969     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15970     GAME_CTRL_ID_UNDO,                          NULL,
15971     TRUE, FALSE,                                "undo step"
15972   },
15973   {
15974     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15975     GAME_CTRL_ID_REDO,                          NULL,
15976     TRUE, FALSE,                                "redo step"
15977   },
15978   {
15979     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15980     GAME_CTRL_ID_SAVE,                          NULL,
15981     TRUE, FALSE,                                "save game"
15982   },
15983   {
15984     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15985     GAME_CTRL_ID_PAUSE2,                        NULL,
15986     TRUE, FALSE,                                "pause game"
15987   },
15988   {
15989     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15990     GAME_CTRL_ID_LOAD,                          NULL,
15991     TRUE, FALSE,                                "load game"
15992   },
15993   {
15994     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15995     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15996     FALSE, FALSE,                               "stop game"
15997   },
15998   {
15999     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16000     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16001     FALSE, FALSE,                               "pause game"
16002   },
16003   {
16004     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16005     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16006     FALSE, FALSE,                               "play game"
16007   },
16008   {
16009     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16010     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16011     FALSE, TRUE,                                "stop game"
16012   },
16013   {
16014     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16015     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16016     FALSE, TRUE,                                "pause game"
16017   },
16018   {
16019     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16020     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16021     TRUE, FALSE,                                "background music on/off"
16022   },
16023   {
16024     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16025     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16026     TRUE, FALSE,                                "sound loops on/off"
16027   },
16028   {
16029     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16030     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16031     TRUE, FALSE,                                "normal sounds on/off"
16032   },
16033   {
16034     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16035     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16036     FALSE, FALSE,                               "background music on/off"
16037   },
16038   {
16039     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16040     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16041     FALSE, FALSE,                               "sound loops on/off"
16042   },
16043   {
16044     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16045     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16046     FALSE, FALSE,                               "normal sounds on/off"
16047   }
16048 };
16049
16050 void CreateGameButtons(void)
16051 {
16052   int i;
16053
16054   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16055   {
16056     int graphic = gamebutton_info[i].graphic;
16057     struct GraphicInfo *gfx = &graphic_info[graphic];
16058     struct XY *pos = gamebutton_info[i].pos;
16059     struct GadgetInfo *gi;
16060     int button_type;
16061     boolean checked;
16062     unsigned int event_mask;
16063     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16064     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16065     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16066     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16067     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16068     int gd_x   = gfx->src_x;
16069     int gd_y   = gfx->src_y;
16070     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16071     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16072     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16073     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16074     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16075     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16076     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16077     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16078     int id = i;
16079
16080     if (gfx->bitmap == NULL)
16081     {
16082       game_gadget[id] = NULL;
16083
16084       continue;
16085     }
16086
16087     if (id == GAME_CTRL_ID_STOP ||
16088         id == GAME_CTRL_ID_PANEL_STOP ||
16089         id == GAME_CTRL_ID_TOUCH_STOP ||
16090         id == GAME_CTRL_ID_PLAY ||
16091         id == GAME_CTRL_ID_PANEL_PLAY ||
16092         id == GAME_CTRL_ID_SAVE ||
16093         id == GAME_CTRL_ID_LOAD)
16094     {
16095       button_type = GD_TYPE_NORMAL_BUTTON;
16096       checked = FALSE;
16097       event_mask = GD_EVENT_RELEASED;
16098     }
16099     else if (id == GAME_CTRL_ID_UNDO ||
16100              id == GAME_CTRL_ID_REDO)
16101     {
16102       button_type = GD_TYPE_NORMAL_BUTTON;
16103       checked = FALSE;
16104       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16105     }
16106     else
16107     {
16108       button_type = GD_TYPE_CHECK_BUTTON;
16109       checked = (gamebutton_info[i].setup_value != NULL ?
16110                  *gamebutton_info[i].setup_value : FALSE);
16111       event_mask = GD_EVENT_PRESSED;
16112     }
16113
16114     gi = CreateGadget(GDI_CUSTOM_ID, id,
16115                       GDI_IMAGE_ID, graphic,
16116                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16117                       GDI_X, base_x + x,
16118                       GDI_Y, base_y + y,
16119                       GDI_WIDTH, gfx->width,
16120                       GDI_HEIGHT, gfx->height,
16121                       GDI_TYPE, button_type,
16122                       GDI_STATE, GD_BUTTON_UNPRESSED,
16123                       GDI_CHECKED, checked,
16124                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16125                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16126                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16127                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16128                       GDI_DIRECT_DRAW, FALSE,
16129                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16130                       GDI_EVENT_MASK, event_mask,
16131                       GDI_CALLBACK_ACTION, HandleGameButtons,
16132                       GDI_END);
16133
16134     if (gi == NULL)
16135       Fail("cannot create gadget");
16136
16137     game_gadget[id] = gi;
16138   }
16139 }
16140
16141 void FreeGameButtons(void)
16142 {
16143   int i;
16144
16145   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16146     FreeGadget(game_gadget[i]);
16147 }
16148
16149 static void UnmapGameButtonsAtSamePosition(int id)
16150 {
16151   int i;
16152
16153   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16154     if (i != id &&
16155         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16156         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16157       UnmapGadget(game_gadget[i]);
16158 }
16159
16160 static void UnmapGameButtonsAtSamePosition_All(void)
16161 {
16162   if (setup.show_snapshot_buttons)
16163   {
16164     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16165     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16166     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16167   }
16168   else
16169   {
16170     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16171     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16172     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16173
16174     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16175     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16176     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16177   }
16178 }
16179
16180 static void MapGameButtonsAtSamePosition(int id)
16181 {
16182   int i;
16183
16184   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16185     if (i != id &&
16186         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16187         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16188       MapGadget(game_gadget[i]);
16189
16190   UnmapGameButtonsAtSamePosition_All();
16191 }
16192
16193 void MapUndoRedoButtons(void)
16194 {
16195   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16196   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16197
16198   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16199   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16200 }
16201
16202 void UnmapUndoRedoButtons(void)
16203 {
16204   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16205   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16206
16207   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16208   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16209 }
16210
16211 void ModifyPauseButtons(void)
16212 {
16213   static int ids[] =
16214   {
16215     GAME_CTRL_ID_PAUSE,
16216     GAME_CTRL_ID_PAUSE2,
16217     GAME_CTRL_ID_PANEL_PAUSE,
16218     GAME_CTRL_ID_TOUCH_PAUSE,
16219     -1
16220   };
16221   int i;
16222
16223   for (i = 0; ids[i] > -1; i++)
16224     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16225 }
16226
16227 static void MapGameButtonsExt(boolean on_tape)
16228 {
16229   int i;
16230
16231   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16232     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16233         i != GAME_CTRL_ID_UNDO &&
16234         i != GAME_CTRL_ID_REDO)
16235       MapGadget(game_gadget[i]);
16236
16237   UnmapGameButtonsAtSamePosition_All();
16238
16239   RedrawGameButtons();
16240 }
16241
16242 static void UnmapGameButtonsExt(boolean on_tape)
16243 {
16244   int i;
16245
16246   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16247     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16248       UnmapGadget(game_gadget[i]);
16249 }
16250
16251 static void RedrawGameButtonsExt(boolean on_tape)
16252 {
16253   int i;
16254
16255   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16256     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16257       RedrawGadget(game_gadget[i]);
16258 }
16259
16260 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16261 {
16262   if (gi == NULL)
16263     return;
16264
16265   gi->checked = state;
16266 }
16267
16268 static void RedrawSoundButtonGadget(int id)
16269 {
16270   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16271              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16272              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16273              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16274              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16275              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16276              id);
16277
16278   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16279   RedrawGadget(game_gadget[id2]);
16280 }
16281
16282 void MapGameButtons(void)
16283 {
16284   MapGameButtonsExt(FALSE);
16285 }
16286
16287 void UnmapGameButtons(void)
16288 {
16289   UnmapGameButtonsExt(FALSE);
16290 }
16291
16292 void RedrawGameButtons(void)
16293 {
16294   RedrawGameButtonsExt(FALSE);
16295 }
16296
16297 void MapGameButtonsOnTape(void)
16298 {
16299   MapGameButtonsExt(TRUE);
16300 }
16301
16302 void UnmapGameButtonsOnTape(void)
16303 {
16304   UnmapGameButtonsExt(TRUE);
16305 }
16306
16307 void RedrawGameButtonsOnTape(void)
16308 {
16309   RedrawGameButtonsExt(TRUE);
16310 }
16311
16312 static void GameUndoRedoExt(void)
16313 {
16314   ClearPlayerAction();
16315
16316   tape.pausing = TRUE;
16317
16318   RedrawPlayfield();
16319   UpdateAndDisplayGameControlValues();
16320
16321   DrawCompleteVideoDisplay();
16322   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16323   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16324   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16325
16326   BackToFront();
16327 }
16328
16329 static void GameUndo(int steps)
16330 {
16331   if (!CheckEngineSnapshotList())
16332     return;
16333
16334   LoadEngineSnapshot_Undo(steps);
16335
16336   GameUndoRedoExt();
16337 }
16338
16339 static void GameRedo(int steps)
16340 {
16341   if (!CheckEngineSnapshotList())
16342     return;
16343
16344   LoadEngineSnapshot_Redo(steps);
16345
16346   GameUndoRedoExt();
16347 }
16348
16349 static void HandleGameButtonsExt(int id, int button)
16350 {
16351   static boolean game_undo_executed = FALSE;
16352   int steps = BUTTON_STEPSIZE(button);
16353   boolean handle_game_buttons =
16354     (game_status == GAME_MODE_PLAYING ||
16355      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16356
16357   if (!handle_game_buttons)
16358     return;
16359
16360   switch (id)
16361   {
16362     case GAME_CTRL_ID_STOP:
16363     case GAME_CTRL_ID_PANEL_STOP:
16364     case GAME_CTRL_ID_TOUCH_STOP:
16365       if (game_status == GAME_MODE_MAIN)
16366         break;
16367
16368       if (tape.playing)
16369         TapeStop();
16370       else
16371         RequestQuitGame(FALSE);
16372
16373       break;
16374
16375     case GAME_CTRL_ID_PAUSE:
16376     case GAME_CTRL_ID_PAUSE2:
16377     case GAME_CTRL_ID_PANEL_PAUSE:
16378     case GAME_CTRL_ID_TOUCH_PAUSE:
16379       if (network.enabled && game_status == GAME_MODE_PLAYING)
16380       {
16381         if (tape.pausing)
16382           SendToServer_ContinuePlaying();
16383         else
16384           SendToServer_PausePlaying();
16385       }
16386       else
16387         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16388
16389       game_undo_executed = FALSE;
16390
16391       break;
16392
16393     case GAME_CTRL_ID_PLAY:
16394     case GAME_CTRL_ID_PANEL_PLAY:
16395       if (game_status == GAME_MODE_MAIN)
16396       {
16397         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16398       }
16399       else if (tape.pausing)
16400       {
16401         if (network.enabled)
16402           SendToServer_ContinuePlaying();
16403         else
16404           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16405       }
16406       break;
16407
16408     case GAME_CTRL_ID_UNDO:
16409       // Important: When using "save snapshot when collecting an item" mode,
16410       // load last (current) snapshot for first "undo" after pressing "pause"
16411       // (else the last-but-one snapshot would be loaded, because the snapshot
16412       // pointer already points to the last snapshot when pressing "pause",
16413       // which is fine for "every step/move" mode, but not for "every collect")
16414       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16415           !game_undo_executed)
16416         steps--;
16417
16418       game_undo_executed = TRUE;
16419
16420       GameUndo(steps);
16421       break;
16422
16423     case GAME_CTRL_ID_REDO:
16424       GameRedo(steps);
16425       break;
16426
16427     case GAME_CTRL_ID_SAVE:
16428       TapeQuickSave();
16429       break;
16430
16431     case GAME_CTRL_ID_LOAD:
16432       TapeQuickLoad();
16433       break;
16434
16435     case SOUND_CTRL_ID_MUSIC:
16436     case SOUND_CTRL_ID_PANEL_MUSIC:
16437       if (setup.sound_music)
16438       { 
16439         setup.sound_music = FALSE;
16440
16441         FadeMusic();
16442       }
16443       else if (audio.music_available)
16444       { 
16445         setup.sound = setup.sound_music = TRUE;
16446
16447         SetAudioMode(setup.sound);
16448
16449         if (game_status == GAME_MODE_PLAYING)
16450           PlayLevelMusic();
16451       }
16452
16453       RedrawSoundButtonGadget(id);
16454
16455       break;
16456
16457     case SOUND_CTRL_ID_LOOPS:
16458     case SOUND_CTRL_ID_PANEL_LOOPS:
16459       if (setup.sound_loops)
16460         setup.sound_loops = FALSE;
16461       else if (audio.loops_available)
16462       {
16463         setup.sound = setup.sound_loops = TRUE;
16464
16465         SetAudioMode(setup.sound);
16466       }
16467
16468       RedrawSoundButtonGadget(id);
16469
16470       break;
16471
16472     case SOUND_CTRL_ID_SIMPLE:
16473     case SOUND_CTRL_ID_PANEL_SIMPLE:
16474       if (setup.sound_simple)
16475         setup.sound_simple = FALSE;
16476       else if (audio.sound_available)
16477       {
16478         setup.sound = setup.sound_simple = TRUE;
16479
16480         SetAudioMode(setup.sound);
16481       }
16482
16483       RedrawSoundButtonGadget(id);
16484
16485       break;
16486
16487     default:
16488       break;
16489   }
16490 }
16491
16492 static void HandleGameButtons(struct GadgetInfo *gi)
16493 {
16494   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16495 }
16496
16497 void HandleSoundButtonKeys(Key key)
16498 {
16499   if (key == setup.shortcut.sound_simple)
16500     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16501   else if (key == setup.shortcut.sound_loops)
16502     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16503   else if (key == setup.shortcut.sound_music)
16504     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16505 }