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