added support for fourth digit for inventory (dynamite) count on game panel
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_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 CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_INVENTORY_COUNT ||
2685           nr == GAME_PANEL_TIME)
2686       {
2687         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2688
2689         if (use_dynamic_size)           // use dynamic number of digits
2690         {
2691           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2692           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2693           int size2 = size1 + 1;
2694           int font1 = pos->font;
2695           int font2 = pos->font_alt;
2696
2697           size = (value < value_change ? size1 : size2);
2698           font = (value < value_change ? font1 : font2);
2699         }
2700       }
2701
2702       // correct text size if "digits" is zero or less
2703       if (size <= 0)
2704         size = strlen(int2str(value, size));
2705
2706       // dynamically correct text alignment
2707       pos->width = size * getFontWidth(font);
2708
2709       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                   int2str(value, size), font, mask_mode);
2711     }
2712     else if (type == TYPE_ELEMENT)
2713     {
2714       int element, graphic;
2715       Bitmap *src_bitmap;
2716       int src_x, src_y;
2717       int width, height;
2718       int dst_x = PANEL_XPOS(pos);
2719       int dst_y = PANEL_YPOS(pos);
2720
2721       if (value != EL_UNDEFINED && value != EL_EMPTY)
2722       {
2723         element = value;
2724         graphic = el2panelimg(value);
2725
2726 #if 0
2727         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2728               element, EL_NAME(element), size);
2729 #endif
2730
2731         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2732           size = TILESIZE;
2733
2734         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2735                               &src_x, &src_y);
2736
2737         width  = graphic_info[graphic].width  * size / TILESIZE;
2738         height = graphic_info[graphic].height * size / TILESIZE;
2739
2740         if (draw_masked)
2741           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2742                            dst_x, dst_y);
2743         else
2744           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2745                      dst_x, dst_y);
2746       }
2747     }
2748     else if (type == TYPE_GRAPHIC)
2749     {
2750       int graphic        = gpc->graphic;
2751       int graphic_active = gpc->graphic_active;
2752       Bitmap *src_bitmap;
2753       int src_x, src_y;
2754       int width, height;
2755       int dst_x = PANEL_XPOS(pos);
2756       int dst_y = PANEL_YPOS(pos);
2757       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2758                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2759
2760       if (graphic != IMG_UNDEFINED && !skip)
2761       {
2762         if (pos->style == STYLE_REVERSE)
2763           value = 100 - value;
2764
2765         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2766
2767         if (pos->direction & MV_HORIZONTAL)
2768         {
2769           width  = graphic_info[graphic_active].width * value / 100;
2770           height = graphic_info[graphic_active].height;
2771
2772           if (pos->direction == MV_LEFT)
2773           {
2774             src_x += graphic_info[graphic_active].width - width;
2775             dst_x += graphic_info[graphic_active].width - width;
2776           }
2777         }
2778         else
2779         {
2780           width  = graphic_info[graphic_active].width;
2781           height = graphic_info[graphic_active].height * value / 100;
2782
2783           if (pos->direction == MV_UP)
2784           {
2785             src_y += graphic_info[graphic_active].height - height;
2786             dst_y += graphic_info[graphic_active].height - height;
2787           }
2788         }
2789
2790         if (draw_masked)
2791           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2792                            dst_x, dst_y);
2793         else
2794           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2795                      dst_x, dst_y);
2796
2797         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2798
2799         if (pos->direction & MV_HORIZONTAL)
2800         {
2801           if (pos->direction == MV_RIGHT)
2802           {
2803             src_x += width;
2804             dst_x += width;
2805           }
2806           else
2807           {
2808             dst_x = PANEL_XPOS(pos);
2809           }
2810
2811           width = graphic_info[graphic].width - width;
2812         }
2813         else
2814         {
2815           if (pos->direction == MV_DOWN)
2816           {
2817             src_y += height;
2818             dst_y += height;
2819           }
2820           else
2821           {
2822             dst_y = PANEL_YPOS(pos);
2823           }
2824
2825           height = graphic_info[graphic].height - height;
2826         }
2827
2828         if (draw_masked)
2829           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2830                            dst_x, dst_y);
2831         else
2832           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2833                      dst_x, dst_y);
2834       }
2835     }
2836     else if (type == TYPE_STRING)
2837     {
2838       boolean active = (value != 0);
2839       char *state_normal = "off";
2840       char *state_active = "on";
2841       char *state = (active ? state_active : state_normal);
2842       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2843                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2844                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2845                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2846
2847       if (nr == GAME_PANEL_GRAVITY_STATE)
2848       {
2849         int font1 = pos->font;          // (used for normal state)
2850         int font2 = pos->font_alt;      // (used for active state)
2851
2852         font = (active ? font2 : font1);
2853       }
2854
2855       if (s != NULL)
2856       {
2857         char *s_cut;
2858
2859         if (size <= 0)
2860         {
2861           // don't truncate output if "chars" is zero or less
2862           size = strlen(s);
2863
2864           // dynamically correct text alignment
2865           pos->width = size * getFontWidth(font);
2866         }
2867
2868         s_cut = getStringCopyN(s, size);
2869
2870         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2871                     s_cut, font, mask_mode);
2872
2873         free(s_cut);
2874       }
2875     }
2876
2877     redraw_mask |= REDRAW_DOOR_1;
2878   }
2879
2880   SetGameStatus(GAME_MODE_PLAYING);
2881 }
2882
2883 void UpdateAndDisplayGameControlValues(void)
2884 {
2885   if (tape.deactivate_display)
2886     return;
2887
2888   UpdateGameControlValues();
2889   DisplayGameControlValues();
2890 }
2891
2892 void UpdateGameDoorValues(void)
2893 {
2894   UpdateGameControlValues();
2895 }
2896
2897 void DrawGameDoorValues(void)
2898 {
2899   DisplayGameControlValues();
2900 }
2901
2902
2903 // ============================================================================
2904 // InitGameEngine()
2905 // ----------------------------------------------------------------------------
2906 // initialize game engine due to level / tape version number
2907 // ============================================================================
2908
2909 static void InitGameEngine(void)
2910 {
2911   int i, j, k, l, x, y;
2912
2913   // set game engine from tape file when re-playing, else from level file
2914   game.engine_version = (tape.playing ? tape.engine_version :
2915                          level.game_version);
2916
2917   // set single or multi-player game mode (needed for re-playing tapes)
2918   game.team_mode = setup.team_mode;
2919
2920   if (tape.playing)
2921   {
2922     int num_players = 0;
2923
2924     for (i = 0; i < MAX_PLAYERS; i++)
2925       if (tape.player_participates[i])
2926         num_players++;
2927
2928     // multi-player tapes contain input data for more than one player
2929     game.team_mode = (num_players > 1);
2930   }
2931
2932 #if 0
2933   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2934         level.game_version);
2935   Debug("game:init:level", "          tape.file_version   == %06d",
2936         tape.file_version);
2937   Debug("game:init:level", "          tape.game_version   == %06d",
2938         tape.game_version);
2939   Debug("game:init:level", "          tape.engine_version == %06d",
2940         tape.engine_version);
2941   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2942         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2943 #endif
2944
2945   // --------------------------------------------------------------------------
2946   // set flags for bugs and changes according to active game engine version
2947   // --------------------------------------------------------------------------
2948
2949   /*
2950     Summary of bugfix:
2951     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2952
2953     Bug was introduced in version:
2954     2.0.1
2955
2956     Bug was fixed in version:
2957     4.2.0.0
2958
2959     Description:
2960     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2961     but the property "can fall" was missing, which caused some levels to be
2962     unsolvable. This was fixed in version 4.2.0.0.
2963
2964     Affected levels/tapes:
2965     An example for a tape that was fixed by this bugfix is tape 029 from the
2966     level set "rnd_sam_bateman".
2967     The wrong behaviour will still be used for all levels or tapes that were
2968     created/recorded with it. An example for this is tape 023 from the level
2969     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2970   */
2971
2972   boolean use_amoeba_dropping_cannot_fall_bug =
2973     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2974       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2975      (tape.playing &&
2976       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2977       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2978
2979   /*
2980     Summary of bugfix/change:
2981     Fixed move speed of elements entering or leaving magic wall.
2982
2983     Fixed/changed in version:
2984     2.0.1
2985
2986     Description:
2987     Before 2.0.1, move speed of elements entering or leaving magic wall was
2988     twice as fast as it is now.
2989     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2990
2991     Affected levels/tapes:
2992     The first condition is generally needed for all levels/tapes before version
2993     2.0.1, which might use the old behaviour before it was changed; known tapes
2994     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2995     The second condition is an exception from the above case and is needed for
2996     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2997     above, but before it was known that this change would break tapes like the
2998     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2999     although the engine version while recording maybe was before 2.0.1. There
3000     are a lot of tapes that are affected by this exception, like tape 006 from
3001     the level set "rnd_conor_mancone".
3002   */
3003
3004   boolean use_old_move_stepsize_for_magic_wall =
3005     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3006      !(tape.playing &&
3007        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3008        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3009
3010   /*
3011     Summary of bugfix/change:
3012     Fixed handling for custom elements that change when pushed by the player.
3013
3014     Fixed/changed in version:
3015     3.1.0
3016
3017     Description:
3018     Before 3.1.0, custom elements that "change when pushing" changed directly
3019     after the player started pushing them (until then handled in "DigField()").
3020     Since 3.1.0, these custom elements are not changed until the "pushing"
3021     move of the element is finished (now handled in "ContinueMoving()").
3022
3023     Affected levels/tapes:
3024     The first condition is generally needed for all levels/tapes before version
3025     3.1.0, which might use the old behaviour before it was changed; known tapes
3026     that are affected are some tapes from the level set "Walpurgis Gardens" by
3027     Jamie Cullen.
3028     The second condition is an exception from the above case and is needed for
3029     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3030     above (including some development versions of 3.1.0), but before it was
3031     known that this change would break tapes like the above and was fixed in
3032     3.1.1, so that the changed behaviour was active although the engine version
3033     while recording maybe was before 3.1.0. There is at least one tape that is
3034     affected by this exception, which is the tape for the one-level set "Bug
3035     Machine" by Juergen Bonhagen.
3036   */
3037
3038   game.use_change_when_pushing_bug =
3039     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3040      !(tape.playing &&
3041        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3042        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3043
3044   /*
3045     Summary of bugfix/change:
3046     Fixed handling for blocking the field the player leaves when moving.
3047
3048     Fixed/changed in version:
3049     3.1.1
3050
3051     Description:
3052     Before 3.1.1, when "block last field when moving" was enabled, the field
3053     the player is leaving when moving was blocked for the time of the move,
3054     and was directly unblocked afterwards. This resulted in the last field
3055     being blocked for exactly one less than the number of frames of one player
3056     move. Additionally, even when blocking was disabled, the last field was
3057     blocked for exactly one frame.
3058     Since 3.1.1, due to changes in player movement handling, the last field
3059     is not blocked at all when blocking is disabled. When blocking is enabled,
3060     the last field is blocked for exactly the number of frames of one player
3061     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3062     last field is blocked for exactly one more than the number of frames of
3063     one player move.
3064
3065     Affected levels/tapes:
3066     (!!! yet to be determined -- probably many !!!)
3067   */
3068
3069   game.use_block_last_field_bug =
3070     (game.engine_version < VERSION_IDENT(3,1,1,0));
3071
3072   /* various special flags and settings for native Emerald Mine game engine */
3073
3074   game_em.use_single_button =
3075     (game.engine_version > VERSION_IDENT(4,0,0,2));
3076
3077   game_em.use_snap_key_bug =
3078     (game.engine_version < VERSION_IDENT(4,0,1,0));
3079
3080   game_em.use_random_bug =
3081     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3082
3083   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3084
3085   game_em.use_old_explosions            = use_old_em_engine;
3086   game_em.use_old_android               = use_old_em_engine;
3087   game_em.use_old_push_elements         = use_old_em_engine;
3088   game_em.use_old_push_into_acid        = use_old_em_engine;
3089
3090   game_em.use_wrap_around               = !use_old_em_engine;
3091
3092   // --------------------------------------------------------------------------
3093
3094   // set maximal allowed number of custom element changes per game frame
3095   game.max_num_changes_per_frame = 1;
3096
3097   // default scan direction: scan playfield from top/left to bottom/right
3098   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3099
3100   // dynamically adjust element properties according to game engine version
3101   InitElementPropertiesEngine(game.engine_version);
3102
3103   // ---------- initialize special element properties -------------------------
3104
3105   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3106   if (use_amoeba_dropping_cannot_fall_bug)
3107     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3108
3109   // ---------- initialize player's initial move delay ------------------------
3110
3111   // dynamically adjust player properties according to level information
3112   for (i = 0; i < MAX_PLAYERS; i++)
3113     game.initial_move_delay_value[i] =
3114       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3115
3116   // dynamically adjust player properties according to game engine version
3117   for (i = 0; i < MAX_PLAYERS; i++)
3118     game.initial_move_delay[i] =
3119       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3120        game.initial_move_delay_value[i] : 0);
3121
3122   // ---------- initialize player's initial push delay ------------------------
3123
3124   // dynamically adjust player properties according to game engine version
3125   game.initial_push_delay_value =
3126     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3127
3128   // ---------- initialize changing elements ----------------------------------
3129
3130   // initialize changing elements information
3131   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3132   {
3133     struct ElementInfo *ei = &element_info[i];
3134
3135     // this pointer might have been changed in the level editor
3136     ei->change = &ei->change_page[0];
3137
3138     if (!IS_CUSTOM_ELEMENT(i))
3139     {
3140       ei->change->target_element = EL_EMPTY_SPACE;
3141       ei->change->delay_fixed = 0;
3142       ei->change->delay_random = 0;
3143       ei->change->delay_frames = 1;
3144     }
3145
3146     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3147     {
3148       ei->has_change_event[j] = FALSE;
3149
3150       ei->event_page_nr[j] = 0;
3151       ei->event_page[j] = &ei->change_page[0];
3152     }
3153   }
3154
3155   // add changing elements from pre-defined list
3156   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3157   {
3158     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3159     struct ElementInfo *ei = &element_info[ch_delay->element];
3160
3161     ei->change->target_element       = ch_delay->target_element;
3162     ei->change->delay_fixed          = ch_delay->change_delay;
3163
3164     ei->change->pre_change_function  = ch_delay->pre_change_function;
3165     ei->change->change_function      = ch_delay->change_function;
3166     ei->change->post_change_function = ch_delay->post_change_function;
3167
3168     ei->change->can_change = TRUE;
3169     ei->change->can_change_or_has_action = TRUE;
3170
3171     ei->has_change_event[CE_DELAY] = TRUE;
3172
3173     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3174     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3175   }
3176
3177   // ---------- initialize internal run-time variables ------------------------
3178
3179   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3180   {
3181     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3182
3183     for (j = 0; j < ei->num_change_pages; j++)
3184     {
3185       ei->change_page[j].can_change_or_has_action =
3186         (ei->change_page[j].can_change |
3187          ei->change_page[j].has_action);
3188     }
3189   }
3190
3191   // add change events from custom element configuration
3192   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3193   {
3194     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3195
3196     for (j = 0; j < ei->num_change_pages; j++)
3197     {
3198       if (!ei->change_page[j].can_change_or_has_action)
3199         continue;
3200
3201       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3202       {
3203         // only add event page for the first page found with this event
3204         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3205         {
3206           ei->has_change_event[k] = TRUE;
3207
3208           ei->event_page_nr[k] = j;
3209           ei->event_page[k] = &ei->change_page[j];
3210         }
3211       }
3212     }
3213   }
3214
3215   // ---------- initialize reference elements in change conditions ------------
3216
3217   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3218   {
3219     int element = EL_CUSTOM_START + i;
3220     struct ElementInfo *ei = &element_info[element];
3221
3222     for (j = 0; j < ei->num_change_pages; j++)
3223     {
3224       int trigger_element = ei->change_page[j].initial_trigger_element;
3225
3226       if (trigger_element >= EL_PREV_CE_8 &&
3227           trigger_element <= EL_NEXT_CE_8)
3228         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3229
3230       ei->change_page[j].trigger_element = trigger_element;
3231     }
3232   }
3233
3234   // ---------- initialize run-time trigger player and element ----------------
3235
3236   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3237   {
3238     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3239
3240     for (j = 0; j < ei->num_change_pages; j++)
3241     {
3242       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3243       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3244       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3245       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3246       ei->change_page[j].actual_trigger_ce_value = 0;
3247       ei->change_page[j].actual_trigger_ce_score = 0;
3248     }
3249   }
3250
3251   // ---------- initialize trigger events -------------------------------------
3252
3253   // initialize trigger events information
3254   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3255     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3256       trigger_events[i][j] = FALSE;
3257
3258   // add trigger events from element change event properties
3259   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3260   {
3261     struct ElementInfo *ei = &element_info[i];
3262
3263     for (j = 0; j < ei->num_change_pages; j++)
3264     {
3265       if (!ei->change_page[j].can_change_or_has_action)
3266         continue;
3267
3268       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3269       {
3270         int trigger_element = ei->change_page[j].trigger_element;
3271
3272         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3273         {
3274           if (ei->change_page[j].has_event[k])
3275           {
3276             if (IS_GROUP_ELEMENT(trigger_element))
3277             {
3278               struct ElementGroupInfo *group =
3279                 element_info[trigger_element].group;
3280
3281               for (l = 0; l < group->num_elements_resolved; l++)
3282                 trigger_events[group->element_resolved[l]][k] = TRUE;
3283             }
3284             else if (trigger_element == EL_ANY_ELEMENT)
3285               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3286                 trigger_events[l][k] = TRUE;
3287             else
3288               trigger_events[trigger_element][k] = TRUE;
3289           }
3290         }
3291       }
3292     }
3293   }
3294
3295   // ---------- initialize push delay -----------------------------------------
3296
3297   // initialize push delay values to default
3298   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3299   {
3300     if (!IS_CUSTOM_ELEMENT(i))
3301     {
3302       // set default push delay values (corrected since version 3.0.7-1)
3303       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3304       {
3305         element_info[i].push_delay_fixed = 2;
3306         element_info[i].push_delay_random = 8;
3307       }
3308       else
3309       {
3310         element_info[i].push_delay_fixed = 8;
3311         element_info[i].push_delay_random = 8;
3312       }
3313     }
3314   }
3315
3316   // set push delay value for certain elements from pre-defined list
3317   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3318   {
3319     int e = push_delay_list[i].element;
3320
3321     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3322     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3323   }
3324
3325   // set push delay value for Supaplex elements for newer engine versions
3326   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3327   {
3328     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3329     {
3330       if (IS_SP_ELEMENT(i))
3331       {
3332         // set SP push delay to just enough to push under a falling zonk
3333         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3334
3335         element_info[i].push_delay_fixed  = delay;
3336         element_info[i].push_delay_random = 0;
3337       }
3338     }
3339   }
3340
3341   // ---------- initialize move stepsize --------------------------------------
3342
3343   // initialize move stepsize values to default
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345     if (!IS_CUSTOM_ELEMENT(i))
3346       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3347
3348   // set move stepsize value for certain elements from pre-defined list
3349   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3350   {
3351     int e = move_stepsize_list[i].element;
3352
3353     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3354
3355     // set move stepsize value for certain elements for older engine versions
3356     if (use_old_move_stepsize_for_magic_wall)
3357     {
3358       if (e == EL_MAGIC_WALL_FILLING ||
3359           e == EL_MAGIC_WALL_EMPTYING ||
3360           e == EL_BD_MAGIC_WALL_FILLING ||
3361           e == EL_BD_MAGIC_WALL_EMPTYING)
3362         element_info[e].move_stepsize *= 2;
3363     }
3364   }
3365
3366   // ---------- initialize collect score --------------------------------------
3367
3368   // initialize collect score values for custom elements from initial value
3369   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3370     if (IS_CUSTOM_ELEMENT(i))
3371       element_info[i].collect_score = element_info[i].collect_score_initial;
3372
3373   // ---------- initialize collect count --------------------------------------
3374
3375   // initialize collect count values for non-custom elements
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].collect_count_initial = 0;
3379
3380   // add collect count values for all elements from pre-defined list
3381   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3382     element_info[collect_count_list[i].element].collect_count_initial =
3383       collect_count_list[i].count;
3384
3385   // ---------- initialize access direction -----------------------------------
3386
3387   // initialize access direction values to default (access from every side)
3388   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3389     if (!IS_CUSTOM_ELEMENT(i))
3390       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3391
3392   // set access direction value for certain elements from pre-defined list
3393   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3394     element_info[access_direction_list[i].element].access_direction =
3395       access_direction_list[i].direction;
3396
3397   // ---------- initialize explosion content ----------------------------------
3398   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3399   {
3400     if (IS_CUSTOM_ELEMENT(i))
3401       continue;
3402
3403     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3404     {
3405       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3406
3407       element_info[i].content.e[x][y] =
3408         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3409          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3410          i == EL_PLAYER_3 ? EL_EMERALD :
3411          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3412          i == EL_MOLE ? EL_EMERALD_RED :
3413          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3414          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3415          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3416          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3417          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3418          i == EL_WALL_EMERALD ? EL_EMERALD :
3419          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3420          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3421          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3422          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3423          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3424          i == EL_WALL_PEARL ? EL_PEARL :
3425          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3426          EL_EMPTY);
3427     }
3428   }
3429
3430   // ---------- initialize recursion detection --------------------------------
3431   recursion_loop_depth = 0;
3432   recursion_loop_detected = FALSE;
3433   recursion_loop_element = EL_UNDEFINED;
3434
3435   // ---------- initialize graphics engine ------------------------------------
3436   game.scroll_delay_value =
3437     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3438      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3439      !setup.forced_scroll_delay           ? 0 :
3440      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3441   game.scroll_delay_value =
3442     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3443
3444   // ---------- initialize game engine snapshots ------------------------------
3445   for (i = 0; i < MAX_PLAYERS; i++)
3446     game.snapshot.last_action[i] = 0;
3447   game.snapshot.changed_action = FALSE;
3448   game.snapshot.collected_item = FALSE;
3449   game.snapshot.mode =
3450     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3451      SNAPSHOT_MODE_EVERY_STEP :
3452      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3453      SNAPSHOT_MODE_EVERY_MOVE :
3454      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3455      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3456   game.snapshot.save_snapshot = FALSE;
3457
3458   // ---------- initialize level time for Supaplex engine ---------------------
3459   // Supaplex levels with time limit currently unsupported -- should be added
3460   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3461     level.time = 0;
3462
3463   // ---------- initialize flags for handling game actions --------------------
3464
3465   // set flags for game actions to default values
3466   game.use_key_actions = TRUE;
3467   game.use_mouse_actions = FALSE;
3468
3469   // when using Mirror Magic game engine, handle mouse events only
3470   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3471   {
3472     game.use_key_actions = FALSE;
3473     game.use_mouse_actions = TRUE;
3474   }
3475
3476   // check for custom elements with mouse click events
3477   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3478   {
3479     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3480     {
3481       int element = EL_CUSTOM_START + i;
3482
3483       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3484           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3485           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3486           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3487         game.use_mouse_actions = TRUE;
3488     }
3489   }
3490 }
3491
3492 static int get_num_special_action(int element, int action_first,
3493                                   int action_last)
3494 {
3495   int num_special_action = 0;
3496   int i, j;
3497
3498   for (i = action_first; i <= action_last; i++)
3499   {
3500     boolean found = FALSE;
3501
3502     for (j = 0; j < NUM_DIRECTIONS; j++)
3503       if (el_act_dir2img(element, i, j) !=
3504           el_act_dir2img(element, ACTION_DEFAULT, j))
3505         found = TRUE;
3506
3507     if (found)
3508       num_special_action++;
3509     else
3510       break;
3511   }
3512
3513   return num_special_action;
3514 }
3515
3516
3517 // ============================================================================
3518 // InitGame()
3519 // ----------------------------------------------------------------------------
3520 // initialize and start new game
3521 // ============================================================================
3522
3523 #if DEBUG_INIT_PLAYER
3524 static void DebugPrintPlayerStatus(char *message)
3525 {
3526   int i;
3527
3528   if (!options.debug)
3529     return;
3530
3531   Debug("game:init:player", "%s:", message);
3532
3533   for (i = 0; i < MAX_PLAYERS; i++)
3534   {
3535     struct PlayerInfo *player = &stored_player[i];
3536
3537     Debug("game:init:player",
3538           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3539           i + 1,
3540           player->present,
3541           player->connected,
3542           player->connected_locally,
3543           player->connected_network,
3544           player->active,
3545           (local_player == player ? " (local player)" : ""));
3546   }
3547 }
3548 #endif
3549
3550 void InitGame(void)
3551 {
3552   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3553   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3554   int fade_mask = REDRAW_FIELD;
3555
3556   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3557   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3558   int initial_move_dir = MV_DOWN;
3559   int i, j, x, y;
3560
3561   // required here to update video display before fading (FIX THIS)
3562   DrawMaskedBorder(REDRAW_DOOR_2);
3563
3564   if (!game.restart_level)
3565     CloseDoor(DOOR_CLOSE_1);
3566
3567   SetGameStatus(GAME_MODE_PLAYING);
3568
3569   if (level_editor_test_game)
3570     FadeSkipNextFadeOut();
3571   else
3572     FadeSetEnterScreen();
3573
3574   if (CheckFadeAll())
3575     fade_mask = REDRAW_ALL;
3576
3577   FadeLevelSoundsAndMusic();
3578
3579   ExpireSoundLoops(TRUE);
3580
3581   FadeOut(fade_mask);
3582
3583   if (level_editor_test_game)
3584     FadeSkipNextFadeIn();
3585
3586   // needed if different viewport properties defined for playing
3587   ChangeViewportPropertiesIfNeeded();
3588
3589   ClearField();
3590
3591   DrawCompleteVideoDisplay();
3592
3593   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3594
3595   InitGameEngine();
3596   InitGameControlValues();
3597
3598   if (tape.recording)
3599   {
3600     // initialize tape actions from game when recording tape
3601     tape.use_key_actions   = game.use_key_actions;
3602     tape.use_mouse_actions = game.use_mouse_actions;
3603
3604     // initialize visible playfield size when recording tape (for team mode)
3605     tape.scr_fieldx = SCR_FIELDX;
3606     tape.scr_fieldy = SCR_FIELDY;
3607   }
3608
3609   // don't play tapes over network
3610   network_playing = (network.enabled && !tape.playing);
3611
3612   for (i = 0; i < MAX_PLAYERS; i++)
3613   {
3614     struct PlayerInfo *player = &stored_player[i];
3615
3616     player->index_nr = i;
3617     player->index_bit = (1 << i);
3618     player->element_nr = EL_PLAYER_1 + i;
3619
3620     player->present = FALSE;
3621     player->active = FALSE;
3622     player->mapped = FALSE;
3623
3624     player->killed = FALSE;
3625     player->reanimated = FALSE;
3626     player->buried = FALSE;
3627
3628     player->action = 0;
3629     player->effective_action = 0;
3630     player->programmed_action = 0;
3631     player->snap_action = 0;
3632
3633     player->mouse_action.lx = 0;
3634     player->mouse_action.ly = 0;
3635     player->mouse_action.button = 0;
3636     player->mouse_action.button_hint = 0;
3637
3638     player->effective_mouse_action.lx = 0;
3639     player->effective_mouse_action.ly = 0;
3640     player->effective_mouse_action.button = 0;
3641     player->effective_mouse_action.button_hint = 0;
3642
3643     for (j = 0; j < MAX_NUM_KEYS; j++)
3644       player->key[j] = FALSE;
3645
3646     player->num_white_keys = 0;
3647
3648     player->dynabomb_count = 0;
3649     player->dynabomb_size = 1;
3650     player->dynabombs_left = 0;
3651     player->dynabomb_xl = FALSE;
3652
3653     player->MovDir = initial_move_dir;
3654     player->MovPos = 0;
3655     player->GfxPos = 0;
3656     player->GfxDir = initial_move_dir;
3657     player->GfxAction = ACTION_DEFAULT;
3658     player->Frame = 0;
3659     player->StepFrame = 0;
3660
3661     player->initial_element = player->element_nr;
3662     player->artwork_element =
3663       (level.use_artwork_element[i] ? level.artwork_element[i] :
3664        player->element_nr);
3665     player->use_murphy = FALSE;
3666
3667     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3668     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3669
3670     player->gravity = level.initial_player_gravity[i];
3671
3672     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3673
3674     player->actual_frame_counter = 0;
3675
3676     player->step_counter = 0;
3677
3678     player->last_move_dir = initial_move_dir;
3679
3680     player->is_active = FALSE;
3681
3682     player->is_waiting = FALSE;
3683     player->is_moving = FALSE;
3684     player->is_auto_moving = FALSE;
3685     player->is_digging = FALSE;
3686     player->is_snapping = FALSE;
3687     player->is_collecting = FALSE;
3688     player->is_pushing = FALSE;
3689     player->is_switching = FALSE;
3690     player->is_dropping = FALSE;
3691     player->is_dropping_pressed = FALSE;
3692
3693     player->is_bored = FALSE;
3694     player->is_sleeping = FALSE;
3695
3696     player->was_waiting = TRUE;
3697     player->was_moving = FALSE;
3698     player->was_snapping = FALSE;
3699     player->was_dropping = FALSE;
3700
3701     player->force_dropping = FALSE;
3702
3703     player->frame_counter_bored = -1;
3704     player->frame_counter_sleeping = -1;
3705
3706     player->anim_delay_counter = 0;
3707     player->post_delay_counter = 0;
3708
3709     player->dir_waiting = initial_move_dir;
3710     player->action_waiting = ACTION_DEFAULT;
3711     player->last_action_waiting = ACTION_DEFAULT;
3712     player->special_action_bored = ACTION_DEFAULT;
3713     player->special_action_sleeping = ACTION_DEFAULT;
3714
3715     player->switch_x = -1;
3716     player->switch_y = -1;
3717
3718     player->drop_x = -1;
3719     player->drop_y = -1;
3720
3721     player->show_envelope = 0;
3722
3723     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3724
3725     player->push_delay       = -1;      // initialized when pushing starts
3726     player->push_delay_value = game.initial_push_delay_value;
3727
3728     player->drop_delay = 0;
3729     player->drop_pressed_delay = 0;
3730
3731     player->last_jx = -1;
3732     player->last_jy = -1;
3733     player->jx = -1;
3734     player->jy = -1;
3735
3736     player->shield_normal_time_left = 0;
3737     player->shield_deadly_time_left = 0;
3738
3739     player->last_removed_element = EL_UNDEFINED;
3740
3741     player->inventory_infinite_element = EL_UNDEFINED;
3742     player->inventory_size = 0;
3743
3744     if (level.use_initial_inventory[i])
3745     {
3746       for (j = 0; j < level.initial_inventory_size[i]; j++)
3747       {
3748         int element = level.initial_inventory_content[i][j];
3749         int collect_count = element_info[element].collect_count_initial;
3750         int k;
3751
3752         if (!IS_CUSTOM_ELEMENT(element))
3753           collect_count = 1;
3754
3755         if (collect_count == 0)
3756           player->inventory_infinite_element = element;
3757         else
3758           for (k = 0; k < collect_count; k++)
3759             if (player->inventory_size < MAX_INVENTORY_SIZE)
3760               player->inventory_element[player->inventory_size++] = element;
3761       }
3762     }
3763
3764     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3765     SnapField(player, 0, 0);
3766
3767     map_player_action[i] = i;
3768   }
3769
3770   network_player_action_received = FALSE;
3771
3772   // initial null action
3773   if (network_playing)
3774     SendToServer_MovePlayer(MV_NONE);
3775
3776   FrameCounter = 0;
3777   TimeFrames = 0;
3778   TimePlayed = 0;
3779   TimeLeft = level.time;
3780   TapeTime = 0;
3781
3782   ScreenMovDir = MV_NONE;
3783   ScreenMovPos = 0;
3784   ScreenGfxPos = 0;
3785
3786   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3787
3788   game.robot_wheel_x = -1;
3789   game.robot_wheel_y = -1;
3790
3791   game.exit_x = -1;
3792   game.exit_y = -1;
3793
3794   game.all_players_gone = FALSE;
3795
3796   game.LevelSolved = FALSE;
3797   game.GameOver = FALSE;
3798
3799   game.GamePlayed = !tape.playing;
3800
3801   game.LevelSolved_GameWon = FALSE;
3802   game.LevelSolved_GameEnd = FALSE;
3803   game.LevelSolved_SaveTape = FALSE;
3804   game.LevelSolved_SaveScore = FALSE;
3805
3806   game.LevelSolved_CountingTime = 0;
3807   game.LevelSolved_CountingScore = 0;
3808   game.LevelSolved_CountingHealth = 0;
3809
3810   game.panel.active = TRUE;
3811
3812   game.no_time_limit = (level.time == 0);
3813
3814   game.yamyam_content_nr = 0;
3815   game.robot_wheel_active = FALSE;
3816   game.magic_wall_active = FALSE;
3817   game.magic_wall_time_left = 0;
3818   game.light_time_left = 0;
3819   game.timegate_time_left = 0;
3820   game.switchgate_pos = 0;
3821   game.wind_direction = level.wind_direction_initial;
3822
3823   game.time_final = 0;
3824   game.score_time_final = 0;
3825
3826   game.score = 0;
3827   game.score_final = 0;
3828
3829   game.health = MAX_HEALTH;
3830   game.health_final = MAX_HEALTH;
3831
3832   game.gems_still_needed = level.gems_needed;
3833   game.sokoban_fields_still_needed = 0;
3834   game.sokoban_objects_still_needed = 0;
3835   game.lights_still_needed = 0;
3836   game.players_still_needed = 0;
3837   game.friends_still_needed = 0;
3838
3839   game.lenses_time_left = 0;
3840   game.magnify_time_left = 0;
3841
3842   game.ball_active = level.ball_active_initial;
3843   game.ball_content_nr = 0;
3844
3845   game.explosions_delayed = TRUE;
3846
3847   game.envelope_active = FALSE;
3848
3849   // special case: set custom artwork setting to initial value
3850   game.use_masked_elements = game.use_masked_elements_initial;
3851
3852   for (i = 0; i < NUM_BELTS; i++)
3853   {
3854     game.belt_dir[i] = MV_NONE;
3855     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3856   }
3857
3858   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3859     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3860
3861 #if DEBUG_INIT_PLAYER
3862   DebugPrintPlayerStatus("Player status at level initialization");
3863 #endif
3864
3865   SCAN_PLAYFIELD(x, y)
3866   {
3867     Tile[x][y] = Last[x][y] = level.field[x][y];
3868     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3869     ChangeDelay[x][y] = 0;
3870     ChangePage[x][y] = -1;
3871     CustomValue[x][y] = 0;              // initialized in InitField()
3872     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3873     AmoebaNr[x][y] = 0;
3874     WasJustMoving[x][y] = 0;
3875     WasJustFalling[x][y] = 0;
3876     CheckCollision[x][y] = 0;
3877     CheckImpact[x][y] = 0;
3878     Stop[x][y] = FALSE;
3879     Pushed[x][y] = FALSE;
3880
3881     ChangeCount[x][y] = 0;
3882     ChangeEvent[x][y] = -1;
3883
3884     ExplodePhase[x][y] = 0;
3885     ExplodeDelay[x][y] = 0;
3886     ExplodeField[x][y] = EX_TYPE_NONE;
3887
3888     RunnerVisit[x][y] = 0;
3889     PlayerVisit[x][y] = 0;
3890
3891     GfxFrame[x][y] = 0;
3892     GfxRandom[x][y] = INIT_GFX_RANDOM();
3893     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3894     GfxElement[x][y] = EL_UNDEFINED;
3895     GfxElementEmpty[x][y] = EL_EMPTY;
3896     GfxAction[x][y] = ACTION_DEFAULT;
3897     GfxDir[x][y] = MV_NONE;
3898     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3899   }
3900
3901   SCAN_PLAYFIELD(x, y)
3902   {
3903     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3904       emulate_bd = FALSE;
3905     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3906       emulate_sp = FALSE;
3907
3908     InitField(x, y, TRUE);
3909
3910     ResetGfxAnimation(x, y);
3911   }
3912
3913   InitBeltMovement();
3914
3915   for (i = 0; i < MAX_PLAYERS; i++)
3916   {
3917     struct PlayerInfo *player = &stored_player[i];
3918
3919     // set number of special actions for bored and sleeping animation
3920     player->num_special_action_bored =
3921       get_num_special_action(player->artwork_element,
3922                              ACTION_BORING_1, ACTION_BORING_LAST);
3923     player->num_special_action_sleeping =
3924       get_num_special_action(player->artwork_element,
3925                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3926   }
3927
3928   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3929                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3930
3931   // initialize type of slippery elements
3932   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3933   {
3934     if (!IS_CUSTOM_ELEMENT(i))
3935     {
3936       // default: elements slip down either to the left or right randomly
3937       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3938
3939       // SP style elements prefer to slip down on the left side
3940       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3941         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3942
3943       // BD style elements prefer to slip down on the left side
3944       if (game.emulation == EMU_BOULDERDASH)
3945         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3946     }
3947   }
3948
3949   // initialize explosion and ignition delay
3950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3951   {
3952     if (!IS_CUSTOM_ELEMENT(i))
3953     {
3954       int num_phase = 8;
3955       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3956                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3957                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3958       int last_phase = (num_phase + 1) * delay;
3959       int half_phase = (num_phase / 2) * delay;
3960
3961       element_info[i].explosion_delay = last_phase - 1;
3962       element_info[i].ignition_delay = half_phase;
3963
3964       if (i == EL_BLACK_ORB)
3965         element_info[i].ignition_delay = 1;
3966     }
3967   }
3968
3969   // correct non-moving belts to start moving left
3970   for (i = 0; i < NUM_BELTS; i++)
3971     if (game.belt_dir[i] == MV_NONE)
3972       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3973
3974 #if USE_NEW_PLAYER_ASSIGNMENTS
3975   // use preferred player also in local single-player mode
3976   if (!network.enabled && !game.team_mode)
3977   {
3978     int new_index_nr = setup.network_player_nr;
3979
3980     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3981     {
3982       for (i = 0; i < MAX_PLAYERS; i++)
3983         stored_player[i].connected_locally = FALSE;
3984
3985       stored_player[new_index_nr].connected_locally = TRUE;
3986     }
3987   }
3988
3989   for (i = 0; i < MAX_PLAYERS; i++)
3990   {
3991     stored_player[i].connected = FALSE;
3992
3993     // in network game mode, the local player might not be the first player
3994     if (stored_player[i].connected_locally)
3995       local_player = &stored_player[i];
3996   }
3997
3998   if (!network.enabled)
3999     local_player->connected = TRUE;
4000
4001   if (tape.playing)
4002   {
4003     for (i = 0; i < MAX_PLAYERS; i++)
4004       stored_player[i].connected = tape.player_participates[i];
4005   }
4006   else if (network.enabled)
4007   {
4008     // add team mode players connected over the network (needed for correct
4009     // assignment of player figures from level to locally playing players)
4010
4011     for (i = 0; i < MAX_PLAYERS; i++)
4012       if (stored_player[i].connected_network)
4013         stored_player[i].connected = TRUE;
4014   }
4015   else if (game.team_mode)
4016   {
4017     // try to guess locally connected team mode players (needed for correct
4018     // assignment of player figures from level to locally playing players)
4019
4020     for (i = 0; i < MAX_PLAYERS; i++)
4021       if (setup.input[i].use_joystick ||
4022           setup.input[i].key.left != KSYM_UNDEFINED)
4023         stored_player[i].connected = TRUE;
4024   }
4025
4026 #if DEBUG_INIT_PLAYER
4027   DebugPrintPlayerStatus("Player status after level initialization");
4028 #endif
4029
4030 #if DEBUG_INIT_PLAYER
4031   Debug("game:init:player", "Reassigning players ...");
4032 #endif
4033
4034   // check if any connected player was not found in playfield
4035   for (i = 0; i < MAX_PLAYERS; i++)
4036   {
4037     struct PlayerInfo *player = &stored_player[i];
4038
4039     if (player->connected && !player->present)
4040     {
4041       struct PlayerInfo *field_player = NULL;
4042
4043 #if DEBUG_INIT_PLAYER
4044       Debug("game:init:player",
4045             "- looking for field player for player %d ...", i + 1);
4046 #endif
4047
4048       // assign first free player found that is present in the playfield
4049
4050       // first try: look for unmapped playfield player that is not connected
4051       for (j = 0; j < MAX_PLAYERS; j++)
4052         if (field_player == NULL &&
4053             stored_player[j].present &&
4054             !stored_player[j].mapped &&
4055             !stored_player[j].connected)
4056           field_player = &stored_player[j];
4057
4058       // second try: look for *any* unmapped playfield player
4059       for (j = 0; j < MAX_PLAYERS; j++)
4060         if (field_player == NULL &&
4061             stored_player[j].present &&
4062             !stored_player[j].mapped)
4063           field_player = &stored_player[j];
4064
4065       if (field_player != NULL)
4066       {
4067         int jx = field_player->jx, jy = field_player->jy;
4068
4069 #if DEBUG_INIT_PLAYER
4070         Debug("game:init:player", "- found player %d",
4071               field_player->index_nr + 1);
4072 #endif
4073
4074         player->present = FALSE;
4075         player->active = FALSE;
4076
4077         field_player->present = TRUE;
4078         field_player->active = TRUE;
4079
4080         /*
4081         player->initial_element = field_player->initial_element;
4082         player->artwork_element = field_player->artwork_element;
4083
4084         player->block_last_field       = field_player->block_last_field;
4085         player->block_delay_adjustment = field_player->block_delay_adjustment;
4086         */
4087
4088         StorePlayer[jx][jy] = field_player->element_nr;
4089
4090         field_player->jx = field_player->last_jx = jx;
4091         field_player->jy = field_player->last_jy = jy;
4092
4093         if (local_player == player)
4094           local_player = field_player;
4095
4096         map_player_action[field_player->index_nr] = i;
4097
4098         field_player->mapped = TRUE;
4099
4100 #if DEBUG_INIT_PLAYER
4101         Debug("game:init:player", "- map_player_action[%d] == %d",
4102               field_player->index_nr + 1, i + 1);
4103 #endif
4104       }
4105     }
4106
4107     if (player->connected && player->present)
4108       player->mapped = TRUE;
4109   }
4110
4111 #if DEBUG_INIT_PLAYER
4112   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4113 #endif
4114
4115 #else
4116
4117   // check if any connected player was not found in playfield
4118   for (i = 0; i < MAX_PLAYERS; i++)
4119   {
4120     struct PlayerInfo *player = &stored_player[i];
4121
4122     if (player->connected && !player->present)
4123     {
4124       for (j = 0; j < MAX_PLAYERS; j++)
4125       {
4126         struct PlayerInfo *field_player = &stored_player[j];
4127         int jx = field_player->jx, jy = field_player->jy;
4128
4129         // assign first free player found that is present in the playfield
4130         if (field_player->present && !field_player->connected)
4131         {
4132           player->present = TRUE;
4133           player->active = TRUE;
4134
4135           field_player->present = FALSE;
4136           field_player->active = FALSE;
4137
4138           player->initial_element = field_player->initial_element;
4139           player->artwork_element = field_player->artwork_element;
4140
4141           player->block_last_field       = field_player->block_last_field;
4142           player->block_delay_adjustment = field_player->block_delay_adjustment;
4143
4144           StorePlayer[jx][jy] = player->element_nr;
4145
4146           player->jx = player->last_jx = jx;
4147           player->jy = player->last_jy = jy;
4148
4149           break;
4150         }
4151       }
4152     }
4153   }
4154 #endif
4155
4156 #if 0
4157   Debug("game:init:player", "local_player->present == %d",
4158         local_player->present);
4159 #endif
4160
4161   // set focus to local player for network games, else to all players
4162   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4163   game.centered_player_nr_next = game.centered_player_nr;
4164   game.set_centered_player = FALSE;
4165   game.set_centered_player_wrap = FALSE;
4166
4167   if (network_playing && tape.recording)
4168   {
4169     // store client dependent player focus when recording network games
4170     tape.centered_player_nr_next = game.centered_player_nr_next;
4171     tape.set_centered_player = TRUE;
4172   }
4173
4174   if (tape.playing)
4175   {
4176     // when playing a tape, eliminate all players who do not participate
4177
4178 #if USE_NEW_PLAYER_ASSIGNMENTS
4179
4180     if (!game.team_mode)
4181     {
4182       for (i = 0; i < MAX_PLAYERS; i++)
4183       {
4184         if (stored_player[i].active &&
4185             !tape.player_participates[map_player_action[i]])
4186         {
4187           struct PlayerInfo *player = &stored_player[i];
4188           int jx = player->jx, jy = player->jy;
4189
4190 #if DEBUG_INIT_PLAYER
4191           Debug("game:init:player", "Removing player %d at (%d, %d)",
4192                 i + 1, jx, jy);
4193 #endif
4194
4195           player->active = FALSE;
4196           StorePlayer[jx][jy] = 0;
4197           Tile[jx][jy] = EL_EMPTY;
4198         }
4199       }
4200     }
4201
4202 #else
4203
4204     for (i = 0; i < MAX_PLAYERS; i++)
4205     {
4206       if (stored_player[i].active &&
4207           !tape.player_participates[i])
4208       {
4209         struct PlayerInfo *player = &stored_player[i];
4210         int jx = player->jx, jy = player->jy;
4211
4212         player->active = FALSE;
4213         StorePlayer[jx][jy] = 0;
4214         Tile[jx][jy] = EL_EMPTY;
4215       }
4216     }
4217 #endif
4218   }
4219   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4220   {
4221     // when in single player mode, eliminate all but the local player
4222
4223     for (i = 0; i < MAX_PLAYERS; i++)
4224     {
4225       struct PlayerInfo *player = &stored_player[i];
4226
4227       if (player->active && player != local_player)
4228       {
4229         int jx = player->jx, jy = player->jy;
4230
4231         player->active = FALSE;
4232         player->present = FALSE;
4233
4234         StorePlayer[jx][jy] = 0;
4235         Tile[jx][jy] = EL_EMPTY;
4236       }
4237     }
4238   }
4239
4240   for (i = 0; i < MAX_PLAYERS; i++)
4241     if (stored_player[i].active)
4242       game.players_still_needed++;
4243
4244   if (level.solved_by_one_player)
4245     game.players_still_needed = 1;
4246
4247   // when recording the game, store which players take part in the game
4248   if (tape.recording)
4249   {
4250 #if USE_NEW_PLAYER_ASSIGNMENTS
4251     for (i = 0; i < MAX_PLAYERS; i++)
4252       if (stored_player[i].connected)
4253         tape.player_participates[i] = TRUE;
4254 #else
4255     for (i = 0; i < MAX_PLAYERS; i++)
4256       if (stored_player[i].active)
4257         tape.player_participates[i] = TRUE;
4258 #endif
4259   }
4260
4261 #if DEBUG_INIT_PLAYER
4262   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4263 #endif
4264
4265   if (BorderElement == EL_EMPTY)
4266   {
4267     SBX_Left = 0;
4268     SBX_Right = lev_fieldx - SCR_FIELDX;
4269     SBY_Upper = 0;
4270     SBY_Lower = lev_fieldy - SCR_FIELDY;
4271   }
4272   else
4273   {
4274     SBX_Left = -1;
4275     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4276     SBY_Upper = -1;
4277     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4278   }
4279
4280   if (full_lev_fieldx <= SCR_FIELDX)
4281     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4282   if (full_lev_fieldy <= SCR_FIELDY)
4283     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4284
4285   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4286     SBX_Left--;
4287   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4288     SBY_Upper--;
4289
4290   // if local player not found, look for custom element that might create
4291   // the player (make some assumptions about the right custom element)
4292   if (!local_player->present)
4293   {
4294     int start_x = 0, start_y = 0;
4295     int found_rating = 0;
4296     int found_element = EL_UNDEFINED;
4297     int player_nr = local_player->index_nr;
4298
4299     SCAN_PLAYFIELD(x, y)
4300     {
4301       int element = Tile[x][y];
4302       int content;
4303       int xx, yy;
4304       boolean is_player;
4305
4306       if (level.use_start_element[player_nr] &&
4307           level.start_element[player_nr] == element &&
4308           found_rating < 4)
4309       {
4310         start_x = x;
4311         start_y = y;
4312
4313         found_rating = 4;
4314         found_element = element;
4315       }
4316
4317       if (!IS_CUSTOM_ELEMENT(element))
4318         continue;
4319
4320       if (CAN_CHANGE(element))
4321       {
4322         for (i = 0; i < element_info[element].num_change_pages; i++)
4323         {
4324           // check for player created from custom element as single target
4325           content = element_info[element].change_page[i].target_element;
4326           is_player = IS_PLAYER_ELEMENT(content);
4327
4328           if (is_player && (found_rating < 3 ||
4329                             (found_rating == 3 && element < found_element)))
4330           {
4331             start_x = x;
4332             start_y = y;
4333
4334             found_rating = 3;
4335             found_element = element;
4336           }
4337         }
4338       }
4339
4340       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4341       {
4342         // check for player created from custom element as explosion content
4343         content = element_info[element].content.e[xx][yy];
4344         is_player = IS_PLAYER_ELEMENT(content);
4345
4346         if (is_player && (found_rating < 2 ||
4347                           (found_rating == 2 && element < found_element)))
4348         {
4349           start_x = x + xx - 1;
4350           start_y = y + yy - 1;
4351
4352           found_rating = 2;
4353           found_element = element;
4354         }
4355
4356         if (!CAN_CHANGE(element))
4357           continue;
4358
4359         for (i = 0; i < element_info[element].num_change_pages; i++)
4360         {
4361           // check for player created from custom element as extended target
4362           content =
4363             element_info[element].change_page[i].target_content.e[xx][yy];
4364
4365           is_player = IS_PLAYER_ELEMENT(content);
4366
4367           if (is_player && (found_rating < 1 ||
4368                             (found_rating == 1 && element < found_element)))
4369           {
4370             start_x = x + xx - 1;
4371             start_y = y + yy - 1;
4372
4373             found_rating = 1;
4374             found_element = element;
4375           }
4376         }
4377       }
4378     }
4379
4380     scroll_x = SCROLL_POSITION_X(start_x);
4381     scroll_y = SCROLL_POSITION_Y(start_y);
4382   }
4383   else
4384   {
4385     scroll_x = SCROLL_POSITION_X(local_player->jx);
4386     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4387   }
4388
4389   // !!! FIX THIS (START) !!!
4390   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4391   {
4392     InitGameEngine_EM();
4393   }
4394   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4395   {
4396     InitGameEngine_SP();
4397   }
4398   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4399   {
4400     InitGameEngine_MM();
4401   }
4402   else
4403   {
4404     DrawLevel(REDRAW_FIELD);
4405     DrawAllPlayers();
4406
4407     // after drawing the level, correct some elements
4408     if (game.timegate_time_left == 0)
4409       CloseAllOpenTimegates();
4410   }
4411
4412   // blit playfield from scroll buffer to normal back buffer for fading in
4413   BlitScreenToBitmap(backbuffer);
4414   // !!! FIX THIS (END) !!!
4415
4416   DrawMaskedBorder(fade_mask);
4417
4418   FadeIn(fade_mask);
4419
4420 #if 1
4421   // full screen redraw is required at this point in the following cases:
4422   // - special editor door undrawn when game was started from level editor
4423   // - drawing area (playfield) was changed and has to be removed completely
4424   redraw_mask = REDRAW_ALL;
4425   BackToFront();
4426 #endif
4427
4428   if (!game.restart_level)
4429   {
4430     // copy default game door content to main double buffer
4431
4432     // !!! CHECK AGAIN !!!
4433     SetPanelBackground();
4434     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4435     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4436   }
4437
4438   SetPanelBackground();
4439   SetDrawBackgroundMask(REDRAW_DOOR_1);
4440
4441   UpdateAndDisplayGameControlValues();
4442
4443   if (!game.restart_level)
4444   {
4445     UnmapGameButtons();
4446     UnmapTapeButtons();
4447
4448     FreeGameButtons();
4449     CreateGameButtons();
4450
4451     MapGameButtons();
4452     MapTapeButtons();
4453
4454     // copy actual game door content to door double buffer for OpenDoor()
4455     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4456
4457     OpenDoor(DOOR_OPEN_ALL);
4458
4459     KeyboardAutoRepeatOffUnlessAutoplay();
4460
4461 #if DEBUG_INIT_PLAYER
4462     DebugPrintPlayerStatus("Player status (final)");
4463 #endif
4464   }
4465
4466   UnmapAllGadgets();
4467
4468   MapGameButtons();
4469   MapTapeButtons();
4470
4471   if (!game.restart_level && !tape.playing)
4472   {
4473     LevelStats_incPlayed(level_nr);
4474
4475     SaveLevelSetup_SeriesInfo();
4476   }
4477
4478   game.restart_level = FALSE;
4479   game.restart_game_message = NULL;
4480
4481   game.request_active = FALSE;
4482   game.request_active_or_moving = FALSE;
4483
4484   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4485     InitGameActions_MM();
4486
4487   SaveEngineSnapshotToListInitial();
4488
4489   if (!game.restart_level)
4490   {
4491     PlaySound(SND_GAME_STARTING);
4492
4493     if (setup.sound_music)
4494       PlayLevelMusic();
4495   }
4496
4497   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4498 }
4499
4500 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4501                         int actual_player_x, int actual_player_y)
4502 {
4503   // this is used for non-R'n'D game engines to update certain engine values
4504
4505   // needed to determine if sounds are played within the visible screen area
4506   scroll_x = actual_scroll_x;
4507   scroll_y = actual_scroll_y;
4508
4509   // needed to get player position for "follow finger" playing input method
4510   local_player->jx = actual_player_x;
4511   local_player->jy = actual_player_y;
4512 }
4513
4514 void InitMovDir(int x, int y)
4515 {
4516   int i, element = Tile[x][y];
4517   static int xy[4][2] =
4518   {
4519     {  0, +1 },
4520     { +1,  0 },
4521     {  0, -1 },
4522     { -1,  0 }
4523   };
4524   static int direction[3][4] =
4525   {
4526     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4527     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4528     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4529   };
4530
4531   switch (element)
4532   {
4533     case EL_BUG_RIGHT:
4534     case EL_BUG_UP:
4535     case EL_BUG_LEFT:
4536     case EL_BUG_DOWN:
4537       Tile[x][y] = EL_BUG;
4538       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4539       break;
4540
4541     case EL_SPACESHIP_RIGHT:
4542     case EL_SPACESHIP_UP:
4543     case EL_SPACESHIP_LEFT:
4544     case EL_SPACESHIP_DOWN:
4545       Tile[x][y] = EL_SPACESHIP;
4546       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4547       break;
4548
4549     case EL_BD_BUTTERFLY_RIGHT:
4550     case EL_BD_BUTTERFLY_UP:
4551     case EL_BD_BUTTERFLY_LEFT:
4552     case EL_BD_BUTTERFLY_DOWN:
4553       Tile[x][y] = EL_BD_BUTTERFLY;
4554       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4555       break;
4556
4557     case EL_BD_FIREFLY_RIGHT:
4558     case EL_BD_FIREFLY_UP:
4559     case EL_BD_FIREFLY_LEFT:
4560     case EL_BD_FIREFLY_DOWN:
4561       Tile[x][y] = EL_BD_FIREFLY;
4562       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4563       break;
4564
4565     case EL_PACMAN_RIGHT:
4566     case EL_PACMAN_UP:
4567     case EL_PACMAN_LEFT:
4568     case EL_PACMAN_DOWN:
4569       Tile[x][y] = EL_PACMAN;
4570       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4571       break;
4572
4573     case EL_YAMYAM_LEFT:
4574     case EL_YAMYAM_RIGHT:
4575     case EL_YAMYAM_UP:
4576     case EL_YAMYAM_DOWN:
4577       Tile[x][y] = EL_YAMYAM;
4578       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4579       break;
4580
4581     case EL_SP_SNIKSNAK:
4582       MovDir[x][y] = MV_UP;
4583       break;
4584
4585     case EL_SP_ELECTRON:
4586       MovDir[x][y] = MV_LEFT;
4587       break;
4588
4589     case EL_MOLE_LEFT:
4590     case EL_MOLE_RIGHT:
4591     case EL_MOLE_UP:
4592     case EL_MOLE_DOWN:
4593       Tile[x][y] = EL_MOLE;
4594       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4595       break;
4596
4597     case EL_SPRING_LEFT:
4598     case EL_SPRING_RIGHT:
4599       Tile[x][y] = EL_SPRING;
4600       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4601       break;
4602
4603     default:
4604       if (IS_CUSTOM_ELEMENT(element))
4605       {
4606         struct ElementInfo *ei = &element_info[element];
4607         int move_direction_initial = ei->move_direction_initial;
4608         int move_pattern = ei->move_pattern;
4609
4610         if (move_direction_initial == MV_START_PREVIOUS)
4611         {
4612           if (MovDir[x][y] != MV_NONE)
4613             return;
4614
4615           move_direction_initial = MV_START_AUTOMATIC;
4616         }
4617
4618         if (move_direction_initial == MV_START_RANDOM)
4619           MovDir[x][y] = 1 << RND(4);
4620         else if (move_direction_initial & MV_ANY_DIRECTION)
4621           MovDir[x][y] = move_direction_initial;
4622         else if (move_pattern == MV_ALL_DIRECTIONS ||
4623                  move_pattern == MV_TURNING_LEFT ||
4624                  move_pattern == MV_TURNING_RIGHT ||
4625                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4626                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4627                  move_pattern == MV_TURNING_RANDOM)
4628           MovDir[x][y] = 1 << RND(4);
4629         else if (move_pattern == MV_HORIZONTAL)
4630           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4631         else if (move_pattern == MV_VERTICAL)
4632           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4633         else if (move_pattern & MV_ANY_DIRECTION)
4634           MovDir[x][y] = element_info[element].move_pattern;
4635         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4636                  move_pattern == MV_ALONG_RIGHT_SIDE)
4637         {
4638           // use random direction as default start direction
4639           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4640             MovDir[x][y] = 1 << RND(4);
4641
4642           for (i = 0; i < NUM_DIRECTIONS; i++)
4643           {
4644             int x1 = x + xy[i][0];
4645             int y1 = y + xy[i][1];
4646
4647             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4648             {
4649               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4650                 MovDir[x][y] = direction[0][i];
4651               else
4652                 MovDir[x][y] = direction[1][i];
4653
4654               break;
4655             }
4656           }
4657         }                
4658       }
4659       else
4660       {
4661         MovDir[x][y] = 1 << RND(4);
4662
4663         if (element != EL_BUG &&
4664             element != EL_SPACESHIP &&
4665             element != EL_BD_BUTTERFLY &&
4666             element != EL_BD_FIREFLY)
4667           break;
4668
4669         for (i = 0; i < NUM_DIRECTIONS; i++)
4670         {
4671           int x1 = x + xy[i][0];
4672           int y1 = y + xy[i][1];
4673
4674           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4675           {
4676             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4677             {
4678               MovDir[x][y] = direction[0][i];
4679               break;
4680             }
4681             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4682                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4683             {
4684               MovDir[x][y] = direction[1][i];
4685               break;
4686             }
4687           }
4688         }
4689       }
4690       break;
4691   }
4692
4693   GfxDir[x][y] = MovDir[x][y];
4694 }
4695
4696 void InitAmoebaNr(int x, int y)
4697 {
4698   int i;
4699   int group_nr = AmoebaNeighbourNr(x, y);
4700
4701   if (group_nr == 0)
4702   {
4703     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4704     {
4705       if (AmoebaCnt[i] == 0)
4706       {
4707         group_nr = i;
4708         break;
4709       }
4710     }
4711   }
4712
4713   AmoebaNr[x][y] = group_nr;
4714   AmoebaCnt[group_nr]++;
4715   AmoebaCnt2[group_nr]++;
4716 }
4717
4718 static void LevelSolved_SetFinalGameValues(void)
4719 {
4720   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4721   game.score_time_final = (level.use_step_counter ? TimePlayed :
4722                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4723
4724   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4725                       game_em.lev->score :
4726                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4727                       game_mm.score :
4728                       game.score);
4729
4730   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4731                        MM_HEALTH(game_mm.laser_overload_value) :
4732                        game.health);
4733
4734   game.LevelSolved_CountingTime = game.time_final;
4735   game.LevelSolved_CountingScore = game.score_final;
4736   game.LevelSolved_CountingHealth = game.health_final;
4737 }
4738
4739 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4740 {
4741   game.LevelSolved_CountingTime = time;
4742   game.LevelSolved_CountingScore = score;
4743   game.LevelSolved_CountingHealth = health;
4744
4745   game_panel_controls[GAME_PANEL_TIME].value = time;
4746   game_panel_controls[GAME_PANEL_SCORE].value = score;
4747   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4748
4749   DisplayGameControlValues();
4750 }
4751
4752 static void LevelSolved(void)
4753 {
4754   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4755       game.players_still_needed > 0)
4756     return;
4757
4758   game.LevelSolved = TRUE;
4759   game.GameOver = TRUE;
4760
4761   // needed here to display correct panel values while player walks into exit
4762   LevelSolved_SetFinalGameValues();
4763 }
4764
4765 void GameWon(void)
4766 {
4767   static int time_count_steps;
4768   static int time, time_final;
4769   static float score, score_final; // needed for time score < 10 for 10 seconds
4770   static int health, health_final;
4771   static int game_over_delay_1 = 0;
4772   static int game_over_delay_2 = 0;
4773   static int game_over_delay_3 = 0;
4774   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4775   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4776
4777   if (!game.LevelSolved_GameWon)
4778   {
4779     int i;
4780
4781     // do not start end game actions before the player stops moving (to exit)
4782     if (local_player->active && local_player->MovPos)
4783       return;
4784
4785     // calculate final game values after player finished walking into exit
4786     LevelSolved_SetFinalGameValues();
4787
4788     game.LevelSolved_GameWon = TRUE;
4789     game.LevelSolved_SaveTape = tape.recording;
4790     game.LevelSolved_SaveScore = !tape.playing;
4791
4792     if (!tape.playing)
4793     {
4794       LevelStats_incSolved(level_nr);
4795
4796       SaveLevelSetup_SeriesInfo();
4797     }
4798
4799     if (tape.auto_play)         // tape might already be stopped here
4800       tape.auto_play_level_solved = TRUE;
4801
4802     TapeStop();
4803
4804     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4805     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4806     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4807
4808     time = time_final = game.time_final;
4809     score = score_final = game.score_final;
4810     health = health_final = game.health_final;
4811
4812     // update game panel values before (delayed) counting of score (if any)
4813     LevelSolved_DisplayFinalGameValues(time, score, health);
4814
4815     // if level has time score defined, calculate new final game values
4816     if (time_score > 0)
4817     {
4818       int time_final_max = 999;
4819       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4820       int time_frames = 0;
4821       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4822       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4823
4824       if (TimeLeft > 0)
4825       {
4826         time_final = 0;
4827         time_frames = time_frames_left;
4828       }
4829       else if (game.no_time_limit && TimePlayed < time_final_max)
4830       {
4831         time_final = time_final_max;
4832         time_frames = time_frames_final_max - time_frames_played;
4833       }
4834
4835       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4836
4837       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4838
4839       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4840       {
4841         health_final = 0;
4842         score_final += health * time_score;
4843       }
4844
4845       game.score_final = score_final;
4846       game.health_final = health_final;
4847     }
4848
4849     // if not counting score after game, immediately update game panel values
4850     if (level_editor_test_game || !setup.count_score_after_game)
4851     {
4852       time = time_final;
4853       score = score_final;
4854
4855       LevelSolved_DisplayFinalGameValues(time, score, health);
4856     }
4857
4858     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4859     {
4860       // check if last player has left the level
4861       if (game.exit_x >= 0 &&
4862           game.exit_y >= 0)
4863       {
4864         int x = game.exit_x;
4865         int y = game.exit_y;
4866         int element = Tile[x][y];
4867
4868         // close exit door after last player
4869         if ((game.all_players_gone &&
4870              (element == EL_EXIT_OPEN ||
4871               element == EL_SP_EXIT_OPEN ||
4872               element == EL_STEEL_EXIT_OPEN)) ||
4873             element == EL_EM_EXIT_OPEN ||
4874             element == EL_EM_STEEL_EXIT_OPEN)
4875         {
4876
4877           Tile[x][y] =
4878             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4879              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4880              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4881              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4882              EL_EM_STEEL_EXIT_CLOSING);
4883
4884           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4885         }
4886
4887         // player disappears
4888         DrawLevelField(x, y);
4889       }
4890
4891       for (i = 0; i < MAX_PLAYERS; i++)
4892       {
4893         struct PlayerInfo *player = &stored_player[i];
4894
4895         if (player->present)
4896         {
4897           RemovePlayer(player);
4898
4899           // player disappears
4900           DrawLevelField(player->jx, player->jy);
4901         }
4902       }
4903     }
4904
4905     PlaySound(SND_GAME_WINNING);
4906   }
4907
4908   if (setup.count_score_after_game)
4909   {
4910     if (time != time_final)
4911     {
4912       if (game_over_delay_1 > 0)
4913       {
4914         game_over_delay_1--;
4915
4916         return;
4917       }
4918
4919       int time_to_go = ABS(time_final - time);
4920       int time_count_dir = (time < time_final ? +1 : -1);
4921
4922       if (time_to_go < time_count_steps)
4923         time_count_steps = 1;
4924
4925       time  += time_count_steps * time_count_dir;
4926       score += time_count_steps * time_score;
4927
4928       // set final score to correct rounding differences after counting score
4929       if (time == time_final)
4930         score = score_final;
4931
4932       LevelSolved_DisplayFinalGameValues(time, score, health);
4933
4934       if (time == time_final)
4935         StopSound(SND_GAME_LEVELTIME_BONUS);
4936       else if (setup.sound_loops)
4937         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4938       else
4939         PlaySound(SND_GAME_LEVELTIME_BONUS);
4940
4941       return;
4942     }
4943
4944     if (health != health_final)
4945     {
4946       if (game_over_delay_2 > 0)
4947       {
4948         game_over_delay_2--;
4949
4950         return;
4951       }
4952
4953       int health_count_dir = (health < health_final ? +1 : -1);
4954
4955       health += health_count_dir;
4956       score  += time_score;
4957
4958       LevelSolved_DisplayFinalGameValues(time, score, health);
4959
4960       if (health == health_final)
4961         StopSound(SND_GAME_LEVELTIME_BONUS);
4962       else if (setup.sound_loops)
4963         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4964       else
4965         PlaySound(SND_GAME_LEVELTIME_BONUS);
4966
4967       return;
4968     }
4969   }
4970
4971   game.panel.active = FALSE;
4972
4973   if (game_over_delay_3 > 0)
4974   {
4975     game_over_delay_3--;
4976
4977     return;
4978   }
4979
4980   GameEnd();
4981 }
4982
4983 void GameEnd(void)
4984 {
4985   // used instead of "level_nr" (needed for network games)
4986   int last_level_nr = levelset.level_nr;
4987   boolean tape_saved = FALSE;
4988
4989   game.LevelSolved_GameEnd = TRUE;
4990
4991   if (game.LevelSolved_SaveTape)
4992   {
4993     // make sure that request dialog to save tape does not open door again
4994     if (!global.use_envelope_request)
4995       CloseDoor(DOOR_CLOSE_1);
4996
4997     // ask to save tape
4998     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4999
5000     // set unique basename for score tape (also saved in high score table)
5001     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5002   }
5003
5004   // if no tape is to be saved, close both doors simultaneously
5005   CloseDoor(DOOR_CLOSE_ALL);
5006
5007   if (level_editor_test_game)
5008   {
5009     SetGameStatus(GAME_MODE_MAIN);
5010
5011     DrawMainMenu();
5012
5013     return;
5014   }
5015
5016   if (!game.LevelSolved_SaveScore)
5017   {
5018     SetGameStatus(GAME_MODE_MAIN);
5019
5020     DrawMainMenu();
5021
5022     return;
5023   }
5024
5025   if (level_nr == leveldir_current->handicap_level)
5026   {
5027     leveldir_current->handicap_level++;
5028
5029     SaveLevelSetup_SeriesInfo();
5030   }
5031
5032   // save score and score tape before potentially erasing tape below
5033   NewHighScore(last_level_nr, tape_saved);
5034
5035   if (setup.increment_levels &&
5036       level_nr < leveldir_current->last_level &&
5037       !network_playing)
5038   {
5039     level_nr++;         // advance to next level
5040     TapeErase();        // start with empty tape
5041
5042     if (setup.auto_play_next_level)
5043     {
5044       LoadLevel(level_nr);
5045
5046       SaveLevelSetup_SeriesInfo();
5047     }
5048   }
5049
5050   if (scores.last_added >= 0 && setup.show_scores_after_game)
5051   {
5052     SetGameStatus(GAME_MODE_SCORES);
5053
5054     DrawHallOfFame(last_level_nr);
5055   }
5056   else if (setup.auto_play_next_level && setup.increment_levels &&
5057            last_level_nr < leveldir_current->last_level &&
5058            !network_playing)
5059   {
5060     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5061   }
5062   else
5063   {
5064     SetGameStatus(GAME_MODE_MAIN);
5065
5066     DrawMainMenu();
5067   }
5068 }
5069
5070 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5071                          boolean one_score_entry_per_name)
5072 {
5073   int i;
5074
5075   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5076     return -1;
5077
5078   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5079   {
5080     struct ScoreEntry *entry = &list->entry[i];
5081     boolean score_is_better = (new_entry->score >  entry->score);
5082     boolean score_is_equal  = (new_entry->score == entry->score);
5083     boolean time_is_better  = (new_entry->time  <  entry->time);
5084     boolean time_is_equal   = (new_entry->time  == entry->time);
5085     boolean better_by_score = (score_is_better ||
5086                                (score_is_equal && time_is_better));
5087     boolean better_by_time  = (time_is_better ||
5088                                (time_is_equal && score_is_better));
5089     boolean is_better = (level.rate_time_over_score ? better_by_time :
5090                          better_by_score);
5091     boolean entry_is_empty = (entry->score == 0 &&
5092                               entry->time == 0);
5093
5094     // prevent adding server score entries if also existing in local score file
5095     // (special case: historic score entries have an empty tape basename entry)
5096     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5097         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5098     {
5099       // special case: use server score instead of local score value if higher
5100       // (historic scores might have been truncated to 16-bit values locally)
5101       if (score_is_better)
5102         entry->score = new_entry->score;
5103
5104       return -1;
5105     }
5106
5107     if (is_better || entry_is_empty)
5108     {
5109       // player has made it to the hall of fame
5110
5111       if (i < MAX_SCORE_ENTRIES - 1)
5112       {
5113         int m = MAX_SCORE_ENTRIES - 1;
5114         int l;
5115
5116         if (one_score_entry_per_name)
5117         {
5118           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5119             if (strEqual(list->entry[l].name, new_entry->name))
5120               m = l;
5121
5122           if (m == i)   // player's new highscore overwrites his old one
5123             goto put_into_list;
5124         }
5125
5126         for (l = m; l > i; l--)
5127           list->entry[l] = list->entry[l - 1];
5128       }
5129
5130       put_into_list:
5131
5132       *entry = *new_entry;
5133
5134       return i;
5135     }
5136     else if (one_score_entry_per_name &&
5137              strEqual(entry->name, new_entry->name))
5138     {
5139       // player already in high score list with better score or time
5140
5141       return -1;
5142     }
5143   }
5144
5145   return -1;
5146 }
5147
5148 void NewHighScore(int level_nr, boolean tape_saved)
5149 {
5150   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5151   boolean one_per_name = FALSE;
5152
5153   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5154   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5155
5156   new_entry.score = game.score_final;
5157   new_entry.time = game.score_time_final;
5158
5159   LoadScore(level_nr);
5160
5161   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5162
5163   if (scores.last_added < 0)
5164     return;
5165
5166   SaveScore(level_nr);
5167
5168   // store last added local score entry (before merging server scores)
5169   scores.last_added_local = scores.last_added;
5170
5171   if (!game.LevelSolved_SaveTape)
5172     return;
5173
5174   SaveScoreTape(level_nr);
5175
5176   if (setup.ask_for_using_api_server)
5177   {
5178     setup.use_api_server =
5179       Request("Upload your score and tape to the high score server?", REQ_ASK);
5180
5181     if (!setup.use_api_server)
5182       Request("Not using high score server! Use setup menu to enable again!",
5183               REQ_CONFIRM);
5184
5185     runtime.use_api_server = setup.use_api_server;
5186
5187     // after asking for using API server once, do not ask again
5188     setup.ask_for_using_api_server = FALSE;
5189
5190     SaveSetup_ServerSetup();
5191   }
5192
5193   SaveServerScore(level_nr, tape_saved);
5194 }
5195
5196 void MergeServerScore(void)
5197 {
5198   struct ScoreEntry last_added_entry;
5199   boolean one_per_name = FALSE;
5200   int i;
5201
5202   if (scores.last_added >= 0)
5203     last_added_entry = scores.entry[scores.last_added];
5204
5205   for (i = 0; i < server_scores.num_entries; i++)
5206   {
5207     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5208
5209     if (pos >= 0 && pos <= scores.last_added)
5210       scores.last_added++;
5211   }
5212
5213   if (scores.last_added >= MAX_SCORE_ENTRIES)
5214   {
5215     scores.last_added = MAX_SCORE_ENTRIES - 1;
5216     scores.force_last_added = TRUE;
5217
5218     scores.entry[scores.last_added] = last_added_entry;
5219   }
5220 }
5221
5222 static int getElementMoveStepsizeExt(int x, int y, int direction)
5223 {
5224   int element = Tile[x][y];
5225   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5226   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5227   int horiz_move = (dx != 0);
5228   int sign = (horiz_move ? dx : dy);
5229   int step = sign * element_info[element].move_stepsize;
5230
5231   // special values for move stepsize for spring and things on conveyor belt
5232   if (horiz_move)
5233   {
5234     if (CAN_FALL(element) &&
5235         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5236       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5237     else if (element == EL_SPRING)
5238       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5239   }
5240
5241   return step;
5242 }
5243
5244 static int getElementMoveStepsize(int x, int y)
5245 {
5246   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5247 }
5248
5249 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5250 {
5251   if (player->GfxAction != action || player->GfxDir != dir)
5252   {
5253     player->GfxAction = action;
5254     player->GfxDir = dir;
5255     player->Frame = 0;
5256     player->StepFrame = 0;
5257   }
5258 }
5259
5260 static void ResetGfxFrame(int x, int y)
5261 {
5262   // profiling showed that "autotest" spends 10~20% of its time in this function
5263   if (DrawingDeactivatedField())
5264     return;
5265
5266   int element = Tile[x][y];
5267   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268
5269   if (graphic_info[graphic].anim_global_sync)
5270     GfxFrame[x][y] = FrameCounter;
5271   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5272     GfxFrame[x][y] = CustomValue[x][y];
5273   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5274     GfxFrame[x][y] = element_info[element].collect_score;
5275   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5276     GfxFrame[x][y] = ChangeDelay[x][y];
5277 }
5278
5279 static void ResetGfxAnimation(int x, int y)
5280 {
5281   GfxAction[x][y] = ACTION_DEFAULT;
5282   GfxDir[x][y] = MovDir[x][y];
5283   GfxFrame[x][y] = 0;
5284
5285   ResetGfxFrame(x, y);
5286 }
5287
5288 static void ResetRandomAnimationValue(int x, int y)
5289 {
5290   GfxRandom[x][y] = INIT_GFX_RANDOM();
5291 }
5292
5293 static void InitMovingField(int x, int y, int direction)
5294 {
5295   int element = Tile[x][y];
5296   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5297   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5298   int newx = x + dx;
5299   int newy = y + dy;
5300   boolean is_moving_before, is_moving_after;
5301
5302   // check if element was/is moving or being moved before/after mode change
5303   is_moving_before = (WasJustMoving[x][y] != 0);
5304   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5305
5306   // reset animation only for moving elements which change direction of moving
5307   // or which just started or stopped moving
5308   // (else CEs with property "can move" / "not moving" are reset each frame)
5309   if (is_moving_before != is_moving_after ||
5310       direction != MovDir[x][y])
5311     ResetGfxAnimation(x, y);
5312
5313   MovDir[x][y] = direction;
5314   GfxDir[x][y] = direction;
5315
5316   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5317                      direction == MV_DOWN && CAN_FALL(element) ?
5318                      ACTION_FALLING : ACTION_MOVING);
5319
5320   // this is needed for CEs with property "can move" / "not moving"
5321
5322   if (is_moving_after)
5323   {
5324     if (Tile[newx][newy] == EL_EMPTY)
5325       Tile[newx][newy] = EL_BLOCKED;
5326
5327     MovDir[newx][newy] = MovDir[x][y];
5328
5329     CustomValue[newx][newy] = CustomValue[x][y];
5330
5331     GfxFrame[newx][newy] = GfxFrame[x][y];
5332     GfxRandom[newx][newy] = GfxRandom[x][y];
5333     GfxAction[newx][newy] = GfxAction[x][y];
5334     GfxDir[newx][newy] = GfxDir[x][y];
5335   }
5336 }
5337
5338 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5339 {
5340   int direction = MovDir[x][y];
5341   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5342   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5343
5344   *goes_to_x = newx;
5345   *goes_to_y = newy;
5346 }
5347
5348 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5349 {
5350   int oldx = x, oldy = y;
5351   int direction = MovDir[x][y];
5352
5353   if (direction == MV_LEFT)
5354     oldx++;
5355   else if (direction == MV_RIGHT)
5356     oldx--;
5357   else if (direction == MV_UP)
5358     oldy++;
5359   else if (direction == MV_DOWN)
5360     oldy--;
5361
5362   *comes_from_x = oldx;
5363   *comes_from_y = oldy;
5364 }
5365
5366 static int MovingOrBlocked2Element(int x, int y)
5367 {
5368   int element = Tile[x][y];
5369
5370   if (element == EL_BLOCKED)
5371   {
5372     int oldx, oldy;
5373
5374     Blocked2Moving(x, y, &oldx, &oldy);
5375     return Tile[oldx][oldy];
5376   }
5377   else
5378     return element;
5379 }
5380
5381 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5382 {
5383   // like MovingOrBlocked2Element(), but if element is moving
5384   // and (x,y) is the field the moving element is just leaving,
5385   // return EL_BLOCKED instead of the element value
5386   int element = Tile[x][y];
5387
5388   if (IS_MOVING(x, y))
5389   {
5390     if (element == EL_BLOCKED)
5391     {
5392       int oldx, oldy;
5393
5394       Blocked2Moving(x, y, &oldx, &oldy);
5395       return Tile[oldx][oldy];
5396     }
5397     else
5398       return EL_BLOCKED;
5399   }
5400   else
5401     return element;
5402 }
5403
5404 static void RemoveField(int x, int y)
5405 {
5406   Tile[x][y] = EL_EMPTY;
5407
5408   MovPos[x][y] = 0;
5409   MovDir[x][y] = 0;
5410   MovDelay[x][y] = 0;
5411
5412   CustomValue[x][y] = 0;
5413
5414   AmoebaNr[x][y] = 0;
5415   ChangeDelay[x][y] = 0;
5416   ChangePage[x][y] = -1;
5417   Pushed[x][y] = FALSE;
5418
5419   GfxElement[x][y] = EL_UNDEFINED;
5420   GfxAction[x][y] = ACTION_DEFAULT;
5421   GfxDir[x][y] = MV_NONE;
5422 }
5423
5424 static void RemoveMovingField(int x, int y)
5425 {
5426   int oldx = x, oldy = y, newx = x, newy = y;
5427   int element = Tile[x][y];
5428   int next_element = EL_UNDEFINED;
5429
5430   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5431     return;
5432
5433   if (IS_MOVING(x, y))
5434   {
5435     Moving2Blocked(x, y, &newx, &newy);
5436
5437     if (Tile[newx][newy] != EL_BLOCKED)
5438     {
5439       // element is moving, but target field is not free (blocked), but
5440       // already occupied by something different (example: acid pool);
5441       // in this case, only remove the moving field, but not the target
5442
5443       RemoveField(oldx, oldy);
5444
5445       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5446
5447       TEST_DrawLevelField(oldx, oldy);
5448
5449       return;
5450     }
5451   }
5452   else if (element == EL_BLOCKED)
5453   {
5454     Blocked2Moving(x, y, &oldx, &oldy);
5455     if (!IS_MOVING(oldx, oldy))
5456       return;
5457   }
5458
5459   if (element == EL_BLOCKED &&
5460       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5461        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5462        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5463        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5464        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5465        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5466     next_element = get_next_element(Tile[oldx][oldy]);
5467
5468   RemoveField(oldx, oldy);
5469   RemoveField(newx, newy);
5470
5471   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5472
5473   if (next_element != EL_UNDEFINED)
5474     Tile[oldx][oldy] = next_element;
5475
5476   TEST_DrawLevelField(oldx, oldy);
5477   TEST_DrawLevelField(newx, newy);
5478 }
5479
5480 void DrawDynamite(int x, int y)
5481 {
5482   int sx = SCREENX(x), sy = SCREENY(y);
5483   int graphic = el2img(Tile[x][y]);
5484   int frame;
5485
5486   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5487     return;
5488
5489   if (IS_WALKABLE_INSIDE(Back[x][y]))
5490     return;
5491
5492   if (Back[x][y])
5493     DrawLevelElement(x, y, Back[x][y]);
5494   else if (Store[x][y])
5495     DrawLevelElement(x, y, Store[x][y]);
5496   else if (game.use_masked_elements)
5497     DrawLevelElement(x, y, EL_EMPTY);
5498
5499   frame = getGraphicAnimationFrameXY(graphic, x, y);
5500
5501   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5502     DrawGraphicThruMask(sx, sy, graphic, frame);
5503   else
5504     DrawGraphic(sx, sy, graphic, frame);
5505 }
5506
5507 static void CheckDynamite(int x, int y)
5508 {
5509   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5510   {
5511     MovDelay[x][y]--;
5512
5513     if (MovDelay[x][y] != 0)
5514     {
5515       DrawDynamite(x, y);
5516       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5517
5518       return;
5519     }
5520   }
5521
5522   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5523
5524   Bang(x, y);
5525 }
5526
5527 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5528 {
5529   boolean num_checked_players = 0;
5530   int i;
5531
5532   for (i = 0; i < MAX_PLAYERS; i++)
5533   {
5534     if (stored_player[i].active)
5535     {
5536       int sx = stored_player[i].jx;
5537       int sy = stored_player[i].jy;
5538
5539       if (num_checked_players == 0)
5540       {
5541         *sx1 = *sx2 = sx;
5542         *sy1 = *sy2 = sy;
5543       }
5544       else
5545       {
5546         *sx1 = MIN(*sx1, sx);
5547         *sy1 = MIN(*sy1, sy);
5548         *sx2 = MAX(*sx2, sx);
5549         *sy2 = MAX(*sy2, sy);
5550       }
5551
5552       num_checked_players++;
5553     }
5554   }
5555 }
5556
5557 static boolean checkIfAllPlayersFitToScreen_RND(void)
5558 {
5559   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5560
5561   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5562
5563   return (sx2 - sx1 < SCR_FIELDX &&
5564           sy2 - sy1 < SCR_FIELDY);
5565 }
5566
5567 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5568 {
5569   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5570
5571   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5572
5573   *sx = (sx1 + sx2) / 2;
5574   *sy = (sy1 + sy2) / 2;
5575 }
5576
5577 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5578                                boolean center_screen, boolean quick_relocation)
5579 {
5580   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5581   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5582   boolean no_delay = (tape.warp_forward);
5583   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5584   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5585   int new_scroll_x, new_scroll_y;
5586
5587   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5588   {
5589     // case 1: quick relocation inside visible screen (without scrolling)
5590
5591     RedrawPlayfield();
5592
5593     return;
5594   }
5595
5596   if (!level.shifted_relocation || center_screen)
5597   {
5598     // relocation _with_ centering of screen
5599
5600     new_scroll_x = SCROLL_POSITION_X(x);
5601     new_scroll_y = SCROLL_POSITION_Y(y);
5602   }
5603   else
5604   {
5605     // relocation _without_ centering of screen
5606
5607     int center_scroll_x = SCROLL_POSITION_X(old_x);
5608     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5609     int offset_x = x + (scroll_x - center_scroll_x);
5610     int offset_y = y + (scroll_y - center_scroll_y);
5611
5612     // for new screen position, apply previous offset to center position
5613     new_scroll_x = SCROLL_POSITION_X(offset_x);
5614     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5615   }
5616
5617   if (quick_relocation)
5618   {
5619     // case 2: quick relocation (redraw without visible scrolling)
5620
5621     scroll_x = new_scroll_x;
5622     scroll_y = new_scroll_y;
5623
5624     RedrawPlayfield();
5625
5626     return;
5627   }
5628
5629   // case 3: visible relocation (with scrolling to new position)
5630
5631   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5632
5633   SetVideoFrameDelay(wait_delay_value);
5634
5635   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5636   {
5637     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5638     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5639
5640     if (dx == 0 && dy == 0)             // no scrolling needed at all
5641       break;
5642
5643     scroll_x -= dx;
5644     scroll_y -= dy;
5645
5646     // set values for horizontal/vertical screen scrolling (half tile size)
5647     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5648     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5649     int pos_x = dx * TILEX / 2;
5650     int pos_y = dy * TILEY / 2;
5651     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5652     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5653
5654     ScrollLevel(dx, dy);
5655     DrawAllPlayers();
5656
5657     // scroll in two steps of half tile size to make things smoother
5658     BlitScreenToBitmapExt_RND(window, fx, fy);
5659
5660     // scroll second step to align at full tile size
5661     BlitScreenToBitmap(window);
5662   }
5663
5664   DrawAllPlayers();
5665   BackToFront();
5666
5667   SetVideoFrameDelay(frame_delay_value_old);
5668 }
5669
5670 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5671 {
5672   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5673   int player_nr = GET_PLAYER_NR(el_player);
5674   struct PlayerInfo *player = &stored_player[player_nr];
5675   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5676   boolean no_delay = (tape.warp_forward);
5677   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5678   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5679   int old_jx = player->jx;
5680   int old_jy = player->jy;
5681   int old_element = Tile[old_jx][old_jy];
5682   int element = Tile[jx][jy];
5683   boolean player_relocated = (old_jx != jx || old_jy != jy);
5684
5685   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5686   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5687   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5688   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5689   int leave_side_horiz = move_dir_horiz;
5690   int leave_side_vert  = move_dir_vert;
5691   int enter_side = enter_side_horiz | enter_side_vert;
5692   int leave_side = leave_side_horiz | leave_side_vert;
5693
5694   if (player->buried)           // do not reanimate dead player
5695     return;
5696
5697   if (!player_relocated)        // no need to relocate the player
5698     return;
5699
5700   if (IS_PLAYER(jx, jy))        // player already placed at new position
5701   {
5702     RemoveField(jx, jy);        // temporarily remove newly placed player
5703     DrawLevelField(jx, jy);
5704   }
5705
5706   if (player->present)
5707   {
5708     while (player->MovPos)
5709     {
5710       ScrollPlayer(player, SCROLL_GO_ON);
5711       ScrollScreen(NULL, SCROLL_GO_ON);
5712
5713       AdvanceFrameAndPlayerCounters(player->index_nr);
5714
5715       DrawPlayer(player);
5716
5717       BackToFront_WithFrameDelay(wait_delay_value);
5718     }
5719
5720     DrawPlayer(player);         // needed here only to cleanup last field
5721     DrawLevelField(player->jx, player->jy);     // remove player graphic
5722
5723     player->is_moving = FALSE;
5724   }
5725
5726   if (IS_CUSTOM_ELEMENT(old_element))
5727     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5728                                CE_LEFT_BY_PLAYER,
5729                                player->index_bit, leave_side);
5730
5731   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5732                                       CE_PLAYER_LEAVES_X,
5733                                       player->index_bit, leave_side);
5734
5735   Tile[jx][jy] = el_player;
5736   InitPlayerField(jx, jy, el_player, TRUE);
5737
5738   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5739      possible that the relocation target field did not contain a player element,
5740      but a walkable element, to which the new player was relocated -- in this
5741      case, restore that (already initialized!) element on the player field */
5742   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5743   {
5744     Tile[jx][jy] = element;     // restore previously existing element
5745   }
5746
5747   // only visually relocate centered player
5748   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5749                      FALSE, level.instant_relocation);
5750
5751   TestIfPlayerTouchesBadThing(jx, jy);
5752   TestIfPlayerTouchesCustomElement(jx, jy);
5753
5754   if (IS_CUSTOM_ELEMENT(element))
5755     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5756                                player->index_bit, enter_side);
5757
5758   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5759                                       player->index_bit, enter_side);
5760
5761   if (player->is_switching)
5762   {
5763     /* ensure that relocation while still switching an element does not cause
5764        a new element to be treated as also switched directly after relocation
5765        (this is important for teleporter switches that teleport the player to
5766        a place where another teleporter switch is in the same direction, which
5767        would then incorrectly be treated as immediately switched before the
5768        direction key that caused the switch was released) */
5769
5770     player->switch_x += jx - old_jx;
5771     player->switch_y += jy - old_jy;
5772   }
5773 }
5774
5775 static void Explode(int ex, int ey, int phase, int mode)
5776 {
5777   int x, y;
5778   int last_phase;
5779   int border_element;
5780
5781   // !!! eliminate this variable !!!
5782   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5783
5784   if (game.explosions_delayed)
5785   {
5786     ExplodeField[ex][ey] = mode;
5787     return;
5788   }
5789
5790   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5791   {
5792     int center_element = Tile[ex][ey];
5793     int artwork_element, explosion_element;     // set these values later
5794
5795     // remove things displayed in background while burning dynamite
5796     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5797       Back[ex][ey] = 0;
5798
5799     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5800     {
5801       // put moving element to center field (and let it explode there)
5802       center_element = MovingOrBlocked2Element(ex, ey);
5803       RemoveMovingField(ex, ey);
5804       Tile[ex][ey] = center_element;
5805     }
5806
5807     // now "center_element" is finally determined -- set related values now
5808     artwork_element = center_element;           // for custom player artwork
5809     explosion_element = center_element;         // for custom player artwork
5810
5811     if (IS_PLAYER(ex, ey))
5812     {
5813       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5814
5815       artwork_element = stored_player[player_nr].artwork_element;
5816
5817       if (level.use_explosion_element[player_nr])
5818       {
5819         explosion_element = level.explosion_element[player_nr];
5820         artwork_element = explosion_element;
5821       }
5822     }
5823
5824     if (mode == EX_TYPE_NORMAL ||
5825         mode == EX_TYPE_CENTER ||
5826         mode == EX_TYPE_CROSS)
5827       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5828
5829     last_phase = element_info[explosion_element].explosion_delay + 1;
5830
5831     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5832     {
5833       int xx = x - ex + 1;
5834       int yy = y - ey + 1;
5835       int element;
5836
5837       if (!IN_LEV_FIELD(x, y) ||
5838           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5839           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5840         continue;
5841
5842       element = Tile[x][y];
5843
5844       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5845       {
5846         element = MovingOrBlocked2Element(x, y);
5847
5848         if (!IS_EXPLOSION_PROOF(element))
5849           RemoveMovingField(x, y);
5850       }
5851
5852       // indestructible elements can only explode in center (but not flames)
5853       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5854                                            mode == EX_TYPE_BORDER)) ||
5855           element == EL_FLAMES)
5856         continue;
5857
5858       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5859          behaviour, for example when touching a yamyam that explodes to rocks
5860          with active deadly shield, a rock is created under the player !!! */
5861       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5862 #if 0
5863       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5864           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5865            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5866 #else
5867       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5868 #endif
5869       {
5870         if (IS_ACTIVE_BOMB(element))
5871         {
5872           // re-activate things under the bomb like gate or penguin
5873           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5874           Back[x][y] = 0;
5875         }
5876
5877         continue;
5878       }
5879
5880       // save walkable background elements while explosion on same tile
5881       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5882           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5883         Back[x][y] = element;
5884
5885       // ignite explodable elements reached by other explosion
5886       if (element == EL_EXPLOSION)
5887         element = Store2[x][y];
5888
5889       if (AmoebaNr[x][y] &&
5890           (element == EL_AMOEBA_FULL ||
5891            element == EL_BD_AMOEBA ||
5892            element == EL_AMOEBA_GROWING))
5893       {
5894         AmoebaCnt[AmoebaNr[x][y]]--;
5895         AmoebaCnt2[AmoebaNr[x][y]]--;
5896       }
5897
5898       RemoveField(x, y);
5899
5900       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5901       {
5902         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5903
5904         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5905
5906         if (PLAYERINFO(ex, ey)->use_murphy)
5907           Store[x][y] = EL_EMPTY;
5908       }
5909
5910       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5911       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5912       else if (IS_PLAYER_ELEMENT(center_element))
5913         Store[x][y] = EL_EMPTY;
5914       else if (center_element == EL_YAMYAM)
5915         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5916       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5917         Store[x][y] = element_info[center_element].content.e[xx][yy];
5918 #if 1
5919       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5920       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5921       // otherwise) -- FIX THIS !!!
5922       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5923         Store[x][y] = element_info[element].content.e[1][1];
5924 #else
5925       else if (!CAN_EXPLODE(element))
5926         Store[x][y] = element_info[element].content.e[1][1];
5927 #endif
5928       else
5929         Store[x][y] = EL_EMPTY;
5930
5931       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5932           center_element == EL_AMOEBA_TO_DIAMOND)
5933         Store2[x][y] = element;
5934
5935       Tile[x][y] = EL_EXPLOSION;
5936       GfxElement[x][y] = artwork_element;
5937
5938       ExplodePhase[x][y] = 1;
5939       ExplodeDelay[x][y] = last_phase;
5940
5941       Stop[x][y] = TRUE;
5942     }
5943
5944     if (center_element == EL_YAMYAM)
5945       game.yamyam_content_nr =
5946         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5947
5948     return;
5949   }
5950
5951   if (Stop[ex][ey])
5952     return;
5953
5954   x = ex;
5955   y = ey;
5956
5957   if (phase == 1)
5958     GfxFrame[x][y] = 0;         // restart explosion animation
5959
5960   last_phase = ExplodeDelay[x][y];
5961
5962   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5963
5964   // this can happen if the player leaves an explosion just in time
5965   if (GfxElement[x][y] == EL_UNDEFINED)
5966     GfxElement[x][y] = EL_EMPTY;
5967
5968   border_element = Store2[x][y];
5969   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5970     border_element = StorePlayer[x][y];
5971
5972   if (phase == element_info[border_element].ignition_delay ||
5973       phase == last_phase)
5974   {
5975     boolean border_explosion = FALSE;
5976
5977     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5978         !PLAYER_EXPLOSION_PROTECTED(x, y))
5979     {
5980       KillPlayerUnlessExplosionProtected(x, y);
5981       border_explosion = TRUE;
5982     }
5983     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5984     {
5985       Tile[x][y] = Store2[x][y];
5986       Store2[x][y] = 0;
5987       Bang(x, y);
5988       border_explosion = TRUE;
5989     }
5990     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5991     {
5992       AmoebaToDiamond(x, y);
5993       Store2[x][y] = 0;
5994       border_explosion = TRUE;
5995     }
5996
5997     // if an element just explodes due to another explosion (chain-reaction),
5998     // do not immediately end the new explosion when it was the last frame of
5999     // the explosion (as it would be done in the following "if"-statement!)
6000     if (border_explosion && phase == last_phase)
6001       return;
6002   }
6003
6004   // this can happen if the player was just killed by an explosion
6005   if (GfxElement[x][y] == EL_UNDEFINED)
6006     GfxElement[x][y] = EL_EMPTY;
6007
6008   if (phase == last_phase)
6009   {
6010     int element;
6011
6012     element = Tile[x][y] = Store[x][y];
6013     Store[x][y] = Store2[x][y] = 0;
6014     GfxElement[x][y] = EL_UNDEFINED;
6015
6016     // player can escape from explosions and might therefore be still alive
6017     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6018         element <= EL_PLAYER_IS_EXPLODING_4)
6019     {
6020       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6021       int explosion_element = EL_PLAYER_1 + player_nr;
6022       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6023       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6024
6025       if (level.use_explosion_element[player_nr])
6026         explosion_element = level.explosion_element[player_nr];
6027
6028       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6029                     element_info[explosion_element].content.e[xx][yy]);
6030     }
6031
6032     // restore probably existing indestructible background element
6033     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6034       element = Tile[x][y] = Back[x][y];
6035     Back[x][y] = 0;
6036
6037     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6038     GfxDir[x][y] = MV_NONE;
6039     ChangeDelay[x][y] = 0;
6040     ChangePage[x][y] = -1;
6041
6042     CustomValue[x][y] = 0;
6043
6044     InitField_WithBug2(x, y, FALSE);
6045
6046     TEST_DrawLevelField(x, y);
6047
6048     TestIfElementTouchesCustomElement(x, y);
6049
6050     if (GFX_CRUMBLED(element))
6051       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6052
6053     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6054       StorePlayer[x][y] = 0;
6055
6056     if (IS_PLAYER_ELEMENT(element))
6057       RelocatePlayer(x, y, element);
6058   }
6059   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6060   {
6061     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6062     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6063
6064     if (phase == delay)
6065       TEST_DrawLevelFieldCrumbled(x, y);
6066
6067     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6068     {
6069       DrawLevelElement(x, y, Back[x][y]);
6070       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6071     }
6072     else if (IS_WALKABLE_UNDER(Back[x][y]))
6073     {
6074       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6075       DrawLevelElementThruMask(x, y, Back[x][y]);
6076     }
6077     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6078       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6079   }
6080 }
6081
6082 static void DynaExplode(int ex, int ey)
6083 {
6084   int i, j;
6085   int dynabomb_element = Tile[ex][ey];
6086   int dynabomb_size = 1;
6087   boolean dynabomb_xl = FALSE;
6088   struct PlayerInfo *player;
6089   static int xy[4][2] =
6090   {
6091     { 0, -1 },
6092     { -1, 0 },
6093     { +1, 0 },
6094     { 0, +1 }
6095   };
6096
6097   if (IS_ACTIVE_BOMB(dynabomb_element))
6098   {
6099     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6100     dynabomb_size = player->dynabomb_size;
6101     dynabomb_xl = player->dynabomb_xl;
6102     player->dynabombs_left++;
6103   }
6104
6105   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6106
6107   for (i = 0; i < NUM_DIRECTIONS; i++)
6108   {
6109     for (j = 1; j <= dynabomb_size; j++)
6110     {
6111       int x = ex + j * xy[i][0];
6112       int y = ey + j * xy[i][1];
6113       int element;
6114
6115       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6116         break;
6117
6118       element = Tile[x][y];
6119
6120       // do not restart explosions of fields with active bombs
6121       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6122         continue;
6123
6124       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6125
6126       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6127           !IS_DIGGABLE(element) && !dynabomb_xl)
6128         break;
6129     }
6130   }
6131 }
6132
6133 void Bang(int x, int y)
6134 {
6135   int element = MovingOrBlocked2Element(x, y);
6136   int explosion_type = EX_TYPE_NORMAL;
6137
6138   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6139   {
6140     struct PlayerInfo *player = PLAYERINFO(x, y);
6141
6142     element = Tile[x][y] = player->initial_element;
6143
6144     if (level.use_explosion_element[player->index_nr])
6145     {
6146       int explosion_element = level.explosion_element[player->index_nr];
6147
6148       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6149         explosion_type = EX_TYPE_CROSS;
6150       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6151         explosion_type = EX_TYPE_CENTER;
6152     }
6153   }
6154
6155   switch (element)
6156   {
6157     case EL_BUG:
6158     case EL_SPACESHIP:
6159     case EL_BD_BUTTERFLY:
6160     case EL_BD_FIREFLY:
6161     case EL_YAMYAM:
6162     case EL_DARK_YAMYAM:
6163     case EL_ROBOT:
6164     case EL_PACMAN:
6165     case EL_MOLE:
6166       RaiseScoreElement(element);
6167       break;
6168
6169     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6170     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6171     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6172     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6173     case EL_DYNABOMB_INCREASE_NUMBER:
6174     case EL_DYNABOMB_INCREASE_SIZE:
6175     case EL_DYNABOMB_INCREASE_POWER:
6176       explosion_type = EX_TYPE_DYNA;
6177       break;
6178
6179     case EL_DC_LANDMINE:
6180       explosion_type = EX_TYPE_CENTER;
6181       break;
6182
6183     case EL_PENGUIN:
6184     case EL_LAMP:
6185     case EL_LAMP_ACTIVE:
6186     case EL_AMOEBA_TO_DIAMOND:
6187       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6188         explosion_type = EX_TYPE_CENTER;
6189       break;
6190
6191     default:
6192       if (element_info[element].explosion_type == EXPLODES_CROSS)
6193         explosion_type = EX_TYPE_CROSS;
6194       else if (element_info[element].explosion_type == EXPLODES_1X1)
6195         explosion_type = EX_TYPE_CENTER;
6196       break;
6197   }
6198
6199   if (explosion_type == EX_TYPE_DYNA)
6200     DynaExplode(x, y);
6201   else
6202     Explode(x, y, EX_PHASE_START, explosion_type);
6203
6204   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6205 }
6206
6207 static void SplashAcid(int x, int y)
6208 {
6209   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6210       (!IN_LEV_FIELD(x - 1, y - 2) ||
6211        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6212     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6213
6214   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6215       (!IN_LEV_FIELD(x + 1, y - 2) ||
6216        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6217     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6218
6219   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6220 }
6221
6222 static void InitBeltMovement(void)
6223 {
6224   static int belt_base_element[4] =
6225   {
6226     EL_CONVEYOR_BELT_1_LEFT,
6227     EL_CONVEYOR_BELT_2_LEFT,
6228     EL_CONVEYOR_BELT_3_LEFT,
6229     EL_CONVEYOR_BELT_4_LEFT
6230   };
6231   static int belt_base_active_element[4] =
6232   {
6233     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6234     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6235     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6236     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6237   };
6238
6239   int x, y, i, j;
6240
6241   // set frame order for belt animation graphic according to belt direction
6242   for (i = 0; i < NUM_BELTS; i++)
6243   {
6244     int belt_nr = i;
6245
6246     for (j = 0; j < NUM_BELT_PARTS; j++)
6247     {
6248       int element = belt_base_active_element[belt_nr] + j;
6249       int graphic_1 = el2img(element);
6250       int graphic_2 = el2panelimg(element);
6251
6252       if (game.belt_dir[i] == MV_LEFT)
6253       {
6254         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6255         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6256       }
6257       else
6258       {
6259         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6260         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6261       }
6262     }
6263   }
6264
6265   SCAN_PLAYFIELD(x, y)
6266   {
6267     int element = Tile[x][y];
6268
6269     for (i = 0; i < NUM_BELTS; i++)
6270     {
6271       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6272       {
6273         int e_belt_nr = getBeltNrFromBeltElement(element);
6274         int belt_nr = i;
6275
6276         if (e_belt_nr == belt_nr)
6277         {
6278           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6279
6280           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6281         }
6282       }
6283     }
6284   }
6285 }
6286
6287 static void ToggleBeltSwitch(int x, int y)
6288 {
6289   static int belt_base_element[4] =
6290   {
6291     EL_CONVEYOR_BELT_1_LEFT,
6292     EL_CONVEYOR_BELT_2_LEFT,
6293     EL_CONVEYOR_BELT_3_LEFT,
6294     EL_CONVEYOR_BELT_4_LEFT
6295   };
6296   static int belt_base_active_element[4] =
6297   {
6298     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6299     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6300     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6301     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6302   };
6303   static int belt_base_switch_element[4] =
6304   {
6305     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6306     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6307     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6308     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6309   };
6310   static int belt_move_dir[4] =
6311   {
6312     MV_LEFT,
6313     MV_NONE,
6314     MV_RIGHT,
6315     MV_NONE,
6316   };
6317
6318   int element = Tile[x][y];
6319   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6320   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6321   int belt_dir = belt_move_dir[belt_dir_nr];
6322   int xx, yy, i;
6323
6324   if (!IS_BELT_SWITCH(element))
6325     return;
6326
6327   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6328   game.belt_dir[belt_nr] = belt_dir;
6329
6330   if (belt_dir_nr == 3)
6331     belt_dir_nr = 1;
6332
6333   // set frame order for belt animation graphic according to belt direction
6334   for (i = 0; i < NUM_BELT_PARTS; i++)
6335   {
6336     int element = belt_base_active_element[belt_nr] + i;
6337     int graphic_1 = el2img(element);
6338     int graphic_2 = el2panelimg(element);
6339
6340     if (belt_dir == MV_LEFT)
6341     {
6342       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6343       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6344     }
6345     else
6346     {
6347       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6348       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6349     }
6350   }
6351
6352   SCAN_PLAYFIELD(xx, yy)
6353   {
6354     int element = Tile[xx][yy];
6355
6356     if (IS_BELT_SWITCH(element))
6357     {
6358       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6359
6360       if (e_belt_nr == belt_nr)
6361       {
6362         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6363         TEST_DrawLevelField(xx, yy);
6364       }
6365     }
6366     else if (IS_BELT(element) && belt_dir != MV_NONE)
6367     {
6368       int e_belt_nr = getBeltNrFromBeltElement(element);
6369
6370       if (e_belt_nr == belt_nr)
6371       {
6372         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6373
6374         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6375         TEST_DrawLevelField(xx, yy);
6376       }
6377     }
6378     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6379     {
6380       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6381
6382       if (e_belt_nr == belt_nr)
6383       {
6384         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6385
6386         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6387         TEST_DrawLevelField(xx, yy);
6388       }
6389     }
6390   }
6391 }
6392
6393 static void ToggleSwitchgateSwitch(int x, int y)
6394 {
6395   int xx, yy;
6396
6397   game.switchgate_pos = !game.switchgate_pos;
6398
6399   SCAN_PLAYFIELD(xx, yy)
6400   {
6401     int element = Tile[xx][yy];
6402
6403     if (element == EL_SWITCHGATE_SWITCH_UP)
6404     {
6405       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6406       TEST_DrawLevelField(xx, yy);
6407     }
6408     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6409     {
6410       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6411       TEST_DrawLevelField(xx, yy);
6412     }
6413     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6414     {
6415       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6416       TEST_DrawLevelField(xx, yy);
6417     }
6418     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6419     {
6420       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6421       TEST_DrawLevelField(xx, yy);
6422     }
6423     else if (element == EL_SWITCHGATE_OPEN ||
6424              element == EL_SWITCHGATE_OPENING)
6425     {
6426       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6427
6428       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6429     }
6430     else if (element == EL_SWITCHGATE_CLOSED ||
6431              element == EL_SWITCHGATE_CLOSING)
6432     {
6433       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6434
6435       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6436     }
6437   }
6438 }
6439
6440 static int getInvisibleActiveFromInvisibleElement(int element)
6441 {
6442   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6443           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6444           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6445           element);
6446 }
6447
6448 static int getInvisibleFromInvisibleActiveElement(int element)
6449 {
6450   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6451           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6452           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6453           element);
6454 }
6455
6456 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6457 {
6458   int x, y;
6459
6460   SCAN_PLAYFIELD(x, y)
6461   {
6462     int element = Tile[x][y];
6463
6464     if (element == EL_LIGHT_SWITCH &&
6465         game.light_time_left > 0)
6466     {
6467       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6471              game.light_time_left == 0)
6472     {
6473       Tile[x][y] = EL_LIGHT_SWITCH;
6474       TEST_DrawLevelField(x, y);
6475     }
6476     else if (element == EL_EMC_DRIPPER &&
6477              game.light_time_left > 0)
6478     {
6479       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6480       TEST_DrawLevelField(x, y);
6481     }
6482     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6483              game.light_time_left == 0)
6484     {
6485       Tile[x][y] = EL_EMC_DRIPPER;
6486       TEST_DrawLevelField(x, y);
6487     }
6488     else if (element == EL_INVISIBLE_STEELWALL ||
6489              element == EL_INVISIBLE_WALL ||
6490              element == EL_INVISIBLE_SAND)
6491     {
6492       if (game.light_time_left > 0)
6493         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6494
6495       TEST_DrawLevelField(x, y);
6496
6497       // uncrumble neighbour fields, if needed
6498       if (element == EL_INVISIBLE_SAND)
6499         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6500     }
6501     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6502              element == EL_INVISIBLE_WALL_ACTIVE ||
6503              element == EL_INVISIBLE_SAND_ACTIVE)
6504     {
6505       if (game.light_time_left == 0)
6506         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6507
6508       TEST_DrawLevelField(x, y);
6509
6510       // re-crumble neighbour fields, if needed
6511       if (element == EL_INVISIBLE_SAND)
6512         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6513     }
6514   }
6515 }
6516
6517 static void RedrawAllInvisibleElementsForLenses(void)
6518 {
6519   int x, y;
6520
6521   SCAN_PLAYFIELD(x, y)
6522   {
6523     int element = Tile[x][y];
6524
6525     if (element == EL_EMC_DRIPPER &&
6526         game.lenses_time_left > 0)
6527     {
6528       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6529       TEST_DrawLevelField(x, y);
6530     }
6531     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6532              game.lenses_time_left == 0)
6533     {
6534       Tile[x][y] = EL_EMC_DRIPPER;
6535       TEST_DrawLevelField(x, y);
6536     }
6537     else if (element == EL_INVISIBLE_STEELWALL ||
6538              element == EL_INVISIBLE_WALL ||
6539              element == EL_INVISIBLE_SAND)
6540     {
6541       if (game.lenses_time_left > 0)
6542         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6543
6544       TEST_DrawLevelField(x, y);
6545
6546       // uncrumble neighbour fields, if needed
6547       if (element == EL_INVISIBLE_SAND)
6548         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6549     }
6550     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6551              element == EL_INVISIBLE_WALL_ACTIVE ||
6552              element == EL_INVISIBLE_SAND_ACTIVE)
6553     {
6554       if (game.lenses_time_left == 0)
6555         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6556
6557       TEST_DrawLevelField(x, y);
6558
6559       // re-crumble neighbour fields, if needed
6560       if (element == EL_INVISIBLE_SAND)
6561         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6562     }
6563   }
6564 }
6565
6566 static void RedrawAllInvisibleElementsForMagnifier(void)
6567 {
6568   int x, y;
6569
6570   SCAN_PLAYFIELD(x, y)
6571   {
6572     int element = Tile[x][y];
6573
6574     if (element == EL_EMC_FAKE_GRASS &&
6575         game.magnify_time_left > 0)
6576     {
6577       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6578       TEST_DrawLevelField(x, y);
6579     }
6580     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6581              game.magnify_time_left == 0)
6582     {
6583       Tile[x][y] = EL_EMC_FAKE_GRASS;
6584       TEST_DrawLevelField(x, y);
6585     }
6586     else if (IS_GATE_GRAY(element) &&
6587              game.magnify_time_left > 0)
6588     {
6589       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6590                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6591                     IS_EM_GATE_GRAY(element) ?
6592                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6593                     IS_EMC_GATE_GRAY(element) ?
6594                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6595                     IS_DC_GATE_GRAY(element) ?
6596                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6597                     element);
6598       TEST_DrawLevelField(x, y);
6599     }
6600     else if (IS_GATE_GRAY_ACTIVE(element) &&
6601              game.magnify_time_left == 0)
6602     {
6603       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6604                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6605                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6606                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6607                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6608                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6609                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6610                     EL_DC_GATE_WHITE_GRAY :
6611                     element);
6612       TEST_DrawLevelField(x, y);
6613     }
6614   }
6615 }
6616
6617 static void ToggleLightSwitch(int x, int y)
6618 {
6619   int element = Tile[x][y];
6620
6621   game.light_time_left =
6622     (element == EL_LIGHT_SWITCH ?
6623      level.time_light * FRAMES_PER_SECOND : 0);
6624
6625   RedrawAllLightSwitchesAndInvisibleElements();
6626 }
6627
6628 static void ActivateTimegateSwitch(int x, int y)
6629 {
6630   int xx, yy;
6631
6632   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6633
6634   SCAN_PLAYFIELD(xx, yy)
6635   {
6636     int element = Tile[xx][yy];
6637
6638     if (element == EL_TIMEGATE_CLOSED ||
6639         element == EL_TIMEGATE_CLOSING)
6640     {
6641       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6642       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6643     }
6644
6645     /*
6646     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6647     {
6648       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6649       TEST_DrawLevelField(xx, yy);
6650     }
6651     */
6652
6653   }
6654
6655   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6656                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6657 }
6658
6659 static void Impact(int x, int y)
6660 {
6661   boolean last_line = (y == lev_fieldy - 1);
6662   boolean object_hit = FALSE;
6663   boolean impact = (last_line || object_hit);
6664   int element = Tile[x][y];
6665   int smashed = EL_STEELWALL;
6666
6667   if (!last_line)       // check if element below was hit
6668   {
6669     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6670       return;
6671
6672     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6673                                          MovDir[x][y + 1] != MV_DOWN ||
6674                                          MovPos[x][y + 1] <= TILEY / 2));
6675
6676     // do not smash moving elements that left the smashed field in time
6677     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6678         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6679       object_hit = FALSE;
6680
6681 #if USE_QUICKSAND_IMPACT_BUGFIX
6682     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6683     {
6684       RemoveMovingField(x, y + 1);
6685       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6686       Tile[x][y + 2] = EL_ROCK;
6687       TEST_DrawLevelField(x, y + 2);
6688
6689       object_hit = TRUE;
6690     }
6691
6692     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6693     {
6694       RemoveMovingField(x, y + 1);
6695       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6696       Tile[x][y + 2] = EL_ROCK;
6697       TEST_DrawLevelField(x, y + 2);
6698
6699       object_hit = TRUE;
6700     }
6701 #endif
6702
6703     if (object_hit)
6704       smashed = MovingOrBlocked2Element(x, y + 1);
6705
6706     impact = (last_line || object_hit);
6707   }
6708
6709   if (!last_line && smashed == EL_ACID) // element falls into acid
6710   {
6711     SplashAcid(x, y + 1);
6712     return;
6713   }
6714
6715   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6716   // only reset graphic animation if graphic really changes after impact
6717   if (impact &&
6718       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6719   {
6720     ResetGfxAnimation(x, y);
6721     TEST_DrawLevelField(x, y);
6722   }
6723
6724   if (impact && CAN_EXPLODE_IMPACT(element))
6725   {
6726     Bang(x, y);
6727     return;
6728   }
6729   else if (impact && element == EL_PEARL &&
6730            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6731   {
6732     ResetGfxAnimation(x, y);
6733
6734     Tile[x][y] = EL_PEARL_BREAKING;
6735     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6736     return;
6737   }
6738   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6739   {
6740     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6741
6742     return;
6743   }
6744
6745   if (impact && element == EL_AMOEBA_DROP)
6746   {
6747     if (object_hit && IS_PLAYER(x, y + 1))
6748       KillPlayerUnlessEnemyProtected(x, y + 1);
6749     else if (object_hit && smashed == EL_PENGUIN)
6750       Bang(x, y + 1);
6751     else
6752     {
6753       Tile[x][y] = EL_AMOEBA_GROWING;
6754       Store[x][y] = EL_AMOEBA_WET;
6755
6756       ResetRandomAnimationValue(x, y);
6757     }
6758     return;
6759   }
6760
6761   if (object_hit)               // check which object was hit
6762   {
6763     if ((CAN_PASS_MAGIC_WALL(element) && 
6764          (smashed == EL_MAGIC_WALL ||
6765           smashed == EL_BD_MAGIC_WALL)) ||
6766         (CAN_PASS_DC_MAGIC_WALL(element) &&
6767          smashed == EL_DC_MAGIC_WALL))
6768     {
6769       int xx, yy;
6770       int activated_magic_wall =
6771         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6772          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6773          EL_DC_MAGIC_WALL_ACTIVE);
6774
6775       // activate magic wall / mill
6776       SCAN_PLAYFIELD(xx, yy)
6777       {
6778         if (Tile[xx][yy] == smashed)
6779           Tile[xx][yy] = activated_magic_wall;
6780       }
6781
6782       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6783       game.magic_wall_active = TRUE;
6784
6785       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6786                             SND_MAGIC_WALL_ACTIVATING :
6787                             smashed == EL_BD_MAGIC_WALL ?
6788                             SND_BD_MAGIC_WALL_ACTIVATING :
6789                             SND_DC_MAGIC_WALL_ACTIVATING));
6790     }
6791
6792     if (IS_PLAYER(x, y + 1))
6793     {
6794       if (CAN_SMASH_PLAYER(element))
6795       {
6796         KillPlayerUnlessEnemyProtected(x, y + 1);
6797         return;
6798       }
6799     }
6800     else if (smashed == EL_PENGUIN)
6801     {
6802       if (CAN_SMASH_PLAYER(element))
6803       {
6804         Bang(x, y + 1);
6805         return;
6806       }
6807     }
6808     else if (element == EL_BD_DIAMOND)
6809     {
6810       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6811       {
6812         Bang(x, y + 1);
6813         return;
6814       }
6815     }
6816     else if (((element == EL_SP_INFOTRON ||
6817                element == EL_SP_ZONK) &&
6818               (smashed == EL_SP_SNIKSNAK ||
6819                smashed == EL_SP_ELECTRON ||
6820                smashed == EL_SP_DISK_ORANGE)) ||
6821              (element == EL_SP_INFOTRON &&
6822               smashed == EL_SP_DISK_YELLOW))
6823     {
6824       Bang(x, y + 1);
6825       return;
6826     }
6827     else if (CAN_SMASH_EVERYTHING(element))
6828     {
6829       if (IS_CLASSIC_ENEMY(smashed) ||
6830           CAN_EXPLODE_SMASHED(smashed))
6831       {
6832         Bang(x, y + 1);
6833         return;
6834       }
6835       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6836       {
6837         if (smashed == EL_LAMP ||
6838             smashed == EL_LAMP_ACTIVE)
6839         {
6840           Bang(x, y + 1);
6841           return;
6842         }
6843         else if (smashed == EL_NUT)
6844         {
6845           Tile[x][y + 1] = EL_NUT_BREAKING;
6846           PlayLevelSound(x, y, SND_NUT_BREAKING);
6847           RaiseScoreElement(EL_NUT);
6848           return;
6849         }
6850         else if (smashed == EL_PEARL)
6851         {
6852           ResetGfxAnimation(x, y);
6853
6854           Tile[x][y + 1] = EL_PEARL_BREAKING;
6855           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6856           return;
6857         }
6858         else if (smashed == EL_DIAMOND)
6859         {
6860           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6861           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6862           return;
6863         }
6864         else if (IS_BELT_SWITCH(smashed))
6865         {
6866           ToggleBeltSwitch(x, y + 1);
6867         }
6868         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6869                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6870                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6871                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6872         {
6873           ToggleSwitchgateSwitch(x, y + 1);
6874         }
6875         else if (smashed == EL_LIGHT_SWITCH ||
6876                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6877         {
6878           ToggleLightSwitch(x, y + 1);
6879         }
6880         else
6881         {
6882           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6883
6884           CheckElementChangeBySide(x, y + 1, smashed, element,
6885                                    CE_SWITCHED, CH_SIDE_TOP);
6886           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6887                                             CH_SIDE_TOP);
6888         }
6889       }
6890       else
6891       {
6892         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6893       }
6894     }
6895   }
6896
6897   // play sound of magic wall / mill
6898   if (!last_line &&
6899       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6900        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6901        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6902   {
6903     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6904       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6905     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6906       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6907     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6908       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6909
6910     return;
6911   }
6912
6913   // play sound of object that hits the ground
6914   if (last_line || object_hit)
6915     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6916 }
6917
6918 static void TurnRoundExt(int x, int y)
6919 {
6920   static struct
6921   {
6922     int dx, dy;
6923   } move_xy[] =
6924   {
6925     {  0,  0 },
6926     { -1,  0 },
6927     { +1,  0 },
6928     {  0,  0 },
6929     {  0, -1 },
6930     {  0,  0 }, { 0, 0 }, { 0, 0 },
6931     {  0, +1 }
6932   };
6933   static struct
6934   {
6935     int left, right, back;
6936   } turn[] =
6937   {
6938     { 0,        0,              0        },
6939     { MV_DOWN,  MV_UP,          MV_RIGHT },
6940     { MV_UP,    MV_DOWN,        MV_LEFT  },
6941     { 0,        0,              0        },
6942     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6943     { 0,        0,              0        },
6944     { 0,        0,              0        },
6945     { 0,        0,              0        },
6946     { MV_RIGHT, MV_LEFT,        MV_UP    }
6947   };
6948
6949   int element = Tile[x][y];
6950   int move_pattern = element_info[element].move_pattern;
6951
6952   int old_move_dir = MovDir[x][y];
6953   int left_dir  = turn[old_move_dir].left;
6954   int right_dir = turn[old_move_dir].right;
6955   int back_dir  = turn[old_move_dir].back;
6956
6957   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6958   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6959   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6960   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6961
6962   int left_x  = x + left_dx,  left_y  = y + left_dy;
6963   int right_x = x + right_dx, right_y = y + right_dy;
6964   int move_x  = x + move_dx,  move_y  = y + move_dy;
6965
6966   int xx, yy;
6967
6968   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6969   {
6970     TestIfBadThingTouchesOtherBadThing(x, y);
6971
6972     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6973       MovDir[x][y] = right_dir;
6974     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6975       MovDir[x][y] = left_dir;
6976
6977     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6978       MovDelay[x][y] = 9;
6979     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6980       MovDelay[x][y] = 1;
6981   }
6982   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6983   {
6984     TestIfBadThingTouchesOtherBadThing(x, y);
6985
6986     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6987       MovDir[x][y] = left_dir;
6988     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6989       MovDir[x][y] = right_dir;
6990
6991     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6992       MovDelay[x][y] = 9;
6993     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6994       MovDelay[x][y] = 1;
6995   }
6996   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6997   {
6998     TestIfBadThingTouchesOtherBadThing(x, y);
6999
7000     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7001       MovDir[x][y] = left_dir;
7002     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7003       MovDir[x][y] = right_dir;
7004
7005     if (MovDir[x][y] != old_move_dir)
7006       MovDelay[x][y] = 9;
7007   }
7008   else if (element == EL_YAMYAM)
7009   {
7010     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7011     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7012
7013     if (can_turn_left && can_turn_right)
7014       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7015     else if (can_turn_left)
7016       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7017     else if (can_turn_right)
7018       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7019     else
7020       MovDir[x][y] = back_dir;
7021
7022     MovDelay[x][y] = 16 + 16 * RND(3);
7023   }
7024   else if (element == EL_DARK_YAMYAM)
7025   {
7026     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7027                                                          left_x, left_y);
7028     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7029                                                          right_x, right_y);
7030
7031     if (can_turn_left && can_turn_right)
7032       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7033     else if (can_turn_left)
7034       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7035     else if (can_turn_right)
7036       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7037     else
7038       MovDir[x][y] = back_dir;
7039
7040     MovDelay[x][y] = 16 + 16 * RND(3);
7041   }
7042   else if (element == EL_PACMAN)
7043   {
7044     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7045     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7046
7047     if (can_turn_left && can_turn_right)
7048       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7049     else if (can_turn_left)
7050       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7051     else if (can_turn_right)
7052       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7053     else
7054       MovDir[x][y] = back_dir;
7055
7056     MovDelay[x][y] = 6 + RND(40);
7057   }
7058   else if (element == EL_PIG)
7059   {
7060     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7061     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7062     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7063     boolean should_turn_left, should_turn_right, should_move_on;
7064     int rnd_value = 24;
7065     int rnd = RND(rnd_value);
7066
7067     should_turn_left = (can_turn_left &&
7068                         (!can_move_on ||
7069                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7070                                                    y + back_dy + left_dy)));
7071     should_turn_right = (can_turn_right &&
7072                          (!can_move_on ||
7073                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7074                                                     y + back_dy + right_dy)));
7075     should_move_on = (can_move_on &&
7076                       (!can_turn_left ||
7077                        !can_turn_right ||
7078                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7079                                                  y + move_dy + left_dy) ||
7080                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7081                                                  y + move_dy + right_dy)));
7082
7083     if (should_turn_left || should_turn_right || should_move_on)
7084     {
7085       if (should_turn_left && should_turn_right && should_move_on)
7086         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7087                         rnd < 2 * rnd_value / 3 ? right_dir :
7088                         old_move_dir);
7089       else if (should_turn_left && should_turn_right)
7090         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7091       else if (should_turn_left && should_move_on)
7092         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7093       else if (should_turn_right && should_move_on)
7094         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7095       else if (should_turn_left)
7096         MovDir[x][y] = left_dir;
7097       else if (should_turn_right)
7098         MovDir[x][y] = right_dir;
7099       else if (should_move_on)
7100         MovDir[x][y] = old_move_dir;
7101     }
7102     else if (can_move_on && rnd > rnd_value / 8)
7103       MovDir[x][y] = old_move_dir;
7104     else if (can_turn_left && can_turn_right)
7105       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7106     else if (can_turn_left && rnd > rnd_value / 8)
7107       MovDir[x][y] = left_dir;
7108     else if (can_turn_right && rnd > rnd_value/8)
7109       MovDir[x][y] = right_dir;
7110     else
7111       MovDir[x][y] = back_dir;
7112
7113     xx = x + move_xy[MovDir[x][y]].dx;
7114     yy = y + move_xy[MovDir[x][y]].dy;
7115
7116     if (!IN_LEV_FIELD(xx, yy) ||
7117         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7118       MovDir[x][y] = old_move_dir;
7119
7120     MovDelay[x][y] = 0;
7121   }
7122   else if (element == EL_DRAGON)
7123   {
7124     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7125     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7126     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7127     int rnd_value = 24;
7128     int rnd = RND(rnd_value);
7129
7130     if (can_move_on && rnd > rnd_value / 8)
7131       MovDir[x][y] = old_move_dir;
7132     else if (can_turn_left && can_turn_right)
7133       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7134     else if (can_turn_left && rnd > rnd_value / 8)
7135       MovDir[x][y] = left_dir;
7136     else if (can_turn_right && rnd > rnd_value / 8)
7137       MovDir[x][y] = right_dir;
7138     else
7139       MovDir[x][y] = back_dir;
7140
7141     xx = x + move_xy[MovDir[x][y]].dx;
7142     yy = y + move_xy[MovDir[x][y]].dy;
7143
7144     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7145       MovDir[x][y] = old_move_dir;
7146
7147     MovDelay[x][y] = 0;
7148   }
7149   else if (element == EL_MOLE)
7150   {
7151     boolean can_move_on =
7152       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7153                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7154                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7155     if (!can_move_on)
7156     {
7157       boolean can_turn_left =
7158         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7159                               IS_AMOEBOID(Tile[left_x][left_y])));
7160
7161       boolean can_turn_right =
7162         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7163                               IS_AMOEBOID(Tile[right_x][right_y])));
7164
7165       if (can_turn_left && can_turn_right)
7166         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7167       else if (can_turn_left)
7168         MovDir[x][y] = left_dir;
7169       else
7170         MovDir[x][y] = right_dir;
7171     }
7172
7173     if (MovDir[x][y] != old_move_dir)
7174       MovDelay[x][y] = 9;
7175   }
7176   else if (element == EL_BALLOON)
7177   {
7178     MovDir[x][y] = game.wind_direction;
7179     MovDelay[x][y] = 0;
7180   }
7181   else if (element == EL_SPRING)
7182   {
7183     if (MovDir[x][y] & MV_HORIZONTAL)
7184     {
7185       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7186           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7187       {
7188         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7189         ResetGfxAnimation(move_x, move_y);
7190         TEST_DrawLevelField(move_x, move_y);
7191
7192         MovDir[x][y] = back_dir;
7193       }
7194       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7195                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7196         MovDir[x][y] = MV_NONE;
7197     }
7198
7199     MovDelay[x][y] = 0;
7200   }
7201   else if (element == EL_ROBOT ||
7202            element == EL_SATELLITE ||
7203            element == EL_PENGUIN ||
7204            element == EL_EMC_ANDROID)
7205   {
7206     int attr_x = -1, attr_y = -1;
7207
7208     if (game.all_players_gone)
7209     {
7210       attr_x = game.exit_x;
7211       attr_y = game.exit_y;
7212     }
7213     else
7214     {
7215       int i;
7216
7217       for (i = 0; i < MAX_PLAYERS; i++)
7218       {
7219         struct PlayerInfo *player = &stored_player[i];
7220         int jx = player->jx, jy = player->jy;
7221
7222         if (!player->active)
7223           continue;
7224
7225         if (attr_x == -1 ||
7226             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7227         {
7228           attr_x = jx;
7229           attr_y = jy;
7230         }
7231       }
7232     }
7233
7234     if (element == EL_ROBOT &&
7235         game.robot_wheel_x >= 0 &&
7236         game.robot_wheel_y >= 0 &&
7237         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7238          game.engine_version < VERSION_IDENT(3,1,0,0)))
7239     {
7240       attr_x = game.robot_wheel_x;
7241       attr_y = game.robot_wheel_y;
7242     }
7243
7244     if (element == EL_PENGUIN)
7245     {
7246       int i;
7247       static int xy[4][2] =
7248       {
7249         { 0, -1 },
7250         { -1, 0 },
7251         { +1, 0 },
7252         { 0, +1 }
7253       };
7254
7255       for (i = 0; i < NUM_DIRECTIONS; i++)
7256       {
7257         int ex = x + xy[i][0];
7258         int ey = y + xy[i][1];
7259
7260         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7261                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7262                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7263                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7264         {
7265           attr_x = ex;
7266           attr_y = ey;
7267           break;
7268         }
7269       }
7270     }
7271
7272     MovDir[x][y] = MV_NONE;
7273     if (attr_x < x)
7274       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7275     else if (attr_x > x)
7276       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7277     if (attr_y < y)
7278       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7279     else if (attr_y > y)
7280       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7281
7282     if (element == EL_ROBOT)
7283     {
7284       int newx, newy;
7285
7286       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7287         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7288       Moving2Blocked(x, y, &newx, &newy);
7289
7290       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7291         MovDelay[x][y] = 8 + 8 * !RND(3);
7292       else
7293         MovDelay[x][y] = 16;
7294     }
7295     else if (element == EL_PENGUIN)
7296     {
7297       int newx, newy;
7298
7299       MovDelay[x][y] = 1;
7300
7301       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7302       {
7303         boolean first_horiz = RND(2);
7304         int new_move_dir = MovDir[x][y];
7305
7306         MovDir[x][y] =
7307           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7308         Moving2Blocked(x, y, &newx, &newy);
7309
7310         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7311           return;
7312
7313         MovDir[x][y] =
7314           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7315         Moving2Blocked(x, y, &newx, &newy);
7316
7317         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7318           return;
7319
7320         MovDir[x][y] = old_move_dir;
7321         return;
7322       }
7323     }
7324     else if (element == EL_SATELLITE)
7325     {
7326       int newx, newy;
7327
7328       MovDelay[x][y] = 1;
7329
7330       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7331       {
7332         boolean first_horiz = RND(2);
7333         int new_move_dir = MovDir[x][y];
7334
7335         MovDir[x][y] =
7336           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7337         Moving2Blocked(x, y, &newx, &newy);
7338
7339         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7340           return;
7341
7342         MovDir[x][y] =
7343           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7344         Moving2Blocked(x, y, &newx, &newy);
7345
7346         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7347           return;
7348
7349         MovDir[x][y] = old_move_dir;
7350         return;
7351       }
7352     }
7353     else if (element == EL_EMC_ANDROID)
7354     {
7355       static int check_pos[16] =
7356       {
7357         -1,             //  0 => (invalid)
7358         7,              //  1 => MV_LEFT
7359         3,              //  2 => MV_RIGHT
7360         -1,             //  3 => (invalid)
7361         1,              //  4 =>            MV_UP
7362         0,              //  5 => MV_LEFT  | MV_UP
7363         2,              //  6 => MV_RIGHT | MV_UP
7364         -1,             //  7 => (invalid)
7365         5,              //  8 =>            MV_DOWN
7366         6,              //  9 => MV_LEFT  | MV_DOWN
7367         4,              // 10 => MV_RIGHT | MV_DOWN
7368         -1,             // 11 => (invalid)
7369         -1,             // 12 => (invalid)
7370         -1,             // 13 => (invalid)
7371         -1,             // 14 => (invalid)
7372         -1,             // 15 => (invalid)
7373       };
7374       static struct
7375       {
7376         int dx, dy;
7377         int dir;
7378       } check_xy[8] =
7379       {
7380         { -1, -1,       MV_LEFT  | MV_UP   },
7381         {  0, -1,                  MV_UP   },
7382         { +1, -1,       MV_RIGHT | MV_UP   },
7383         { +1,  0,       MV_RIGHT           },
7384         { +1, +1,       MV_RIGHT | MV_DOWN },
7385         {  0, +1,                  MV_DOWN },
7386         { -1, +1,       MV_LEFT  | MV_DOWN },
7387         { -1,  0,       MV_LEFT            },
7388       };
7389       int start_pos, check_order;
7390       boolean can_clone = FALSE;
7391       int i;
7392
7393       // check if there is any free field around current position
7394       for (i = 0; i < 8; i++)
7395       {
7396         int newx = x + check_xy[i].dx;
7397         int newy = y + check_xy[i].dy;
7398
7399         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7400         {
7401           can_clone = TRUE;
7402
7403           break;
7404         }
7405       }
7406
7407       if (can_clone)            // randomly find an element to clone
7408       {
7409         can_clone = FALSE;
7410
7411         start_pos = check_pos[RND(8)];
7412         check_order = (RND(2) ? -1 : +1);
7413
7414         for (i = 0; i < 8; i++)
7415         {
7416           int pos_raw = start_pos + i * check_order;
7417           int pos = (pos_raw + 8) % 8;
7418           int newx = x + check_xy[pos].dx;
7419           int newy = y + check_xy[pos].dy;
7420
7421           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7422           {
7423             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7424             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7425
7426             Store[x][y] = Tile[newx][newy];
7427
7428             can_clone = TRUE;
7429
7430             break;
7431           }
7432         }
7433       }
7434
7435       if (can_clone)            // randomly find a direction to move
7436       {
7437         can_clone = FALSE;
7438
7439         start_pos = check_pos[RND(8)];
7440         check_order = (RND(2) ? -1 : +1);
7441
7442         for (i = 0; i < 8; i++)
7443         {
7444           int pos_raw = start_pos + i * check_order;
7445           int pos = (pos_raw + 8) % 8;
7446           int newx = x + check_xy[pos].dx;
7447           int newy = y + check_xy[pos].dy;
7448           int new_move_dir = check_xy[pos].dir;
7449
7450           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7451           {
7452             MovDir[x][y] = new_move_dir;
7453             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7454
7455             can_clone = TRUE;
7456
7457             break;
7458           }
7459         }
7460       }
7461
7462       if (can_clone)            // cloning and moving successful
7463         return;
7464
7465       // cannot clone -- try to move towards player
7466
7467       start_pos = check_pos[MovDir[x][y] & 0x0f];
7468       check_order = (RND(2) ? -1 : +1);
7469
7470       for (i = 0; i < 3; i++)
7471       {
7472         // first check start_pos, then previous/next or (next/previous) pos
7473         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7474         int pos = (pos_raw + 8) % 8;
7475         int newx = x + check_xy[pos].dx;
7476         int newy = y + check_xy[pos].dy;
7477         int new_move_dir = check_xy[pos].dir;
7478
7479         if (IS_PLAYER(newx, newy))
7480           break;
7481
7482         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7483         {
7484           MovDir[x][y] = new_move_dir;
7485           MovDelay[x][y] = level.android_move_time * 8 + 1;
7486
7487           break;
7488         }
7489       }
7490     }
7491   }
7492   else if (move_pattern == MV_TURNING_LEFT ||
7493            move_pattern == MV_TURNING_RIGHT ||
7494            move_pattern == MV_TURNING_LEFT_RIGHT ||
7495            move_pattern == MV_TURNING_RIGHT_LEFT ||
7496            move_pattern == MV_TURNING_RANDOM ||
7497            move_pattern == MV_ALL_DIRECTIONS)
7498   {
7499     boolean can_turn_left =
7500       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7501     boolean can_turn_right =
7502       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7503
7504     if (element_info[element].move_stepsize == 0)       // "not moving"
7505       return;
7506
7507     if (move_pattern == MV_TURNING_LEFT)
7508       MovDir[x][y] = left_dir;
7509     else if (move_pattern == MV_TURNING_RIGHT)
7510       MovDir[x][y] = right_dir;
7511     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7512       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7513     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7514       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7515     else if (move_pattern == MV_TURNING_RANDOM)
7516       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7517                       can_turn_right && !can_turn_left ? right_dir :
7518                       RND(2) ? left_dir : right_dir);
7519     else if (can_turn_left && can_turn_right)
7520       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7521     else if (can_turn_left)
7522       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7523     else if (can_turn_right)
7524       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7525     else
7526       MovDir[x][y] = back_dir;
7527
7528     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7529   }
7530   else if (move_pattern == MV_HORIZONTAL ||
7531            move_pattern == MV_VERTICAL)
7532   {
7533     if (move_pattern & old_move_dir)
7534       MovDir[x][y] = back_dir;
7535     else if (move_pattern == MV_HORIZONTAL)
7536       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7537     else if (move_pattern == MV_VERTICAL)
7538       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7539
7540     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7541   }
7542   else if (move_pattern & MV_ANY_DIRECTION)
7543   {
7544     MovDir[x][y] = move_pattern;
7545     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7546   }
7547   else if (move_pattern & MV_WIND_DIRECTION)
7548   {
7549     MovDir[x][y] = game.wind_direction;
7550     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7551   }
7552   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7553   {
7554     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7555       MovDir[x][y] = left_dir;
7556     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7557       MovDir[x][y] = right_dir;
7558
7559     if (MovDir[x][y] != old_move_dir)
7560       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7561   }
7562   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7563   {
7564     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7565       MovDir[x][y] = right_dir;
7566     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7567       MovDir[x][y] = left_dir;
7568
7569     if (MovDir[x][y] != old_move_dir)
7570       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7571   }
7572   else if (move_pattern == MV_TOWARDS_PLAYER ||
7573            move_pattern == MV_AWAY_FROM_PLAYER)
7574   {
7575     int attr_x = -1, attr_y = -1;
7576     int newx, newy;
7577     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7578
7579     if (game.all_players_gone)
7580     {
7581       attr_x = game.exit_x;
7582       attr_y = game.exit_y;
7583     }
7584     else
7585     {
7586       int i;
7587
7588       for (i = 0; i < MAX_PLAYERS; i++)
7589       {
7590         struct PlayerInfo *player = &stored_player[i];
7591         int jx = player->jx, jy = player->jy;
7592
7593         if (!player->active)
7594           continue;
7595
7596         if (attr_x == -1 ||
7597             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7598         {
7599           attr_x = jx;
7600           attr_y = jy;
7601         }
7602       }
7603     }
7604
7605     MovDir[x][y] = MV_NONE;
7606     if (attr_x < x)
7607       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7608     else if (attr_x > x)
7609       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7610     if (attr_y < y)
7611       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7612     else if (attr_y > y)
7613       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7614
7615     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7616
7617     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7618     {
7619       boolean first_horiz = RND(2);
7620       int new_move_dir = MovDir[x][y];
7621
7622       if (element_info[element].move_stepsize == 0)     // "not moving"
7623       {
7624         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7625         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7626
7627         return;
7628       }
7629
7630       MovDir[x][y] =
7631         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7632       Moving2Blocked(x, y, &newx, &newy);
7633
7634       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7635         return;
7636
7637       MovDir[x][y] =
7638         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7639       Moving2Blocked(x, y, &newx, &newy);
7640
7641       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7642         return;
7643
7644       MovDir[x][y] = old_move_dir;
7645     }
7646   }
7647   else if (move_pattern == MV_WHEN_PUSHED ||
7648            move_pattern == MV_WHEN_DROPPED)
7649   {
7650     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7651       MovDir[x][y] = MV_NONE;
7652
7653     MovDelay[x][y] = 0;
7654   }
7655   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7656   {
7657     static int test_xy[7][2] =
7658     {
7659       { 0, -1 },
7660       { -1, 0 },
7661       { +1, 0 },
7662       { 0, +1 },
7663       { 0, -1 },
7664       { -1, 0 },
7665       { +1, 0 },
7666     };
7667     static int test_dir[7] =
7668     {
7669       MV_UP,
7670       MV_LEFT,
7671       MV_RIGHT,
7672       MV_DOWN,
7673       MV_UP,
7674       MV_LEFT,
7675       MV_RIGHT,
7676     };
7677     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7678     int move_preference = -1000000;     // start with very low preference
7679     int new_move_dir = MV_NONE;
7680     int start_test = RND(4);
7681     int i;
7682
7683     for (i = 0; i < NUM_DIRECTIONS; i++)
7684     {
7685       int move_dir = test_dir[start_test + i];
7686       int move_dir_preference;
7687
7688       xx = x + test_xy[start_test + i][0];
7689       yy = y + test_xy[start_test + i][1];
7690
7691       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7692           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7693       {
7694         new_move_dir = move_dir;
7695
7696         break;
7697       }
7698
7699       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7700         continue;
7701
7702       move_dir_preference = -1 * RunnerVisit[xx][yy];
7703       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7704         move_dir_preference = PlayerVisit[xx][yy];
7705
7706       if (move_dir_preference > move_preference)
7707       {
7708         // prefer field that has not been visited for the longest time
7709         move_preference = move_dir_preference;
7710         new_move_dir = move_dir;
7711       }
7712       else if (move_dir_preference == move_preference &&
7713                move_dir == old_move_dir)
7714       {
7715         // prefer last direction when all directions are preferred equally
7716         move_preference = move_dir_preference;
7717         new_move_dir = move_dir;
7718       }
7719     }
7720
7721     MovDir[x][y] = new_move_dir;
7722     if (old_move_dir != new_move_dir)
7723       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7724   }
7725 }
7726
7727 static void TurnRound(int x, int y)
7728 {
7729   int direction = MovDir[x][y];
7730
7731   TurnRoundExt(x, y);
7732
7733   GfxDir[x][y] = MovDir[x][y];
7734
7735   if (direction != MovDir[x][y])
7736     GfxFrame[x][y] = 0;
7737
7738   if (MovDelay[x][y])
7739     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7740
7741   ResetGfxFrame(x, y);
7742 }
7743
7744 static boolean JustBeingPushed(int x, int y)
7745 {
7746   int i;
7747
7748   for (i = 0; i < MAX_PLAYERS; i++)
7749   {
7750     struct PlayerInfo *player = &stored_player[i];
7751
7752     if (player->active && player->is_pushing && player->MovPos)
7753     {
7754       int next_jx = player->jx + (player->jx - player->last_jx);
7755       int next_jy = player->jy + (player->jy - player->last_jy);
7756
7757       if (x == next_jx && y == next_jy)
7758         return TRUE;
7759     }
7760   }
7761
7762   return FALSE;
7763 }
7764
7765 static void StartMoving(int x, int y)
7766 {
7767   boolean started_moving = FALSE;       // some elements can fall _and_ move
7768   int element = Tile[x][y];
7769
7770   if (Stop[x][y])
7771     return;
7772
7773   if (MovDelay[x][y] == 0)
7774     GfxAction[x][y] = ACTION_DEFAULT;
7775
7776   if (CAN_FALL(element) && y < lev_fieldy - 1)
7777   {
7778     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7779         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7780       if (JustBeingPushed(x, y))
7781         return;
7782
7783     if (element == EL_QUICKSAND_FULL)
7784     {
7785       if (IS_FREE(x, y + 1))
7786       {
7787         InitMovingField(x, y, MV_DOWN);
7788         started_moving = TRUE;
7789
7790         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7791 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7792         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7793           Store[x][y] = EL_ROCK;
7794 #else
7795         Store[x][y] = EL_ROCK;
7796 #endif
7797
7798         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7799       }
7800       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7801       {
7802         if (!MovDelay[x][y])
7803         {
7804           MovDelay[x][y] = TILEY + 1;
7805
7806           ResetGfxAnimation(x, y);
7807           ResetGfxAnimation(x, y + 1);
7808         }
7809
7810         if (MovDelay[x][y])
7811         {
7812           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7813           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7814
7815           MovDelay[x][y]--;
7816           if (MovDelay[x][y])
7817             return;
7818         }
7819
7820         Tile[x][y] = EL_QUICKSAND_EMPTY;
7821         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7822         Store[x][y + 1] = Store[x][y];
7823         Store[x][y] = 0;
7824
7825         PlayLevelSoundAction(x, y, ACTION_FILLING);
7826       }
7827       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7828       {
7829         if (!MovDelay[x][y])
7830         {
7831           MovDelay[x][y] = TILEY + 1;
7832
7833           ResetGfxAnimation(x, y);
7834           ResetGfxAnimation(x, y + 1);
7835         }
7836
7837         if (MovDelay[x][y])
7838         {
7839           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7840           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7841
7842           MovDelay[x][y]--;
7843           if (MovDelay[x][y])
7844             return;
7845         }
7846
7847         Tile[x][y] = EL_QUICKSAND_EMPTY;
7848         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7849         Store[x][y + 1] = Store[x][y];
7850         Store[x][y] = 0;
7851
7852         PlayLevelSoundAction(x, y, ACTION_FILLING);
7853       }
7854     }
7855     else if (element == EL_QUICKSAND_FAST_FULL)
7856     {
7857       if (IS_FREE(x, y + 1))
7858       {
7859         InitMovingField(x, y, MV_DOWN);
7860         started_moving = TRUE;
7861
7862         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7863 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7864         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7865           Store[x][y] = EL_ROCK;
7866 #else
7867         Store[x][y] = EL_ROCK;
7868 #endif
7869
7870         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7871       }
7872       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7873       {
7874         if (!MovDelay[x][y])
7875         {
7876           MovDelay[x][y] = TILEY + 1;
7877
7878           ResetGfxAnimation(x, y);
7879           ResetGfxAnimation(x, y + 1);
7880         }
7881
7882         if (MovDelay[x][y])
7883         {
7884           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7885           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7886
7887           MovDelay[x][y]--;
7888           if (MovDelay[x][y])
7889             return;
7890         }
7891
7892         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7893         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7894         Store[x][y + 1] = Store[x][y];
7895         Store[x][y] = 0;
7896
7897         PlayLevelSoundAction(x, y, ACTION_FILLING);
7898       }
7899       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7900       {
7901         if (!MovDelay[x][y])
7902         {
7903           MovDelay[x][y] = TILEY + 1;
7904
7905           ResetGfxAnimation(x, y);
7906           ResetGfxAnimation(x, y + 1);
7907         }
7908
7909         if (MovDelay[x][y])
7910         {
7911           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7912           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7913
7914           MovDelay[x][y]--;
7915           if (MovDelay[x][y])
7916             return;
7917         }
7918
7919         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7920         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7921         Store[x][y + 1] = Store[x][y];
7922         Store[x][y] = 0;
7923
7924         PlayLevelSoundAction(x, y, ACTION_FILLING);
7925       }
7926     }
7927     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7928              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7929     {
7930       InitMovingField(x, y, MV_DOWN);
7931       started_moving = TRUE;
7932
7933       Tile[x][y] = EL_QUICKSAND_FILLING;
7934       Store[x][y] = element;
7935
7936       PlayLevelSoundAction(x, y, ACTION_FILLING);
7937     }
7938     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7939              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7940     {
7941       InitMovingField(x, y, MV_DOWN);
7942       started_moving = TRUE;
7943
7944       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7945       Store[x][y] = element;
7946
7947       PlayLevelSoundAction(x, y, ACTION_FILLING);
7948     }
7949     else if (element == EL_MAGIC_WALL_FULL)
7950     {
7951       if (IS_FREE(x, y + 1))
7952       {
7953         InitMovingField(x, y, MV_DOWN);
7954         started_moving = TRUE;
7955
7956         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7957         Store[x][y] = EL_CHANGED(Store[x][y]);
7958       }
7959       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7960       {
7961         if (!MovDelay[x][y])
7962           MovDelay[x][y] = TILEY / 4 + 1;
7963
7964         if (MovDelay[x][y])
7965         {
7966           MovDelay[x][y]--;
7967           if (MovDelay[x][y])
7968             return;
7969         }
7970
7971         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7972         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7973         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7974         Store[x][y] = 0;
7975       }
7976     }
7977     else if (element == EL_BD_MAGIC_WALL_FULL)
7978     {
7979       if (IS_FREE(x, y + 1))
7980       {
7981         InitMovingField(x, y, MV_DOWN);
7982         started_moving = TRUE;
7983
7984         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7985         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7986       }
7987       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7988       {
7989         if (!MovDelay[x][y])
7990           MovDelay[x][y] = TILEY / 4 + 1;
7991
7992         if (MovDelay[x][y])
7993         {
7994           MovDelay[x][y]--;
7995           if (MovDelay[x][y])
7996             return;
7997         }
7998
7999         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8000         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8001         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8002         Store[x][y] = 0;
8003       }
8004     }
8005     else if (element == EL_DC_MAGIC_WALL_FULL)
8006     {
8007       if (IS_FREE(x, y + 1))
8008       {
8009         InitMovingField(x, y, MV_DOWN);
8010         started_moving = TRUE;
8011
8012         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8013         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8014       }
8015       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8016       {
8017         if (!MovDelay[x][y])
8018           MovDelay[x][y] = TILEY / 4 + 1;
8019
8020         if (MovDelay[x][y])
8021         {
8022           MovDelay[x][y]--;
8023           if (MovDelay[x][y])
8024             return;
8025         }
8026
8027         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8028         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8029         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8030         Store[x][y] = 0;
8031       }
8032     }
8033     else if ((CAN_PASS_MAGIC_WALL(element) &&
8034               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8035                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8036              (CAN_PASS_DC_MAGIC_WALL(element) &&
8037               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8038
8039     {
8040       InitMovingField(x, y, MV_DOWN);
8041       started_moving = TRUE;
8042
8043       Tile[x][y] =
8044         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8045          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8046          EL_DC_MAGIC_WALL_FILLING);
8047       Store[x][y] = element;
8048     }
8049     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8050     {
8051       SplashAcid(x, y + 1);
8052
8053       InitMovingField(x, y, MV_DOWN);
8054       started_moving = TRUE;
8055
8056       Store[x][y] = EL_ACID;
8057     }
8058     else if (
8059              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8060               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8061              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8062               CAN_FALL(element) && WasJustFalling[x][y] &&
8063               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8064
8065              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8066               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8067               (Tile[x][y + 1] == EL_BLOCKED)))
8068     {
8069       /* this is needed for a special case not covered by calling "Impact()"
8070          from "ContinueMoving()": if an element moves to a tile directly below
8071          another element which was just falling on that tile (which was empty
8072          in the previous frame), the falling element above would just stop
8073          instead of smashing the element below (in previous version, the above
8074          element was just checked for "moving" instead of "falling", resulting
8075          in incorrect smashes caused by horizontal movement of the above
8076          element; also, the case of the player being the element to smash was
8077          simply not covered here... :-/ ) */
8078
8079       CheckCollision[x][y] = 0;
8080       CheckImpact[x][y] = 0;
8081
8082       Impact(x, y);
8083     }
8084     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8085     {
8086       if (MovDir[x][y] == MV_NONE)
8087       {
8088         InitMovingField(x, y, MV_DOWN);
8089         started_moving = TRUE;
8090       }
8091     }
8092     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8093     {
8094       if (WasJustFalling[x][y]) // prevent animation from being restarted
8095         MovDir[x][y] = MV_DOWN;
8096
8097       InitMovingField(x, y, MV_DOWN);
8098       started_moving = TRUE;
8099     }
8100     else if (element == EL_AMOEBA_DROP)
8101     {
8102       Tile[x][y] = EL_AMOEBA_GROWING;
8103       Store[x][y] = EL_AMOEBA_WET;
8104     }
8105     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8106               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8107              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8108              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8109     {
8110       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8111                                 (IS_FREE(x - 1, y + 1) ||
8112                                  Tile[x - 1][y + 1] == EL_ACID));
8113       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8114                                 (IS_FREE(x + 1, y + 1) ||
8115                                  Tile[x + 1][y + 1] == EL_ACID));
8116       boolean can_fall_any  = (can_fall_left || can_fall_right);
8117       boolean can_fall_both = (can_fall_left && can_fall_right);
8118       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8119
8120       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8121       {
8122         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8123           can_fall_right = FALSE;
8124         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8125           can_fall_left = FALSE;
8126         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8127           can_fall_right = FALSE;
8128         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8129           can_fall_left = FALSE;
8130
8131         can_fall_any  = (can_fall_left || can_fall_right);
8132         can_fall_both = FALSE;
8133       }
8134
8135       if (can_fall_both)
8136       {
8137         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8138           can_fall_right = FALSE;       // slip down on left side
8139         else
8140           can_fall_left = !(can_fall_right = RND(2));
8141
8142         can_fall_both = FALSE;
8143       }
8144
8145       if (can_fall_any)
8146       {
8147         // if not determined otherwise, prefer left side for slipping down
8148         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8149         started_moving = TRUE;
8150       }
8151     }
8152     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8153     {
8154       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8155       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8156       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8157       int belt_dir = game.belt_dir[belt_nr];
8158
8159       if ((belt_dir == MV_LEFT  && left_is_free) ||
8160           (belt_dir == MV_RIGHT && right_is_free))
8161       {
8162         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8163
8164         InitMovingField(x, y, belt_dir);
8165         started_moving = TRUE;
8166
8167         Pushed[x][y] = TRUE;
8168         Pushed[nextx][y] = TRUE;
8169
8170         GfxAction[x][y] = ACTION_DEFAULT;
8171       }
8172       else
8173       {
8174         MovDir[x][y] = 0;       // if element was moving, stop it
8175       }
8176     }
8177   }
8178
8179   // not "else if" because of elements that can fall and move (EL_SPRING)
8180   if (CAN_MOVE(element) && !started_moving)
8181   {
8182     int move_pattern = element_info[element].move_pattern;
8183     int newx, newy;
8184
8185     Moving2Blocked(x, y, &newx, &newy);
8186
8187     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8188       return;
8189
8190     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8191         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8192     {
8193       WasJustMoving[x][y] = 0;
8194       CheckCollision[x][y] = 0;
8195
8196       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8197
8198       if (Tile[x][y] != element)        // element has changed
8199         return;
8200     }
8201
8202     if (!MovDelay[x][y])        // start new movement phase
8203     {
8204       // all objects that can change their move direction after each step
8205       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8206
8207       if (element != EL_YAMYAM &&
8208           element != EL_DARK_YAMYAM &&
8209           element != EL_PACMAN &&
8210           !(move_pattern & MV_ANY_DIRECTION) &&
8211           move_pattern != MV_TURNING_LEFT &&
8212           move_pattern != MV_TURNING_RIGHT &&
8213           move_pattern != MV_TURNING_LEFT_RIGHT &&
8214           move_pattern != MV_TURNING_RIGHT_LEFT &&
8215           move_pattern != MV_TURNING_RANDOM)
8216       {
8217         TurnRound(x, y);
8218
8219         if (MovDelay[x][y] && (element == EL_BUG ||
8220                                element == EL_SPACESHIP ||
8221                                element == EL_SP_SNIKSNAK ||
8222                                element == EL_SP_ELECTRON ||
8223                                element == EL_MOLE))
8224           TEST_DrawLevelField(x, y);
8225       }
8226     }
8227
8228     if (MovDelay[x][y])         // wait some time before next movement
8229     {
8230       MovDelay[x][y]--;
8231
8232       if (element == EL_ROBOT ||
8233           element == EL_YAMYAM ||
8234           element == EL_DARK_YAMYAM)
8235       {
8236         DrawLevelElementAnimationIfNeeded(x, y, element);
8237         PlayLevelSoundAction(x, y, ACTION_WAITING);
8238       }
8239       else if (element == EL_SP_ELECTRON)
8240         DrawLevelElementAnimationIfNeeded(x, y, element);
8241       else if (element == EL_DRAGON)
8242       {
8243         int i;
8244         int dir = MovDir[x][y];
8245         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8246         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8247         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8248                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8249                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8250                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8251         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8252
8253         GfxAction[x][y] = ACTION_ATTACKING;
8254
8255         if (IS_PLAYER(x, y))
8256           DrawPlayerField(x, y);
8257         else
8258           TEST_DrawLevelField(x, y);
8259
8260         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8261
8262         for (i = 1; i <= 3; i++)
8263         {
8264           int xx = x + i * dx;
8265           int yy = y + i * dy;
8266           int sx = SCREENX(xx);
8267           int sy = SCREENY(yy);
8268           int flame_graphic = graphic + (i - 1);
8269
8270           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8271             break;
8272
8273           if (MovDelay[x][y])
8274           {
8275             int flamed = MovingOrBlocked2Element(xx, yy);
8276
8277             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8278               Bang(xx, yy);
8279             else
8280               RemoveMovingField(xx, yy);
8281
8282             ChangeDelay[xx][yy] = 0;
8283
8284             Tile[xx][yy] = EL_FLAMES;
8285
8286             if (IN_SCR_FIELD(sx, sy))
8287             {
8288               TEST_DrawLevelFieldCrumbled(xx, yy);
8289               DrawGraphic(sx, sy, flame_graphic, frame);
8290             }
8291           }
8292           else
8293           {
8294             if (Tile[xx][yy] == EL_FLAMES)
8295               Tile[xx][yy] = EL_EMPTY;
8296             TEST_DrawLevelField(xx, yy);
8297           }
8298         }
8299       }
8300
8301       if (MovDelay[x][y])       // element still has to wait some time
8302       {
8303         PlayLevelSoundAction(x, y, ACTION_WAITING);
8304
8305         return;
8306       }
8307     }
8308
8309     // now make next step
8310
8311     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8312
8313     if (DONT_COLLIDE_WITH(element) &&
8314         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8315         !PLAYER_ENEMY_PROTECTED(newx, newy))
8316     {
8317       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8318
8319       return;
8320     }
8321
8322     else if (CAN_MOVE_INTO_ACID(element) &&
8323              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8324              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8325              (MovDir[x][y] == MV_DOWN ||
8326               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8327     {
8328       SplashAcid(newx, newy);
8329       Store[x][y] = EL_ACID;
8330     }
8331     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8332     {
8333       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8334           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8335           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8336           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8337       {
8338         RemoveField(x, y);
8339         TEST_DrawLevelField(x, y);
8340
8341         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8342         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8343           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8344
8345         game.friends_still_needed--;
8346         if (!game.friends_still_needed &&
8347             !game.GameOver &&
8348             game.all_players_gone)
8349           LevelSolved();
8350
8351         return;
8352       }
8353       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8354       {
8355         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8356           TEST_DrawLevelField(newx, newy);
8357         else
8358           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8359       }
8360       else if (!IS_FREE(newx, newy))
8361       {
8362         GfxAction[x][y] = ACTION_WAITING;
8363
8364         if (IS_PLAYER(x, y))
8365           DrawPlayerField(x, y);
8366         else
8367           TEST_DrawLevelField(x, y);
8368
8369         return;
8370       }
8371     }
8372     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8373     {
8374       if (IS_FOOD_PIG(Tile[newx][newy]))
8375       {
8376         if (IS_MOVING(newx, newy))
8377           RemoveMovingField(newx, newy);
8378         else
8379         {
8380           Tile[newx][newy] = EL_EMPTY;
8381           TEST_DrawLevelField(newx, newy);
8382         }
8383
8384         PlayLevelSound(x, y, SND_PIG_DIGGING);
8385       }
8386       else if (!IS_FREE(newx, newy))
8387       {
8388         if (IS_PLAYER(x, y))
8389           DrawPlayerField(x, y);
8390         else
8391           TEST_DrawLevelField(x, y);
8392
8393         return;
8394       }
8395     }
8396     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8397     {
8398       if (Store[x][y] != EL_EMPTY)
8399       {
8400         boolean can_clone = FALSE;
8401         int xx, yy;
8402
8403         // check if element to clone is still there
8404         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8405         {
8406           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8407           {
8408             can_clone = TRUE;
8409
8410             break;
8411           }
8412         }
8413
8414         // cannot clone or target field not free anymore -- do not clone
8415         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8416           Store[x][y] = EL_EMPTY;
8417       }
8418
8419       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8420       {
8421         if (IS_MV_DIAGONAL(MovDir[x][y]))
8422         {
8423           int diagonal_move_dir = MovDir[x][y];
8424           int stored = Store[x][y];
8425           int change_delay = 8;
8426           int graphic;
8427
8428           // android is moving diagonally
8429
8430           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8431
8432           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8433           GfxElement[x][y] = EL_EMC_ANDROID;
8434           GfxAction[x][y] = ACTION_SHRINKING;
8435           GfxDir[x][y] = diagonal_move_dir;
8436           ChangeDelay[x][y] = change_delay;
8437
8438           if (Store[x][y] == EL_EMPTY)
8439             Store[x][y] = GfxElementEmpty[x][y];
8440
8441           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8442                                    GfxDir[x][y]);
8443
8444           DrawLevelGraphicAnimation(x, y, graphic);
8445           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8446
8447           if (Tile[newx][newy] == EL_ACID)
8448           {
8449             SplashAcid(newx, newy);
8450
8451             return;
8452           }
8453
8454           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8455
8456           Store[newx][newy] = EL_EMC_ANDROID;
8457           GfxElement[newx][newy] = EL_EMC_ANDROID;
8458           GfxAction[newx][newy] = ACTION_GROWING;
8459           GfxDir[newx][newy] = diagonal_move_dir;
8460           ChangeDelay[newx][newy] = change_delay;
8461
8462           graphic = el_act_dir2img(GfxElement[newx][newy],
8463                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8464
8465           DrawLevelGraphicAnimation(newx, newy, graphic);
8466           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8467
8468           return;
8469         }
8470         else
8471         {
8472           Tile[newx][newy] = EL_EMPTY;
8473           TEST_DrawLevelField(newx, newy);
8474
8475           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8476         }
8477       }
8478       else if (!IS_FREE(newx, newy))
8479       {
8480         return;
8481       }
8482     }
8483     else if (IS_CUSTOM_ELEMENT(element) &&
8484              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8485     {
8486       if (!DigFieldByCE(newx, newy, element))
8487         return;
8488
8489       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8490       {
8491         RunnerVisit[x][y] = FrameCounter;
8492         PlayerVisit[x][y] /= 8;         // expire player visit path
8493       }
8494     }
8495     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8496     {
8497       if (!IS_FREE(newx, newy))
8498       {
8499         if (IS_PLAYER(x, y))
8500           DrawPlayerField(x, y);
8501         else
8502           TEST_DrawLevelField(x, y);
8503
8504         return;
8505       }
8506       else
8507       {
8508         boolean wanna_flame = !RND(10);
8509         int dx = newx - x, dy = newy - y;
8510         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8511         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8512         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8513                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8514         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8515                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8516
8517         if ((wanna_flame ||
8518              IS_CLASSIC_ENEMY(element1) ||
8519              IS_CLASSIC_ENEMY(element2)) &&
8520             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8521             element1 != EL_FLAMES && element2 != EL_FLAMES)
8522         {
8523           ResetGfxAnimation(x, y);
8524           GfxAction[x][y] = ACTION_ATTACKING;
8525
8526           if (IS_PLAYER(x, y))
8527             DrawPlayerField(x, y);
8528           else
8529             TEST_DrawLevelField(x, y);
8530
8531           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8532
8533           MovDelay[x][y] = 50;
8534
8535           Tile[newx][newy] = EL_FLAMES;
8536           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8537             Tile[newx1][newy1] = EL_FLAMES;
8538           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8539             Tile[newx2][newy2] = EL_FLAMES;
8540
8541           return;
8542         }
8543       }
8544     }
8545     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8546              Tile[newx][newy] == EL_DIAMOND)
8547     {
8548       if (IS_MOVING(newx, newy))
8549         RemoveMovingField(newx, newy);
8550       else
8551       {
8552         Tile[newx][newy] = EL_EMPTY;
8553         TEST_DrawLevelField(newx, newy);
8554       }
8555
8556       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8557     }
8558     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8559              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8560     {
8561       if (AmoebaNr[newx][newy])
8562       {
8563         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8564         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8565             Tile[newx][newy] == EL_BD_AMOEBA)
8566           AmoebaCnt[AmoebaNr[newx][newy]]--;
8567       }
8568
8569       if (IS_MOVING(newx, newy))
8570       {
8571         RemoveMovingField(newx, newy);
8572       }
8573       else
8574       {
8575         Tile[newx][newy] = EL_EMPTY;
8576         TEST_DrawLevelField(newx, newy);
8577       }
8578
8579       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8580     }
8581     else if ((element == EL_PACMAN || element == EL_MOLE)
8582              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8583     {
8584       if (AmoebaNr[newx][newy])
8585       {
8586         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8587         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8588             Tile[newx][newy] == EL_BD_AMOEBA)
8589           AmoebaCnt[AmoebaNr[newx][newy]]--;
8590       }
8591
8592       if (element == EL_MOLE)
8593       {
8594         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8595         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8596
8597         ResetGfxAnimation(x, y);
8598         GfxAction[x][y] = ACTION_DIGGING;
8599         TEST_DrawLevelField(x, y);
8600
8601         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8602
8603         return;                         // wait for shrinking amoeba
8604       }
8605       else      // element == EL_PACMAN
8606       {
8607         Tile[newx][newy] = EL_EMPTY;
8608         TEST_DrawLevelField(newx, newy);
8609         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8610       }
8611     }
8612     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8613              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8614               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8615     {
8616       // wait for shrinking amoeba to completely disappear
8617       return;
8618     }
8619     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8620     {
8621       // object was running against a wall
8622
8623       TurnRound(x, y);
8624
8625       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8626         DrawLevelElementAnimation(x, y, element);
8627
8628       if (DONT_TOUCH(element))
8629         TestIfBadThingTouchesPlayer(x, y);
8630
8631       return;
8632     }
8633
8634     InitMovingField(x, y, MovDir[x][y]);
8635
8636     PlayLevelSoundAction(x, y, ACTION_MOVING);
8637   }
8638
8639   if (MovDir[x][y])
8640     ContinueMoving(x, y);
8641 }
8642
8643 void ContinueMoving(int x, int y)
8644 {
8645   int element = Tile[x][y];
8646   struct ElementInfo *ei = &element_info[element];
8647   int direction = MovDir[x][y];
8648   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8649   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8650   int newx = x + dx, newy = y + dy;
8651   int stored = Store[x][y];
8652   int stored_new = Store[newx][newy];
8653   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8654   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8655   boolean last_line = (newy == lev_fieldy - 1);
8656   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8657
8658   if (pushed_by_player)         // special case: moving object pushed by player
8659   {
8660     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8661   }
8662   else if (use_step_delay)      // special case: moving object has step delay
8663   {
8664     if (!MovDelay[x][y])
8665       MovPos[x][y] += getElementMoveStepsize(x, y);
8666
8667     if (MovDelay[x][y])
8668       MovDelay[x][y]--;
8669     else
8670       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8671
8672     if (MovDelay[x][y])
8673     {
8674       TEST_DrawLevelField(x, y);
8675
8676       return;   // element is still waiting
8677     }
8678   }
8679   else                          // normal case: generically moving object
8680   {
8681     MovPos[x][y] += getElementMoveStepsize(x, y);
8682   }
8683
8684   if (ABS(MovPos[x][y]) < TILEX)
8685   {
8686     TEST_DrawLevelField(x, y);
8687
8688     return;     // element is still moving
8689   }
8690
8691   // element reached destination field
8692
8693   Tile[x][y] = EL_EMPTY;
8694   Tile[newx][newy] = element;
8695   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8696
8697   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8698   {
8699     element = Tile[newx][newy] = EL_ACID;
8700   }
8701   else if (element == EL_MOLE)
8702   {
8703     Tile[x][y] = EL_SAND;
8704
8705     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8706   }
8707   else if (element == EL_QUICKSAND_FILLING)
8708   {
8709     element = Tile[newx][newy] = get_next_element(element);
8710     Store[newx][newy] = Store[x][y];
8711   }
8712   else if (element == EL_QUICKSAND_EMPTYING)
8713   {
8714     Tile[x][y] = get_next_element(element);
8715     element = Tile[newx][newy] = Store[x][y];
8716   }
8717   else if (element == EL_QUICKSAND_FAST_FILLING)
8718   {
8719     element = Tile[newx][newy] = get_next_element(element);
8720     Store[newx][newy] = Store[x][y];
8721   }
8722   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8723   {
8724     Tile[x][y] = get_next_element(element);
8725     element = Tile[newx][newy] = Store[x][y];
8726   }
8727   else if (element == EL_MAGIC_WALL_FILLING)
8728   {
8729     element = Tile[newx][newy] = get_next_element(element);
8730     if (!game.magic_wall_active)
8731       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8732     Store[newx][newy] = Store[x][y];
8733   }
8734   else if (element == EL_MAGIC_WALL_EMPTYING)
8735   {
8736     Tile[x][y] = get_next_element(element);
8737     if (!game.magic_wall_active)
8738       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8739     element = Tile[newx][newy] = Store[x][y];
8740
8741     InitField(newx, newy, FALSE);
8742   }
8743   else if (element == EL_BD_MAGIC_WALL_FILLING)
8744   {
8745     element = Tile[newx][newy] = get_next_element(element);
8746     if (!game.magic_wall_active)
8747       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8748     Store[newx][newy] = Store[x][y];
8749   }
8750   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8751   {
8752     Tile[x][y] = get_next_element(element);
8753     if (!game.magic_wall_active)
8754       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8755     element = Tile[newx][newy] = Store[x][y];
8756
8757     InitField(newx, newy, FALSE);
8758   }
8759   else if (element == EL_DC_MAGIC_WALL_FILLING)
8760   {
8761     element = Tile[newx][newy] = get_next_element(element);
8762     if (!game.magic_wall_active)
8763       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8764     Store[newx][newy] = Store[x][y];
8765   }
8766   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8767   {
8768     Tile[x][y] = get_next_element(element);
8769     if (!game.magic_wall_active)
8770       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8771     element = Tile[newx][newy] = Store[x][y];
8772
8773     InitField(newx, newy, FALSE);
8774   }
8775   else if (element == EL_AMOEBA_DROPPING)
8776   {
8777     Tile[x][y] = get_next_element(element);
8778     element = Tile[newx][newy] = Store[x][y];
8779   }
8780   else if (element == EL_SOKOBAN_OBJECT)
8781   {
8782     if (Back[x][y])
8783       Tile[x][y] = Back[x][y];
8784
8785     if (Back[newx][newy])
8786       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8787
8788     Back[x][y] = Back[newx][newy] = 0;
8789   }
8790
8791   Store[x][y] = EL_EMPTY;
8792   MovPos[x][y] = 0;
8793   MovDir[x][y] = 0;
8794   MovDelay[x][y] = 0;
8795
8796   MovDelay[newx][newy] = 0;
8797
8798   if (CAN_CHANGE_OR_HAS_ACTION(element))
8799   {
8800     // copy element change control values to new field
8801     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8802     ChangePage[newx][newy]  = ChangePage[x][y];
8803     ChangeCount[newx][newy] = ChangeCount[x][y];
8804     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8805   }
8806
8807   CustomValue[newx][newy] = CustomValue[x][y];
8808
8809   ChangeDelay[x][y] = 0;
8810   ChangePage[x][y] = -1;
8811   ChangeCount[x][y] = 0;
8812   ChangeEvent[x][y] = -1;
8813
8814   CustomValue[x][y] = 0;
8815
8816   // copy animation control values to new field
8817   GfxFrame[newx][newy]  = GfxFrame[x][y];
8818   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8819   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8820   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8821
8822   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8823
8824   // some elements can leave other elements behind after moving
8825   if (ei->move_leave_element != EL_EMPTY &&
8826       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8827       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8828   {
8829     int move_leave_element = ei->move_leave_element;
8830
8831     // this makes it possible to leave the removed element again
8832     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8833       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8834
8835     Tile[x][y] = move_leave_element;
8836
8837     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8838       MovDir[x][y] = direction;
8839
8840     InitField(x, y, FALSE);
8841
8842     if (GFX_CRUMBLED(Tile[x][y]))
8843       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8844
8845     if (IS_PLAYER_ELEMENT(move_leave_element))
8846       RelocatePlayer(x, y, move_leave_element);
8847   }
8848
8849   // do this after checking for left-behind element
8850   ResetGfxAnimation(x, y);      // reset animation values for old field
8851
8852   if (!CAN_MOVE(element) ||
8853       (CAN_FALL(element) && direction == MV_DOWN &&
8854        (element == EL_SPRING ||
8855         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8856         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8857     GfxDir[x][y] = MovDir[newx][newy] = 0;
8858
8859   TEST_DrawLevelField(x, y);
8860   TEST_DrawLevelField(newx, newy);
8861
8862   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8863
8864   // prevent pushed element from moving on in pushed direction
8865   if (pushed_by_player && CAN_MOVE(element) &&
8866       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8867       !(element_info[element].move_pattern & direction))
8868     TurnRound(newx, newy);
8869
8870   // prevent elements on conveyor belt from moving on in last direction
8871   if (pushed_by_conveyor && CAN_FALL(element) &&
8872       direction & MV_HORIZONTAL)
8873     MovDir[newx][newy] = 0;
8874
8875   if (!pushed_by_player)
8876   {
8877     int nextx = newx + dx, nexty = newy + dy;
8878     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8879
8880     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8881
8882     if (CAN_FALL(element) && direction == MV_DOWN)
8883       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8884
8885     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8886       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8887
8888     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8889       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8890   }
8891
8892   if (DONT_TOUCH(element))      // object may be nasty to player or others
8893   {
8894     TestIfBadThingTouchesPlayer(newx, newy);
8895     TestIfBadThingTouchesFriend(newx, newy);
8896
8897     if (!IS_CUSTOM_ELEMENT(element))
8898       TestIfBadThingTouchesOtherBadThing(newx, newy);
8899   }
8900   else if (element == EL_PENGUIN)
8901     TestIfFriendTouchesBadThing(newx, newy);
8902
8903   if (DONT_GET_HIT_BY(element))
8904   {
8905     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8906   }
8907
8908   // give the player one last chance (one more frame) to move away
8909   if (CAN_FALL(element) && direction == MV_DOWN &&
8910       (last_line || (!IS_FREE(x, newy + 1) &&
8911                      (!IS_PLAYER(x, newy + 1) ||
8912                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8913     Impact(x, newy);
8914
8915   if (pushed_by_player && !game.use_change_when_pushing_bug)
8916   {
8917     int push_side = MV_DIR_OPPOSITE(direction);
8918     struct PlayerInfo *player = PLAYERINFO(x, y);
8919
8920     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8921                                player->index_bit, push_side);
8922     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8923                                         player->index_bit, push_side);
8924   }
8925
8926   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8927     MovDelay[newx][newy] = 1;
8928
8929   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8930
8931   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8932   TestIfElementHitsCustomElement(newx, newy, direction);
8933   TestIfPlayerTouchesCustomElement(newx, newy);
8934   TestIfElementTouchesCustomElement(newx, newy);
8935
8936   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8937       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8938     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8939                              MV_DIR_OPPOSITE(direction));
8940 }
8941
8942 int AmoebaNeighbourNr(int ax, int ay)
8943 {
8944   int i;
8945   int element = Tile[ax][ay];
8946   int group_nr = 0;
8947   static int xy[4][2] =
8948   {
8949     { 0, -1 },
8950     { -1, 0 },
8951     { +1, 0 },
8952     { 0, +1 }
8953   };
8954
8955   for (i = 0; i < NUM_DIRECTIONS; i++)
8956   {
8957     int x = ax + xy[i][0];
8958     int y = ay + xy[i][1];
8959
8960     if (!IN_LEV_FIELD(x, y))
8961       continue;
8962
8963     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8964       group_nr = AmoebaNr[x][y];
8965   }
8966
8967   return group_nr;
8968 }
8969
8970 static void AmoebaMerge(int ax, int ay)
8971 {
8972   int i, x, y, xx, yy;
8973   int new_group_nr = AmoebaNr[ax][ay];
8974   static int xy[4][2] =
8975   {
8976     { 0, -1 },
8977     { -1, 0 },
8978     { +1, 0 },
8979     { 0, +1 }
8980   };
8981
8982   if (new_group_nr == 0)
8983     return;
8984
8985   for (i = 0; i < NUM_DIRECTIONS; i++)
8986   {
8987     x = ax + xy[i][0];
8988     y = ay + xy[i][1];
8989
8990     if (!IN_LEV_FIELD(x, y))
8991       continue;
8992
8993     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8994          Tile[x][y] == EL_BD_AMOEBA ||
8995          Tile[x][y] == EL_AMOEBA_DEAD) &&
8996         AmoebaNr[x][y] != new_group_nr)
8997     {
8998       int old_group_nr = AmoebaNr[x][y];
8999
9000       if (old_group_nr == 0)
9001         return;
9002
9003       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9004       AmoebaCnt[old_group_nr] = 0;
9005       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9006       AmoebaCnt2[old_group_nr] = 0;
9007
9008       SCAN_PLAYFIELD(xx, yy)
9009       {
9010         if (AmoebaNr[xx][yy] == old_group_nr)
9011           AmoebaNr[xx][yy] = new_group_nr;
9012       }
9013     }
9014   }
9015 }
9016
9017 void AmoebaToDiamond(int ax, int ay)
9018 {
9019   int i, x, y;
9020
9021   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9022   {
9023     int group_nr = AmoebaNr[ax][ay];
9024
9025 #ifdef DEBUG
9026     if (group_nr == 0)
9027     {
9028       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9029       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9030
9031       return;
9032     }
9033 #endif
9034
9035     SCAN_PLAYFIELD(x, y)
9036     {
9037       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9038       {
9039         AmoebaNr[x][y] = 0;
9040         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9041       }
9042     }
9043
9044     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9045                             SND_AMOEBA_TURNING_TO_GEM :
9046                             SND_AMOEBA_TURNING_TO_ROCK));
9047     Bang(ax, ay);
9048   }
9049   else
9050   {
9051     static int xy[4][2] =
9052     {
9053       { 0, -1 },
9054       { -1, 0 },
9055       { +1, 0 },
9056       { 0, +1 }
9057     };
9058
9059     for (i = 0; i < NUM_DIRECTIONS; i++)
9060     {
9061       x = ax + xy[i][0];
9062       y = ay + xy[i][1];
9063
9064       if (!IN_LEV_FIELD(x, y))
9065         continue;
9066
9067       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9068       {
9069         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9070                               SND_AMOEBA_TURNING_TO_GEM :
9071                               SND_AMOEBA_TURNING_TO_ROCK));
9072         Bang(x, y);
9073       }
9074     }
9075   }
9076 }
9077
9078 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9079 {
9080   int x, y;
9081   int group_nr = AmoebaNr[ax][ay];
9082   boolean done = FALSE;
9083
9084 #ifdef DEBUG
9085   if (group_nr == 0)
9086   {
9087     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9088     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9089
9090     return;
9091   }
9092 #endif
9093
9094   SCAN_PLAYFIELD(x, y)
9095   {
9096     if (AmoebaNr[x][y] == group_nr &&
9097         (Tile[x][y] == EL_AMOEBA_DEAD ||
9098          Tile[x][y] == EL_BD_AMOEBA ||
9099          Tile[x][y] == EL_AMOEBA_GROWING))
9100     {
9101       AmoebaNr[x][y] = 0;
9102       Tile[x][y] = new_element;
9103       InitField(x, y, FALSE);
9104       TEST_DrawLevelField(x, y);
9105       done = TRUE;
9106     }
9107   }
9108
9109   if (done)
9110     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9111                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9112                             SND_BD_AMOEBA_TURNING_TO_GEM));
9113 }
9114
9115 static void AmoebaGrowing(int x, int y)
9116 {
9117   static unsigned int sound_delay = 0;
9118   static unsigned int sound_delay_value = 0;
9119
9120   if (!MovDelay[x][y])          // start new growing cycle
9121   {
9122     MovDelay[x][y] = 7;
9123
9124     if (DelayReached(&sound_delay, sound_delay_value))
9125     {
9126       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9127       sound_delay_value = 30;
9128     }
9129   }
9130
9131   if (MovDelay[x][y])           // wait some time before growing bigger
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_GROWING,
9137                                            6 - MovDelay[x][y]);
9138
9139       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9140     }
9141
9142     if (!MovDelay[x][y])
9143     {
9144       Tile[x][y] = Store[x][y];
9145       Store[x][y] = 0;
9146       TEST_DrawLevelField(x, y);
9147     }
9148   }
9149 }
9150
9151 static void AmoebaShrinking(int x, int y)
9152 {
9153   static unsigned int sound_delay = 0;
9154   static unsigned int sound_delay_value = 0;
9155
9156   if (!MovDelay[x][y])          // start new shrinking cycle
9157   {
9158     MovDelay[x][y] = 7;
9159
9160     if (DelayReached(&sound_delay, sound_delay_value))
9161       sound_delay_value = 30;
9162   }
9163
9164   if (MovDelay[x][y])           // wait some time before shrinking
9165   {
9166     MovDelay[x][y]--;
9167     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9168     {
9169       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9170                                            6 - MovDelay[x][y]);
9171
9172       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9173     }
9174
9175     if (!MovDelay[x][y])
9176     {
9177       Tile[x][y] = EL_EMPTY;
9178       TEST_DrawLevelField(x, y);
9179
9180       // don't let mole enter this field in this cycle;
9181       // (give priority to objects falling to this field from above)
9182       Stop[x][y] = TRUE;
9183     }
9184   }
9185 }
9186
9187 static void AmoebaReproduce(int ax, int ay)
9188 {
9189   int i;
9190   int element = Tile[ax][ay];
9191   int graphic = el2img(element);
9192   int newax = ax, neway = ay;
9193   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9194   static int xy[4][2] =
9195   {
9196     { 0, -1 },
9197     { -1, 0 },
9198     { +1, 0 },
9199     { 0, +1 }
9200   };
9201
9202   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9203   {
9204     Tile[ax][ay] = EL_AMOEBA_DEAD;
9205     TEST_DrawLevelField(ax, ay);
9206     return;
9207   }
9208
9209   if (IS_ANIMATED(graphic))
9210     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9211
9212   if (!MovDelay[ax][ay])        // start making new amoeba field
9213     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9214
9215   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9216   {
9217     MovDelay[ax][ay]--;
9218     if (MovDelay[ax][ay])
9219       return;
9220   }
9221
9222   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9223   {
9224     int start = RND(4);
9225     int x = ax + xy[start][0];
9226     int y = ay + xy[start][1];
9227
9228     if (!IN_LEV_FIELD(x, y))
9229       return;
9230
9231     if (IS_FREE(x, y) ||
9232         CAN_GROW_INTO(Tile[x][y]) ||
9233         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9234         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9235     {
9236       newax = x;
9237       neway = y;
9238     }
9239
9240     if (newax == ax && neway == ay)
9241       return;
9242   }
9243   else                          // normal or "filled" (BD style) amoeba
9244   {
9245     int start = RND(4);
9246     boolean waiting_for_player = FALSE;
9247
9248     for (i = 0; i < NUM_DIRECTIONS; i++)
9249     {
9250       int j = (start + i) % 4;
9251       int x = ax + xy[j][0];
9252       int y = ay + xy[j][1];
9253
9254       if (!IN_LEV_FIELD(x, y))
9255         continue;
9256
9257       if (IS_FREE(x, y) ||
9258           CAN_GROW_INTO(Tile[x][y]) ||
9259           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9260           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9261       {
9262         newax = x;
9263         neway = y;
9264         break;
9265       }
9266       else if (IS_PLAYER(x, y))
9267         waiting_for_player = TRUE;
9268     }
9269
9270     if (newax == ax && neway == ay)             // amoeba cannot grow
9271     {
9272       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9273       {
9274         Tile[ax][ay] = EL_AMOEBA_DEAD;
9275         TEST_DrawLevelField(ax, ay);
9276         AmoebaCnt[AmoebaNr[ax][ay]]--;
9277
9278         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9279         {
9280           if (element == EL_AMOEBA_FULL)
9281             AmoebaToDiamond(ax, ay);
9282           else if (element == EL_BD_AMOEBA)
9283             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9284         }
9285       }
9286       return;
9287     }
9288     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9289     {
9290       // amoeba gets larger by growing in some direction
9291
9292       int new_group_nr = AmoebaNr[ax][ay];
9293
9294 #ifdef DEBUG
9295   if (new_group_nr == 0)
9296   {
9297     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9298           newax, neway);
9299     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9300
9301     return;
9302   }
9303 #endif
9304
9305       AmoebaNr[newax][neway] = new_group_nr;
9306       AmoebaCnt[new_group_nr]++;
9307       AmoebaCnt2[new_group_nr]++;
9308
9309       // if amoeba touches other amoeba(s) after growing, unify them
9310       AmoebaMerge(newax, neway);
9311
9312       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9313       {
9314         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9315         return;
9316       }
9317     }
9318   }
9319
9320   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9321       (neway == lev_fieldy - 1 && newax != ax))
9322   {
9323     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9324     Store[newax][neway] = element;
9325   }
9326   else if (neway == ay || element == EL_EMC_DRIPPER)
9327   {
9328     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9329
9330     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9331   }
9332   else
9333   {
9334     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9335     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9336     Store[ax][ay] = EL_AMOEBA_DROP;
9337     ContinueMoving(ax, ay);
9338     return;
9339   }
9340
9341   TEST_DrawLevelField(newax, neway);
9342 }
9343
9344 static void Life(int ax, int ay)
9345 {
9346   int x1, y1, x2, y2;
9347   int life_time = 40;
9348   int element = Tile[ax][ay];
9349   int graphic = el2img(element);
9350   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9351                          level.biomaze);
9352   boolean changed = FALSE;
9353
9354   if (IS_ANIMATED(graphic))
9355     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9356
9357   if (Stop[ax][ay])
9358     return;
9359
9360   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9361     MovDelay[ax][ay] = life_time;
9362
9363   if (MovDelay[ax][ay])         // wait some time before next cycle
9364   {
9365     MovDelay[ax][ay]--;
9366     if (MovDelay[ax][ay])
9367       return;
9368   }
9369
9370   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9371   {
9372     int xx = ax+x1, yy = ay+y1;
9373     int old_element = Tile[xx][yy];
9374     int num_neighbours = 0;
9375
9376     if (!IN_LEV_FIELD(xx, yy))
9377       continue;
9378
9379     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9380     {
9381       int x = xx+x2, y = yy+y2;
9382
9383       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9384         continue;
9385
9386       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9387       boolean is_neighbour = FALSE;
9388
9389       if (level.use_life_bugs)
9390         is_neighbour =
9391           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9392            (IS_FREE(x, y)                             &&  Stop[x][y]));
9393       else
9394         is_neighbour =
9395           (Last[x][y] == element || is_player_cell);
9396
9397       if (is_neighbour)
9398         num_neighbours++;
9399     }
9400
9401     boolean is_free = FALSE;
9402
9403     if (level.use_life_bugs)
9404       is_free = (IS_FREE(xx, yy));
9405     else
9406       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9407
9408     if (xx == ax && yy == ay)           // field in the middle
9409     {
9410       if (num_neighbours < life_parameter[0] ||
9411           num_neighbours > life_parameter[1])
9412       {
9413         Tile[xx][yy] = EL_EMPTY;
9414         if (Tile[xx][yy] != old_element)
9415           TEST_DrawLevelField(xx, yy);
9416         Stop[xx][yy] = TRUE;
9417         changed = TRUE;
9418       }
9419     }
9420     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9421     {                                   // free border field
9422       if (num_neighbours >= life_parameter[2] &&
9423           num_neighbours <= life_parameter[3])
9424       {
9425         Tile[xx][yy] = element;
9426         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9427         if (Tile[xx][yy] != old_element)
9428           TEST_DrawLevelField(xx, yy);
9429         Stop[xx][yy] = TRUE;
9430         changed = TRUE;
9431       }
9432     }
9433   }
9434
9435   if (changed)
9436     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9437                    SND_GAME_OF_LIFE_GROWING);
9438 }
9439
9440 static void InitRobotWheel(int x, int y)
9441 {
9442   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9443 }
9444
9445 static void RunRobotWheel(int x, int y)
9446 {
9447   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9448 }
9449
9450 static void StopRobotWheel(int x, int y)
9451 {
9452   if (game.robot_wheel_x == x &&
9453       game.robot_wheel_y == y)
9454   {
9455     game.robot_wheel_x = -1;
9456     game.robot_wheel_y = -1;
9457     game.robot_wheel_active = FALSE;
9458   }
9459 }
9460
9461 static void InitTimegateWheel(int x, int y)
9462 {
9463   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9464 }
9465
9466 static void RunTimegateWheel(int x, int y)
9467 {
9468   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9469 }
9470
9471 static void InitMagicBallDelay(int x, int y)
9472 {
9473   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9474 }
9475
9476 static void ActivateMagicBall(int bx, int by)
9477 {
9478   int x, y;
9479
9480   if (level.ball_random)
9481   {
9482     int pos_border = RND(8);    // select one of the eight border elements
9483     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9484     int xx = pos_content % 3;
9485     int yy = pos_content / 3;
9486
9487     x = bx - 1 + xx;
9488     y = by - 1 + yy;
9489
9490     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9491       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9492   }
9493   else
9494   {
9495     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9496     {
9497       int xx = x - bx + 1;
9498       int yy = y - by + 1;
9499
9500       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9501         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9502     }
9503   }
9504
9505   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9506 }
9507
9508 static void CheckExit(int x, int y)
9509 {
9510   if (game.gems_still_needed > 0 ||
9511       game.sokoban_fields_still_needed > 0 ||
9512       game.sokoban_objects_still_needed > 0 ||
9513       game.lights_still_needed > 0)
9514   {
9515     int element = Tile[x][y];
9516     int graphic = el2img(element);
9517
9518     if (IS_ANIMATED(graphic))
9519       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9520
9521     return;
9522   }
9523
9524   // do not re-open exit door closed after last player
9525   if (game.all_players_gone)
9526     return;
9527
9528   Tile[x][y] = EL_EXIT_OPENING;
9529
9530   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9531 }
9532
9533 static void CheckExitEM(int x, int y)
9534 {
9535   if (game.gems_still_needed > 0 ||
9536       game.sokoban_fields_still_needed > 0 ||
9537       game.sokoban_objects_still_needed > 0 ||
9538       game.lights_still_needed > 0)
9539   {
9540     int element = Tile[x][y];
9541     int graphic = el2img(element);
9542
9543     if (IS_ANIMATED(graphic))
9544       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9545
9546     return;
9547   }
9548
9549   // do not re-open exit door closed after last player
9550   if (game.all_players_gone)
9551     return;
9552
9553   Tile[x][y] = EL_EM_EXIT_OPENING;
9554
9555   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9556 }
9557
9558 static void CheckExitSteel(int x, int y)
9559 {
9560   if (game.gems_still_needed > 0 ||
9561       game.sokoban_fields_still_needed > 0 ||
9562       game.sokoban_objects_still_needed > 0 ||
9563       game.lights_still_needed > 0)
9564   {
9565     int element = Tile[x][y];
9566     int graphic = el2img(element);
9567
9568     if (IS_ANIMATED(graphic))
9569       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9570
9571     return;
9572   }
9573
9574   // do not re-open exit door closed after last player
9575   if (game.all_players_gone)
9576     return;
9577
9578   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9579
9580   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9581 }
9582
9583 static void CheckExitSteelEM(int x, int y)
9584 {
9585   if (game.gems_still_needed > 0 ||
9586       game.sokoban_fields_still_needed > 0 ||
9587       game.sokoban_objects_still_needed > 0 ||
9588       game.lights_still_needed > 0)
9589   {
9590     int element = Tile[x][y];
9591     int graphic = el2img(element);
9592
9593     if (IS_ANIMATED(graphic))
9594       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9595
9596     return;
9597   }
9598
9599   // do not re-open exit door closed after last player
9600   if (game.all_players_gone)
9601     return;
9602
9603   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9604
9605   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9606 }
9607
9608 static void CheckExitSP(int x, int y)
9609 {
9610   if (game.gems_still_needed > 0)
9611   {
9612     int element = Tile[x][y];
9613     int graphic = el2img(element);
9614
9615     if (IS_ANIMATED(graphic))
9616       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9617
9618     return;
9619   }
9620
9621   // do not re-open exit door closed after last player
9622   if (game.all_players_gone)
9623     return;
9624
9625   Tile[x][y] = EL_SP_EXIT_OPENING;
9626
9627   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9628 }
9629
9630 static void CloseAllOpenTimegates(void)
9631 {
9632   int x, y;
9633
9634   SCAN_PLAYFIELD(x, y)
9635   {
9636     int element = Tile[x][y];
9637
9638     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9639     {
9640       Tile[x][y] = EL_TIMEGATE_CLOSING;
9641
9642       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9643     }
9644   }
9645 }
9646
9647 static void DrawTwinkleOnField(int x, int y)
9648 {
9649   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9650     return;
9651
9652   if (Tile[x][y] == EL_BD_DIAMOND)
9653     return;
9654
9655   if (MovDelay[x][y] == 0)      // next animation frame
9656     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9657
9658   if (MovDelay[x][y] != 0)      // wait some time before next frame
9659   {
9660     MovDelay[x][y]--;
9661
9662     DrawLevelElementAnimation(x, y, Tile[x][y]);
9663
9664     if (MovDelay[x][y] != 0)
9665     {
9666       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9667                                            10 - MovDelay[x][y]);
9668
9669       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9670     }
9671   }
9672 }
9673
9674 static void MauerWaechst(int x, int y)
9675 {
9676   int delay = 6;
9677
9678   if (!MovDelay[x][y])          // next animation frame
9679     MovDelay[x][y] = 3 * delay;
9680
9681   if (MovDelay[x][y])           // wait some time before next frame
9682   {
9683     MovDelay[x][y]--;
9684
9685     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9686     {
9687       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9688       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9689
9690       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9691     }
9692
9693     if (!MovDelay[x][y])
9694     {
9695       if (MovDir[x][y] == MV_LEFT)
9696       {
9697         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9698           TEST_DrawLevelField(x - 1, y);
9699       }
9700       else if (MovDir[x][y] == MV_RIGHT)
9701       {
9702         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9703           TEST_DrawLevelField(x + 1, y);
9704       }
9705       else if (MovDir[x][y] == MV_UP)
9706       {
9707         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9708           TEST_DrawLevelField(x, y - 1);
9709       }
9710       else
9711       {
9712         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9713           TEST_DrawLevelField(x, y + 1);
9714       }
9715
9716       Tile[x][y] = Store[x][y];
9717       Store[x][y] = 0;
9718       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9719       TEST_DrawLevelField(x, y);
9720     }
9721   }
9722 }
9723
9724 static void MauerAbleger(int ax, int ay)
9725 {
9726   int element = Tile[ax][ay];
9727   int graphic = el2img(element);
9728   boolean oben_frei = FALSE, unten_frei = FALSE;
9729   boolean links_frei = FALSE, rechts_frei = FALSE;
9730   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9731   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9732   boolean new_wall = FALSE;
9733
9734   if (IS_ANIMATED(graphic))
9735     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9736
9737   if (!MovDelay[ax][ay])        // start building new wall
9738     MovDelay[ax][ay] = 6;
9739
9740   if (MovDelay[ax][ay])         // wait some time before building new wall
9741   {
9742     MovDelay[ax][ay]--;
9743     if (MovDelay[ax][ay])
9744       return;
9745   }
9746
9747   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9748     oben_frei = TRUE;
9749   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9750     unten_frei = TRUE;
9751   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9752     links_frei = TRUE;
9753   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9754     rechts_frei = TRUE;
9755
9756   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9757       element == EL_EXPANDABLE_WALL_ANY)
9758   {
9759     if (oben_frei)
9760     {
9761       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9762       Store[ax][ay-1] = element;
9763       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9764       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9765         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9766                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9767       new_wall = TRUE;
9768     }
9769     if (unten_frei)
9770     {
9771       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9772       Store[ax][ay+1] = element;
9773       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9774       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9775         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9776                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9777       new_wall = TRUE;
9778     }
9779   }
9780
9781   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9782       element == EL_EXPANDABLE_WALL_ANY ||
9783       element == EL_EXPANDABLE_WALL ||
9784       element == EL_BD_EXPANDABLE_WALL)
9785   {
9786     if (links_frei)
9787     {
9788       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9789       Store[ax-1][ay] = element;
9790       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9791       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9792         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9793                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9794       new_wall = TRUE;
9795     }
9796
9797     if (rechts_frei)
9798     {
9799       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9800       Store[ax+1][ay] = element;
9801       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9802       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9803         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9804                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9805       new_wall = TRUE;
9806     }
9807   }
9808
9809   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9810     TEST_DrawLevelField(ax, ay);
9811
9812   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9813     oben_massiv = TRUE;
9814   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9815     unten_massiv = TRUE;
9816   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9817     links_massiv = TRUE;
9818   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9819     rechts_massiv = TRUE;
9820
9821   if (((oben_massiv && unten_massiv) ||
9822        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9823        element == EL_EXPANDABLE_WALL) &&
9824       ((links_massiv && rechts_massiv) ||
9825        element == EL_EXPANDABLE_WALL_VERTICAL))
9826     Tile[ax][ay] = EL_WALL;
9827
9828   if (new_wall)
9829     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9830 }
9831
9832 static void MauerAblegerStahl(int ax, int ay)
9833 {
9834   int element = Tile[ax][ay];
9835   int graphic = el2img(element);
9836   boolean oben_frei = FALSE, unten_frei = FALSE;
9837   boolean links_frei = FALSE, rechts_frei = FALSE;
9838   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9839   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9840   boolean new_wall = FALSE;
9841
9842   if (IS_ANIMATED(graphic))
9843     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9844
9845   if (!MovDelay[ax][ay])        // start building new wall
9846     MovDelay[ax][ay] = 6;
9847
9848   if (MovDelay[ax][ay])         // wait some time before building new wall
9849   {
9850     MovDelay[ax][ay]--;
9851     if (MovDelay[ax][ay])
9852       return;
9853   }
9854
9855   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9856     oben_frei = TRUE;
9857   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9858     unten_frei = TRUE;
9859   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9860     links_frei = TRUE;
9861   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9862     rechts_frei = TRUE;
9863
9864   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9865       element == EL_EXPANDABLE_STEELWALL_ANY)
9866   {
9867     if (oben_frei)
9868     {
9869       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9870       Store[ax][ay-1] = element;
9871       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9872       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9873         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9874                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9875       new_wall = TRUE;
9876     }
9877     if (unten_frei)
9878     {
9879       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9880       Store[ax][ay+1] = element;
9881       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9882       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9883         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9884                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9885       new_wall = TRUE;
9886     }
9887   }
9888
9889   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9890       element == EL_EXPANDABLE_STEELWALL_ANY)
9891   {
9892     if (links_frei)
9893     {
9894       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9895       Store[ax-1][ay] = element;
9896       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9897       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9898         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9899                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9900       new_wall = TRUE;
9901     }
9902
9903     if (rechts_frei)
9904     {
9905       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9906       Store[ax+1][ay] = element;
9907       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9908       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9909         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9910                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9911       new_wall = TRUE;
9912     }
9913   }
9914
9915   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9916     oben_massiv = TRUE;
9917   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9918     unten_massiv = TRUE;
9919   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9920     links_massiv = TRUE;
9921   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9922     rechts_massiv = TRUE;
9923
9924   if (((oben_massiv && unten_massiv) ||
9925        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9926       ((links_massiv && rechts_massiv) ||
9927        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9928     Tile[ax][ay] = EL_STEELWALL;
9929
9930   if (new_wall)
9931     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9932 }
9933
9934 static void CheckForDragon(int x, int y)
9935 {
9936   int i, j;
9937   boolean dragon_found = FALSE;
9938   static int xy[4][2] =
9939   {
9940     { 0, -1 },
9941     { -1, 0 },
9942     { +1, 0 },
9943     { 0, +1 }
9944   };
9945
9946   for (i = 0; i < NUM_DIRECTIONS; i++)
9947   {
9948     for (j = 0; j < 4; j++)
9949     {
9950       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9951
9952       if (IN_LEV_FIELD(xx, yy) &&
9953           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9954       {
9955         if (Tile[xx][yy] == EL_DRAGON)
9956           dragon_found = TRUE;
9957       }
9958       else
9959         break;
9960     }
9961   }
9962
9963   if (!dragon_found)
9964   {
9965     for (i = 0; i < NUM_DIRECTIONS; i++)
9966     {
9967       for (j = 0; j < 3; j++)
9968       {
9969         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9970   
9971         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9972         {
9973           Tile[xx][yy] = EL_EMPTY;
9974           TEST_DrawLevelField(xx, yy);
9975         }
9976         else
9977           break;
9978       }
9979     }
9980   }
9981 }
9982
9983 static void InitBuggyBase(int x, int y)
9984 {
9985   int element = Tile[x][y];
9986   int activating_delay = FRAMES_PER_SECOND / 4;
9987
9988   ChangeDelay[x][y] =
9989     (element == EL_SP_BUGGY_BASE ?
9990      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9991      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9992      activating_delay :
9993      element == EL_SP_BUGGY_BASE_ACTIVE ?
9994      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9995 }
9996
9997 static void WarnBuggyBase(int x, int y)
9998 {
9999   int i;
10000   static int xy[4][2] =
10001   {
10002     { 0, -1 },
10003     { -1, 0 },
10004     { +1, 0 },
10005     { 0, +1 }
10006   };
10007
10008   for (i = 0; i < NUM_DIRECTIONS; i++)
10009   {
10010     int xx = x + xy[i][0];
10011     int yy = y + xy[i][1];
10012
10013     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10014     {
10015       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10016
10017       break;
10018     }
10019   }
10020 }
10021
10022 static void InitTrap(int x, int y)
10023 {
10024   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10025 }
10026
10027 static void ActivateTrap(int x, int y)
10028 {
10029   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10030 }
10031
10032 static void ChangeActiveTrap(int x, int y)
10033 {
10034   int graphic = IMG_TRAP_ACTIVE;
10035
10036   // if new animation frame was drawn, correct crumbled sand border
10037   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10038     TEST_DrawLevelFieldCrumbled(x, y);
10039 }
10040
10041 static int getSpecialActionElement(int element, int number, int base_element)
10042 {
10043   return (element != EL_EMPTY ? element :
10044           number != -1 ? base_element + number - 1 :
10045           EL_EMPTY);
10046 }
10047
10048 static int getModifiedActionNumber(int value_old, int operator, int operand,
10049                                    int value_min, int value_max)
10050 {
10051   int value_new = (operator == CA_MODE_SET      ? operand :
10052                    operator == CA_MODE_ADD      ? value_old + operand :
10053                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10054                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10055                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10056                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10057                    value_old);
10058
10059   return (value_new < value_min ? value_min :
10060           value_new > value_max ? value_max :
10061           value_new);
10062 }
10063
10064 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10065 {
10066   struct ElementInfo *ei = &element_info[element];
10067   struct ElementChangeInfo *change = &ei->change_page[page];
10068   int target_element = change->target_element;
10069   int action_type = change->action_type;
10070   int action_mode = change->action_mode;
10071   int action_arg = change->action_arg;
10072   int action_element = change->action_element;
10073   int i;
10074
10075   if (!change->has_action)
10076     return;
10077
10078   // ---------- determine action paramater values -----------------------------
10079
10080   int level_time_value =
10081     (level.time > 0 ? TimeLeft :
10082      TimePlayed);
10083
10084   int action_arg_element_raw =
10085     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10086      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10087      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10088      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10089      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10090      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10091      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10092      EL_EMPTY);
10093   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10094
10095   int action_arg_direction =
10096     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10097      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10098      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10099      change->actual_trigger_side :
10100      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10101      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10102      MV_NONE);
10103
10104   int action_arg_number_min =
10105     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10106      CA_ARG_MIN);
10107
10108   int action_arg_number_max =
10109     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10110      action_type == CA_SET_LEVEL_GEMS ? 999 :
10111      action_type == CA_SET_LEVEL_TIME ? 9999 :
10112      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10113      action_type == CA_SET_CE_VALUE ? 9999 :
10114      action_type == CA_SET_CE_SCORE ? 9999 :
10115      CA_ARG_MAX);
10116
10117   int action_arg_number_reset =
10118     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10119      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10120      action_type == CA_SET_LEVEL_TIME ? level.time :
10121      action_type == CA_SET_LEVEL_SCORE ? 0 :
10122      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10123      action_type == CA_SET_CE_SCORE ? 0 :
10124      0);
10125
10126   int action_arg_number =
10127     (action_arg <= CA_ARG_MAX ? action_arg :
10128      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10129      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10130      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10131      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10132      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10133      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10134      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10135      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10136      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10137      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10138      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10139      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10140      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10141      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10142      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10143      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10144      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10145      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10146      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10147      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10148      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10149      -1);
10150
10151   int action_arg_number_old =
10152     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10153      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10154      action_type == CA_SET_LEVEL_SCORE ? game.score :
10155      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10156      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10157      0);
10158
10159   int action_arg_number_new =
10160     getModifiedActionNumber(action_arg_number_old,
10161                             action_mode, action_arg_number,
10162                             action_arg_number_min, action_arg_number_max);
10163
10164   int trigger_player_bits =
10165     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10166      change->actual_trigger_player_bits : change->trigger_player);
10167
10168   int action_arg_player_bits =
10169     (action_arg >= CA_ARG_PLAYER_1 &&
10170      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10171      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10172      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10173      PLAYER_BITS_ANY);
10174
10175   // ---------- execute action  -----------------------------------------------
10176
10177   switch (action_type)
10178   {
10179     case CA_NO_ACTION:
10180     {
10181       return;
10182     }
10183
10184     // ---------- level actions  ----------------------------------------------
10185
10186     case CA_RESTART_LEVEL:
10187     {
10188       game.restart_level = TRUE;
10189
10190       break;
10191     }
10192
10193     case CA_SHOW_ENVELOPE:
10194     {
10195       int element = getSpecialActionElement(action_arg_element,
10196                                             action_arg_number, EL_ENVELOPE_1);
10197
10198       if (IS_ENVELOPE(element))
10199         local_player->show_envelope = element;
10200
10201       break;
10202     }
10203
10204     case CA_SET_LEVEL_TIME:
10205     {
10206       if (level.time > 0)       // only modify limited time value
10207       {
10208         TimeLeft = action_arg_number_new;
10209
10210         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10211
10212         DisplayGameControlValues();
10213
10214         if (!TimeLeft && setup.time_limit)
10215           for (i = 0; i < MAX_PLAYERS; i++)
10216             KillPlayer(&stored_player[i]);
10217       }
10218
10219       break;
10220     }
10221
10222     case CA_SET_LEVEL_SCORE:
10223     {
10224       game.score = action_arg_number_new;
10225
10226       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10227
10228       DisplayGameControlValues();
10229
10230       break;
10231     }
10232
10233     case CA_SET_LEVEL_GEMS:
10234     {
10235       game.gems_still_needed = action_arg_number_new;
10236
10237       game.snapshot.collected_item = TRUE;
10238
10239       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10240
10241       DisplayGameControlValues();
10242
10243       break;
10244     }
10245
10246     case CA_SET_LEVEL_WIND:
10247     {
10248       game.wind_direction = action_arg_direction;
10249
10250       break;
10251     }
10252
10253     case CA_SET_LEVEL_RANDOM_SEED:
10254     {
10255       // ensure that setting a new random seed while playing is predictable
10256       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10257
10258       break;
10259     }
10260
10261     // ---------- player actions  ---------------------------------------------
10262
10263     case CA_MOVE_PLAYER:
10264     case CA_MOVE_PLAYER_NEW:
10265     {
10266       // automatically move to the next field in specified direction
10267       for (i = 0; i < MAX_PLAYERS; i++)
10268         if (trigger_player_bits & (1 << i))
10269           if (action_type == CA_MOVE_PLAYER ||
10270               stored_player[i].MovPos == 0)
10271             stored_player[i].programmed_action = action_arg_direction;
10272
10273       break;
10274     }
10275
10276     case CA_EXIT_PLAYER:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279         if (action_arg_player_bits & (1 << i))
10280           ExitPlayer(&stored_player[i]);
10281
10282       if (game.players_still_needed == 0)
10283         LevelSolved();
10284
10285       break;
10286     }
10287
10288     case CA_KILL_PLAYER:
10289     {
10290       for (i = 0; i < MAX_PLAYERS; i++)
10291         if (action_arg_player_bits & (1 << i))
10292           KillPlayer(&stored_player[i]);
10293
10294       break;
10295     }
10296
10297     case CA_SET_PLAYER_KEYS:
10298     {
10299       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10300       int element = getSpecialActionElement(action_arg_element,
10301                                             action_arg_number, EL_KEY_1);
10302
10303       if (IS_KEY(element))
10304       {
10305         for (i = 0; i < MAX_PLAYERS; i++)
10306         {
10307           if (trigger_player_bits & (1 << i))
10308           {
10309             stored_player[i].key[KEY_NR(element)] = key_state;
10310
10311             DrawGameDoorValues();
10312           }
10313         }
10314       }
10315
10316       break;
10317     }
10318
10319     case CA_SET_PLAYER_SPEED:
10320     {
10321       for (i = 0; i < MAX_PLAYERS; i++)
10322       {
10323         if (trigger_player_bits & (1 << i))
10324         {
10325           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10326
10327           if (action_arg == CA_ARG_SPEED_FASTER &&
10328               stored_player[i].cannot_move)
10329           {
10330             action_arg_number = STEPSIZE_VERY_SLOW;
10331           }
10332           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10333                    action_arg == CA_ARG_SPEED_FASTER)
10334           {
10335             action_arg_number = 2;
10336             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10337                            CA_MODE_MULTIPLY);
10338           }
10339           else if (action_arg == CA_ARG_NUMBER_RESET)
10340           {
10341             action_arg_number = level.initial_player_stepsize[i];
10342           }
10343
10344           move_stepsize =
10345             getModifiedActionNumber(move_stepsize,
10346                                     action_mode,
10347                                     action_arg_number,
10348                                     action_arg_number_min,
10349                                     action_arg_number_max);
10350
10351           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10352         }
10353       }
10354
10355       break;
10356     }
10357
10358     case CA_SET_PLAYER_SHIELD:
10359     {
10360       for (i = 0; i < MAX_PLAYERS; i++)
10361       {
10362         if (trigger_player_bits & (1 << i))
10363         {
10364           if (action_arg == CA_ARG_SHIELD_OFF)
10365           {
10366             stored_player[i].shield_normal_time_left = 0;
10367             stored_player[i].shield_deadly_time_left = 0;
10368           }
10369           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10370           {
10371             stored_player[i].shield_normal_time_left = 999999;
10372           }
10373           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10374           {
10375             stored_player[i].shield_normal_time_left = 999999;
10376             stored_player[i].shield_deadly_time_left = 999999;
10377           }
10378         }
10379       }
10380
10381       break;
10382     }
10383
10384     case CA_SET_PLAYER_GRAVITY:
10385     {
10386       for (i = 0; i < MAX_PLAYERS; i++)
10387       {
10388         if (trigger_player_bits & (1 << i))
10389         {
10390           stored_player[i].gravity =
10391             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10392              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10393              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10394              stored_player[i].gravity);
10395         }
10396       }
10397
10398       break;
10399     }
10400
10401     case CA_SET_PLAYER_ARTWORK:
10402     {
10403       for (i = 0; i < MAX_PLAYERS; i++)
10404       {
10405         if (trigger_player_bits & (1 << i))
10406         {
10407           int artwork_element = action_arg_element;
10408
10409           if (action_arg == CA_ARG_ELEMENT_RESET)
10410             artwork_element =
10411               (level.use_artwork_element[i] ? level.artwork_element[i] :
10412                stored_player[i].element_nr);
10413
10414           if (stored_player[i].artwork_element != artwork_element)
10415             stored_player[i].Frame = 0;
10416
10417           stored_player[i].artwork_element = artwork_element;
10418
10419           SetPlayerWaiting(&stored_player[i], FALSE);
10420
10421           // set number of special actions for bored and sleeping animation
10422           stored_player[i].num_special_action_bored =
10423             get_num_special_action(artwork_element,
10424                                    ACTION_BORING_1, ACTION_BORING_LAST);
10425           stored_player[i].num_special_action_sleeping =
10426             get_num_special_action(artwork_element,
10427                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10428         }
10429       }
10430
10431       break;
10432     }
10433
10434     case CA_SET_PLAYER_INVENTORY:
10435     {
10436       for (i = 0; i < MAX_PLAYERS; i++)
10437       {
10438         struct PlayerInfo *player = &stored_player[i];
10439         int j, k;
10440
10441         if (trigger_player_bits & (1 << i))
10442         {
10443           int inventory_element = action_arg_element;
10444
10445           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10446               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10447               action_arg == CA_ARG_ELEMENT_ACTION)
10448           {
10449             int element = inventory_element;
10450             int collect_count = element_info[element].collect_count_initial;
10451
10452             if (!IS_CUSTOM_ELEMENT(element))
10453               collect_count = 1;
10454
10455             if (collect_count == 0)
10456               player->inventory_infinite_element = element;
10457             else
10458               for (k = 0; k < collect_count; k++)
10459                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10460                   player->inventory_element[player->inventory_size++] =
10461                     element;
10462           }
10463           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10464                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10465                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10466           {
10467             if (player->inventory_infinite_element != EL_UNDEFINED &&
10468                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10469                                      action_arg_element_raw))
10470               player->inventory_infinite_element = EL_UNDEFINED;
10471
10472             for (k = 0, j = 0; j < player->inventory_size; j++)
10473             {
10474               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10475                                         action_arg_element_raw))
10476                 player->inventory_element[k++] = player->inventory_element[j];
10477             }
10478
10479             player->inventory_size = k;
10480           }
10481           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10482           {
10483             if (player->inventory_size > 0)
10484             {
10485               for (j = 0; j < player->inventory_size - 1; j++)
10486                 player->inventory_element[j] = player->inventory_element[j + 1];
10487
10488               player->inventory_size--;
10489             }
10490           }
10491           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10492           {
10493             if (player->inventory_size > 0)
10494               player->inventory_size--;
10495           }
10496           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10497           {
10498             player->inventory_infinite_element = EL_UNDEFINED;
10499             player->inventory_size = 0;
10500           }
10501           else if (action_arg == CA_ARG_INVENTORY_RESET)
10502           {
10503             player->inventory_infinite_element = EL_UNDEFINED;
10504             player->inventory_size = 0;
10505
10506             if (level.use_initial_inventory[i])
10507             {
10508               for (j = 0; j < level.initial_inventory_size[i]; j++)
10509               {
10510                 int element = level.initial_inventory_content[i][j];
10511                 int collect_count = element_info[element].collect_count_initial;
10512
10513                 if (!IS_CUSTOM_ELEMENT(element))
10514                   collect_count = 1;
10515
10516                 if (collect_count == 0)
10517                   player->inventory_infinite_element = element;
10518                 else
10519                   for (k = 0; k < collect_count; k++)
10520                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10521                       player->inventory_element[player->inventory_size++] =
10522                         element;
10523               }
10524             }
10525           }
10526         }
10527       }
10528
10529       break;
10530     }
10531
10532     // ---------- CE actions  -------------------------------------------------
10533
10534     case CA_SET_CE_VALUE:
10535     {
10536       int last_ce_value = CustomValue[x][y];
10537
10538       CustomValue[x][y] = action_arg_number_new;
10539
10540       if (CustomValue[x][y] != last_ce_value)
10541       {
10542         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10543         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10544
10545         if (CustomValue[x][y] == 0)
10546         {
10547           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10548           ChangeCount[x][y] = 0;        // allow at least one more change
10549
10550           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10551           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10552         }
10553       }
10554
10555       break;
10556     }
10557
10558     case CA_SET_CE_SCORE:
10559     {
10560       int last_ce_score = ei->collect_score;
10561
10562       ei->collect_score = action_arg_number_new;
10563
10564       if (ei->collect_score != last_ce_score)
10565       {
10566         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10567         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10568
10569         if (ei->collect_score == 0)
10570         {
10571           int xx, yy;
10572
10573           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10574           ChangeCount[x][y] = 0;        // allow at least one more change
10575
10576           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10577           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10578
10579           /*
10580             This is a very special case that seems to be a mixture between
10581             CheckElementChange() and CheckTriggeredElementChange(): while
10582             the first one only affects single elements that are triggered
10583             directly, the second one affects multiple elements in the playfield
10584             that are triggered indirectly by another element. This is a third
10585             case: Changing the CE score always affects multiple identical CEs,
10586             so every affected CE must be checked, not only the single CE for
10587             which the CE score was changed in the first place (as every instance
10588             of that CE shares the same CE score, and therefore also can change)!
10589           */
10590           SCAN_PLAYFIELD(xx, yy)
10591           {
10592             if (Tile[xx][yy] == element)
10593               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10594                                  CE_SCORE_GETS_ZERO);
10595           }
10596         }
10597       }
10598
10599       break;
10600     }
10601
10602     case CA_SET_CE_ARTWORK:
10603     {
10604       int artwork_element = action_arg_element;
10605       boolean reset_frame = FALSE;
10606       int xx, yy;
10607
10608       if (action_arg == CA_ARG_ELEMENT_RESET)
10609         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10610                            element);
10611
10612       if (ei->gfx_element != artwork_element)
10613         reset_frame = TRUE;
10614
10615       ei->gfx_element = artwork_element;
10616
10617       SCAN_PLAYFIELD(xx, yy)
10618       {
10619         if (Tile[xx][yy] == element)
10620         {
10621           if (reset_frame)
10622           {
10623             ResetGfxAnimation(xx, yy);
10624             ResetRandomAnimationValue(xx, yy);
10625           }
10626
10627           TEST_DrawLevelField(xx, yy);
10628         }
10629       }
10630
10631       break;
10632     }
10633
10634     // ---------- engine actions  ---------------------------------------------
10635
10636     case CA_SET_ENGINE_SCAN_MODE:
10637     {
10638       InitPlayfieldScanMode(action_arg);
10639
10640       break;
10641     }
10642
10643     default:
10644       break;
10645   }
10646 }
10647
10648 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10649 {
10650   int old_element = Tile[x][y];
10651   int new_element = GetElementFromGroupElement(element);
10652   int previous_move_direction = MovDir[x][y];
10653   int last_ce_value = CustomValue[x][y];
10654   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10655   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10656   boolean add_player_onto_element = (new_element_is_player &&
10657                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10658                                      IS_WALKABLE(old_element));
10659
10660   if (!add_player_onto_element)
10661   {
10662     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10663       RemoveMovingField(x, y);
10664     else
10665       RemoveField(x, y);
10666
10667     Tile[x][y] = new_element;
10668
10669     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10670       MovDir[x][y] = previous_move_direction;
10671
10672     if (element_info[new_element].use_last_ce_value)
10673       CustomValue[x][y] = last_ce_value;
10674
10675     InitField_WithBug1(x, y, FALSE);
10676
10677     new_element = Tile[x][y];   // element may have changed
10678
10679     ResetGfxAnimation(x, y);
10680     ResetRandomAnimationValue(x, y);
10681
10682     TEST_DrawLevelField(x, y);
10683
10684     if (GFX_CRUMBLED(new_element))
10685       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10686   }
10687
10688   // check if element under the player changes from accessible to unaccessible
10689   // (needed for special case of dropping element which then changes)
10690   // (must be checked after creating new element for walkable group elements)
10691   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10692       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10693   {
10694     Bang(x, y);
10695
10696     return;
10697   }
10698
10699   // "ChangeCount" not set yet to allow "entered by player" change one time
10700   if (new_element_is_player)
10701     RelocatePlayer(x, y, new_element);
10702
10703   if (is_change)
10704     ChangeCount[x][y]++;        // count number of changes in the same frame
10705
10706   TestIfBadThingTouchesPlayer(x, y);
10707   TestIfPlayerTouchesCustomElement(x, y);
10708   TestIfElementTouchesCustomElement(x, y);
10709 }
10710
10711 static void CreateField(int x, int y, int element)
10712 {
10713   CreateFieldExt(x, y, element, FALSE);
10714 }
10715
10716 static void CreateElementFromChange(int x, int y, int element)
10717 {
10718   element = GET_VALID_RUNTIME_ELEMENT(element);
10719
10720   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10721   {
10722     int old_element = Tile[x][y];
10723
10724     // prevent changed element from moving in same engine frame
10725     // unless both old and new element can either fall or move
10726     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10727         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10728       Stop[x][y] = TRUE;
10729   }
10730
10731   CreateFieldExt(x, y, element, TRUE);
10732 }
10733
10734 static boolean ChangeElement(int x, int y, int element, int page)
10735 {
10736   struct ElementInfo *ei = &element_info[element];
10737   struct ElementChangeInfo *change = &ei->change_page[page];
10738   int ce_value = CustomValue[x][y];
10739   int ce_score = ei->collect_score;
10740   int target_element;
10741   int old_element = Tile[x][y];
10742
10743   // always use default change event to prevent running into a loop
10744   if (ChangeEvent[x][y] == -1)
10745     ChangeEvent[x][y] = CE_DELAY;
10746
10747   if (ChangeEvent[x][y] == CE_DELAY)
10748   {
10749     // reset actual trigger element, trigger player and action element
10750     change->actual_trigger_element = EL_EMPTY;
10751     change->actual_trigger_player = EL_EMPTY;
10752     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10753     change->actual_trigger_side = CH_SIDE_NONE;
10754     change->actual_trigger_ce_value = 0;
10755     change->actual_trigger_ce_score = 0;
10756   }
10757
10758   // do not change elements more than a specified maximum number of changes
10759   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10760     return FALSE;
10761
10762   ChangeCount[x][y]++;          // count number of changes in the same frame
10763
10764   if (change->explode)
10765   {
10766     Bang(x, y);
10767
10768     return TRUE;
10769   }
10770
10771   if (change->use_target_content)
10772   {
10773     boolean complete_replace = TRUE;
10774     boolean can_replace[3][3];
10775     int xx, yy;
10776
10777     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10778     {
10779       boolean is_empty;
10780       boolean is_walkable;
10781       boolean is_diggable;
10782       boolean is_collectible;
10783       boolean is_removable;
10784       boolean is_destructible;
10785       int ex = x + xx - 1;
10786       int ey = y + yy - 1;
10787       int content_element = change->target_content.e[xx][yy];
10788       int e;
10789
10790       can_replace[xx][yy] = TRUE;
10791
10792       if (ex == x && ey == y)   // do not check changing element itself
10793         continue;
10794
10795       if (content_element == EL_EMPTY_SPACE)
10796       {
10797         can_replace[xx][yy] = FALSE;    // do not replace border with space
10798
10799         continue;
10800       }
10801
10802       if (!IN_LEV_FIELD(ex, ey))
10803       {
10804         can_replace[xx][yy] = FALSE;
10805         complete_replace = FALSE;
10806
10807         continue;
10808       }
10809
10810       e = Tile[ex][ey];
10811
10812       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10813         e = MovingOrBlocked2Element(ex, ey);
10814
10815       is_empty = (IS_FREE(ex, ey) ||
10816                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10817
10818       is_walkable     = (is_empty || IS_WALKABLE(e));
10819       is_diggable     = (is_empty || IS_DIGGABLE(e));
10820       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10821       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10822       is_removable    = (is_diggable || is_collectible);
10823
10824       can_replace[xx][yy] =
10825         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10826           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10827           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10828           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10829           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10830           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10831          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10832
10833       if (!can_replace[xx][yy])
10834         complete_replace = FALSE;
10835     }
10836
10837     if (!change->only_if_complete || complete_replace)
10838     {
10839       boolean something_has_changed = FALSE;
10840
10841       if (change->only_if_complete && change->use_random_replace &&
10842           RND(100) < change->random_percentage)
10843         return FALSE;
10844
10845       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10846       {
10847         int ex = x + xx - 1;
10848         int ey = y + yy - 1;
10849         int content_element;
10850
10851         if (can_replace[xx][yy] && (!change->use_random_replace ||
10852                                     RND(100) < change->random_percentage))
10853         {
10854           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10855             RemoveMovingField(ex, ey);
10856
10857           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10858
10859           content_element = change->target_content.e[xx][yy];
10860           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10861                                               ce_value, ce_score);
10862
10863           CreateElementFromChange(ex, ey, target_element);
10864
10865           something_has_changed = TRUE;
10866
10867           // for symmetry reasons, freeze newly created border elements
10868           if (ex != x || ey != y)
10869             Stop[ex][ey] = TRUE;        // no more moving in this frame
10870         }
10871       }
10872
10873       if (something_has_changed)
10874       {
10875         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10876         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10877       }
10878     }
10879   }
10880   else
10881   {
10882     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10883                                         ce_value, ce_score);
10884
10885     if (element == EL_DIAGONAL_GROWING ||
10886         element == EL_DIAGONAL_SHRINKING)
10887     {
10888       target_element = Store[x][y];
10889
10890       Store[x][y] = EL_EMPTY;
10891     }
10892
10893     // special case: element changes to player (and may be kept if walkable)
10894     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10895       CreateElementFromChange(x, y, EL_EMPTY);
10896
10897     CreateElementFromChange(x, y, target_element);
10898
10899     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10900     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10901   }
10902
10903   // this uses direct change before indirect change
10904   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10905
10906   return TRUE;
10907 }
10908
10909 static void HandleElementChange(int x, int y, int page)
10910 {
10911   int element = MovingOrBlocked2Element(x, y);
10912   struct ElementInfo *ei = &element_info[element];
10913   struct ElementChangeInfo *change = &ei->change_page[page];
10914   boolean handle_action_before_change = FALSE;
10915
10916 #ifdef DEBUG
10917   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10918       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10919   {
10920     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10921           x, y, element, element_info[element].token_name);
10922     Debug("game:playing:HandleElementChange", "This should never happen!");
10923   }
10924 #endif
10925
10926   // this can happen with classic bombs on walkable, changing elements
10927   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10928   {
10929     return;
10930   }
10931
10932   if (ChangeDelay[x][y] == 0)           // initialize element change
10933   {
10934     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10935
10936     if (change->can_change)
10937     {
10938       // !!! not clear why graphic animation should be reset at all here !!!
10939       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10940       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10941
10942       /*
10943         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10944
10945         When using an animation frame delay of 1 (this only happens with
10946         "sp_zonk.moving.left/right" in the classic graphics), the default
10947         (non-moving) animation shows wrong animation frames (while the
10948         moving animation, like "sp_zonk.moving.left/right", is correct,
10949         so this graphical bug never shows up with the classic graphics).
10950         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10951         be drawn instead of the correct frames 0,1,2,3. This is caused by
10952         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10953         an element change: First when the change delay ("ChangeDelay[][]")
10954         counter has reached zero after decrementing, then a second time in
10955         the next frame (after "GfxFrame[][]" was already incremented) when
10956         "ChangeDelay[][]" is reset to the initial delay value again.
10957
10958         This causes frame 0 to be drawn twice, while the last frame won't
10959         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10960
10961         As some animations may already be cleverly designed around this bug
10962         (at least the "Snake Bite" snake tail animation does this), it cannot
10963         simply be fixed here without breaking such existing animations.
10964         Unfortunately, it cannot easily be detected if a graphics set was
10965         designed "before" or "after" the bug was fixed. As a workaround,
10966         a new graphics set option "game.graphics_engine_version" was added
10967         to be able to specify the game's major release version for which the
10968         graphics set was designed, which can then be used to decide if the
10969         bugfix should be used (version 4 and above) or not (version 3 or
10970         below, or if no version was specified at all, as with old sets).
10971
10972         (The wrong/fixed animation frames can be tested with the test level set
10973         "test_gfxframe" and level "000", which contains a specially prepared
10974         custom element at level position (x/y) == (11/9) which uses the zonk
10975         animation mentioned above. Using "game.graphics_engine_version: 4"
10976         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10977         This can also be seen from the debug output for this test element.)
10978       */
10979
10980       // when a custom element is about to change (for example by change delay),
10981       // do not reset graphic animation when the custom element is moving
10982       if (game.graphics_engine_version < 4 &&
10983           !IS_MOVING(x, y))
10984       {
10985         ResetGfxAnimation(x, y);
10986         ResetRandomAnimationValue(x, y);
10987       }
10988
10989       if (change->pre_change_function)
10990         change->pre_change_function(x, y);
10991     }
10992   }
10993
10994   ChangeDelay[x][y]--;
10995
10996   if (ChangeDelay[x][y] != 0)           // continue element change
10997   {
10998     if (change->can_change)
10999     {
11000       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11001
11002       if (IS_ANIMATED(graphic))
11003         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11004
11005       if (change->change_function)
11006         change->change_function(x, y);
11007     }
11008   }
11009   else                                  // finish element change
11010   {
11011     if (ChangePage[x][y] != -1)         // remember page from delayed change
11012     {
11013       page = ChangePage[x][y];
11014       ChangePage[x][y] = -1;
11015
11016       change = &ei->change_page[page];
11017     }
11018
11019     if (IS_MOVING(x, y))                // never change a running system ;-)
11020     {
11021       ChangeDelay[x][y] = 1;            // try change after next move step
11022       ChangePage[x][y] = page;          // remember page to use for change
11023
11024       return;
11025     }
11026
11027     // special case: set new level random seed before changing element
11028     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11029       handle_action_before_change = TRUE;
11030
11031     if (change->has_action && handle_action_before_change)
11032       ExecuteCustomElementAction(x, y, element, page);
11033
11034     if (change->can_change)
11035     {
11036       if (ChangeElement(x, y, element, page))
11037       {
11038         if (change->post_change_function)
11039           change->post_change_function(x, y);
11040       }
11041     }
11042
11043     if (change->has_action && !handle_action_before_change)
11044       ExecuteCustomElementAction(x, y, element, page);
11045   }
11046 }
11047
11048 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11049                                               int trigger_element,
11050                                               int trigger_event,
11051                                               int trigger_player,
11052                                               int trigger_side,
11053                                               int trigger_page)
11054 {
11055   boolean change_done_any = FALSE;
11056   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11057   int i;
11058
11059   if (!(trigger_events[trigger_element][trigger_event]))
11060     return FALSE;
11061
11062   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11063
11064   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11065   {
11066     int element = EL_CUSTOM_START + i;
11067     boolean change_done = FALSE;
11068     int p;
11069
11070     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11071         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11072       continue;
11073
11074     for (p = 0; p < element_info[element].num_change_pages; p++)
11075     {
11076       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11077
11078       if (change->can_change_or_has_action &&
11079           change->has_event[trigger_event] &&
11080           change->trigger_side & trigger_side &&
11081           change->trigger_player & trigger_player &&
11082           change->trigger_page & trigger_page_bits &&
11083           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11084       {
11085         change->actual_trigger_element = trigger_element;
11086         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11087         change->actual_trigger_player_bits = trigger_player;
11088         change->actual_trigger_side = trigger_side;
11089         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11090         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11091
11092         if ((change->can_change && !change_done) || change->has_action)
11093         {
11094           int x, y;
11095
11096           SCAN_PLAYFIELD(x, y)
11097           {
11098             if (Tile[x][y] == element)
11099             {
11100               if (change->can_change && !change_done)
11101               {
11102                 // if element already changed in this frame, not only prevent
11103                 // another element change (checked in ChangeElement()), but
11104                 // also prevent additional element actions for this element
11105
11106                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11107                     !level.use_action_after_change_bug)
11108                   continue;
11109
11110                 ChangeDelay[x][y] = 1;
11111                 ChangeEvent[x][y] = trigger_event;
11112
11113                 HandleElementChange(x, y, p);
11114               }
11115               else if (change->has_action)
11116               {
11117                 // if element already changed in this frame, not only prevent
11118                 // another element change (checked in ChangeElement()), but
11119                 // also prevent additional element actions for this element
11120
11121                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11122                     !level.use_action_after_change_bug)
11123                   continue;
11124
11125                 ExecuteCustomElementAction(x, y, element, p);
11126                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11127               }
11128             }
11129           }
11130
11131           if (change->can_change)
11132           {
11133             change_done = TRUE;
11134             change_done_any = TRUE;
11135           }
11136         }
11137       }
11138     }
11139   }
11140
11141   RECURSION_LOOP_DETECTION_END();
11142
11143   return change_done_any;
11144 }
11145
11146 static boolean CheckElementChangeExt(int x, int y,
11147                                      int element,
11148                                      int trigger_element,
11149                                      int trigger_event,
11150                                      int trigger_player,
11151                                      int trigger_side)
11152 {
11153   boolean change_done = FALSE;
11154   int p;
11155
11156   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11157       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11158     return FALSE;
11159
11160   if (Tile[x][y] == EL_BLOCKED)
11161   {
11162     Blocked2Moving(x, y, &x, &y);
11163     element = Tile[x][y];
11164   }
11165
11166   // check if element has already changed or is about to change after moving
11167   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11168        Tile[x][y] != element) ||
11169
11170       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11171        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11172         ChangePage[x][y] != -1)))
11173     return FALSE;
11174
11175   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11176
11177   for (p = 0; p < element_info[element].num_change_pages; p++)
11178   {
11179     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11180
11181     /* check trigger element for all events where the element that is checked
11182        for changing interacts with a directly adjacent element -- this is
11183        different to element changes that affect other elements to change on the
11184        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11185     boolean check_trigger_element =
11186       (trigger_event == CE_NEXT_TO_X ||
11187        trigger_event == CE_TOUCHING_X ||
11188        trigger_event == CE_HITTING_X ||
11189        trigger_event == CE_HIT_BY_X ||
11190        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11191
11192     if (change->can_change_or_has_action &&
11193         change->has_event[trigger_event] &&
11194         change->trigger_side & trigger_side &&
11195         change->trigger_player & trigger_player &&
11196         (!check_trigger_element ||
11197          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11198     {
11199       change->actual_trigger_element = trigger_element;
11200       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11201       change->actual_trigger_player_bits = trigger_player;
11202       change->actual_trigger_side = trigger_side;
11203       change->actual_trigger_ce_value = CustomValue[x][y];
11204       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11205
11206       // special case: trigger element not at (x,y) position for some events
11207       if (check_trigger_element)
11208       {
11209         static struct
11210         {
11211           int dx, dy;
11212         } move_xy[] =
11213           {
11214             {  0,  0 },
11215             { -1,  0 },
11216             { +1,  0 },
11217             {  0,  0 },
11218             {  0, -1 },
11219             {  0,  0 }, { 0, 0 }, { 0, 0 },
11220             {  0, +1 }
11221           };
11222
11223         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11224         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11225
11226         change->actual_trigger_ce_value = CustomValue[xx][yy];
11227         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11228       }
11229
11230       if (change->can_change && !change_done)
11231       {
11232         ChangeDelay[x][y] = 1;
11233         ChangeEvent[x][y] = trigger_event;
11234
11235         HandleElementChange(x, y, p);
11236
11237         change_done = TRUE;
11238       }
11239       else if (change->has_action)
11240       {
11241         ExecuteCustomElementAction(x, y, element, p);
11242         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11243       }
11244     }
11245   }
11246
11247   RECURSION_LOOP_DETECTION_END();
11248
11249   return change_done;
11250 }
11251
11252 static void PlayPlayerSound(struct PlayerInfo *player)
11253 {
11254   int jx = player->jx, jy = player->jy;
11255   int sound_element = player->artwork_element;
11256   int last_action = player->last_action_waiting;
11257   int action = player->action_waiting;
11258
11259   if (player->is_waiting)
11260   {
11261     if (action != last_action)
11262       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11263     else
11264       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11265   }
11266   else
11267   {
11268     if (action != last_action)
11269       StopSound(element_info[sound_element].sound[last_action]);
11270
11271     if (last_action == ACTION_SLEEPING)
11272       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11273   }
11274 }
11275
11276 static void PlayAllPlayersSound(void)
11277 {
11278   int i;
11279
11280   for (i = 0; i < MAX_PLAYERS; i++)
11281     if (stored_player[i].active)
11282       PlayPlayerSound(&stored_player[i]);
11283 }
11284
11285 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11286 {
11287   boolean last_waiting = player->is_waiting;
11288   int move_dir = player->MovDir;
11289
11290   player->dir_waiting = move_dir;
11291   player->last_action_waiting = player->action_waiting;
11292
11293   if (is_waiting)
11294   {
11295     if (!last_waiting)          // not waiting -> waiting
11296     {
11297       player->is_waiting = TRUE;
11298
11299       player->frame_counter_bored =
11300         FrameCounter +
11301         game.player_boring_delay_fixed +
11302         GetSimpleRandom(game.player_boring_delay_random);
11303       player->frame_counter_sleeping =
11304         FrameCounter +
11305         game.player_sleeping_delay_fixed +
11306         GetSimpleRandom(game.player_sleeping_delay_random);
11307
11308       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11309     }
11310
11311     if (game.player_sleeping_delay_fixed +
11312         game.player_sleeping_delay_random > 0 &&
11313         player->anim_delay_counter == 0 &&
11314         player->post_delay_counter == 0 &&
11315         FrameCounter >= player->frame_counter_sleeping)
11316       player->is_sleeping = TRUE;
11317     else if (game.player_boring_delay_fixed +
11318              game.player_boring_delay_random > 0 &&
11319              FrameCounter >= player->frame_counter_bored)
11320       player->is_bored = TRUE;
11321
11322     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11323                               player->is_bored ? ACTION_BORING :
11324                               ACTION_WAITING);
11325
11326     if (player->is_sleeping && player->use_murphy)
11327     {
11328       // special case for sleeping Murphy when leaning against non-free tile
11329
11330       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11331           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11332            !IS_MOVING(player->jx - 1, player->jy)))
11333         move_dir = MV_LEFT;
11334       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11335                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11336                 !IS_MOVING(player->jx + 1, player->jy)))
11337         move_dir = MV_RIGHT;
11338       else
11339         player->is_sleeping = FALSE;
11340
11341       player->dir_waiting = move_dir;
11342     }
11343
11344     if (player->is_sleeping)
11345     {
11346       if (player->num_special_action_sleeping > 0)
11347       {
11348         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11349         {
11350           int last_special_action = player->special_action_sleeping;
11351           int num_special_action = player->num_special_action_sleeping;
11352           int special_action =
11353             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11354              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11355              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11356              last_special_action + 1 : ACTION_SLEEPING);
11357           int special_graphic =
11358             el_act_dir2img(player->artwork_element, special_action, move_dir);
11359
11360           player->anim_delay_counter =
11361             graphic_info[special_graphic].anim_delay_fixed +
11362             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11363           player->post_delay_counter =
11364             graphic_info[special_graphic].post_delay_fixed +
11365             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11366
11367           player->special_action_sleeping = special_action;
11368         }
11369
11370         if (player->anim_delay_counter > 0)
11371         {
11372           player->action_waiting = player->special_action_sleeping;
11373           player->anim_delay_counter--;
11374         }
11375         else if (player->post_delay_counter > 0)
11376         {
11377           player->post_delay_counter--;
11378         }
11379       }
11380     }
11381     else if (player->is_bored)
11382     {
11383       if (player->num_special_action_bored > 0)
11384       {
11385         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11386         {
11387           int special_action =
11388             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11389           int special_graphic =
11390             el_act_dir2img(player->artwork_element, special_action, move_dir);
11391
11392           player->anim_delay_counter =
11393             graphic_info[special_graphic].anim_delay_fixed +
11394             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11395           player->post_delay_counter =
11396             graphic_info[special_graphic].post_delay_fixed +
11397             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11398
11399           player->special_action_bored = special_action;
11400         }
11401
11402         if (player->anim_delay_counter > 0)
11403         {
11404           player->action_waiting = player->special_action_bored;
11405           player->anim_delay_counter--;
11406         }
11407         else if (player->post_delay_counter > 0)
11408         {
11409           player->post_delay_counter--;
11410         }
11411       }
11412     }
11413   }
11414   else if (last_waiting)        // waiting -> not waiting
11415   {
11416     player->is_waiting = FALSE;
11417     player->is_bored = FALSE;
11418     player->is_sleeping = FALSE;
11419
11420     player->frame_counter_bored = -1;
11421     player->frame_counter_sleeping = -1;
11422
11423     player->anim_delay_counter = 0;
11424     player->post_delay_counter = 0;
11425
11426     player->dir_waiting = player->MovDir;
11427     player->action_waiting = ACTION_DEFAULT;
11428
11429     player->special_action_bored = ACTION_DEFAULT;
11430     player->special_action_sleeping = ACTION_DEFAULT;
11431   }
11432 }
11433
11434 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11435 {
11436   if ((!player->is_moving  && player->was_moving) ||
11437       (player->MovPos == 0 && player->was_moving) ||
11438       (player->is_snapping && !player->was_snapping) ||
11439       (player->is_dropping && !player->was_dropping))
11440   {
11441     if (!CheckSaveEngineSnapshotToList())
11442       return;
11443
11444     player->was_moving = FALSE;
11445     player->was_snapping = TRUE;
11446     player->was_dropping = TRUE;
11447   }
11448   else
11449   {
11450     if (player->is_moving)
11451       player->was_moving = TRUE;
11452
11453     if (!player->is_snapping)
11454       player->was_snapping = FALSE;
11455
11456     if (!player->is_dropping)
11457       player->was_dropping = FALSE;
11458   }
11459
11460   static struct MouseActionInfo mouse_action_last = { 0 };
11461   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11462   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11463
11464   if (new_released)
11465     CheckSaveEngineSnapshotToList();
11466
11467   mouse_action_last = mouse_action;
11468 }
11469
11470 static void CheckSingleStepMode(struct PlayerInfo *player)
11471 {
11472   if (tape.single_step && tape.recording && !tape.pausing)
11473   {
11474     // as it is called "single step mode", just return to pause mode when the
11475     // player stopped moving after one tile (or never starts moving at all)
11476     // (reverse logic needed here in case single step mode used in team mode)
11477     if (player->is_moving ||
11478         player->is_pushing ||
11479         player->is_dropping_pressed ||
11480         player->effective_mouse_action.button)
11481       game.enter_single_step_mode = FALSE;
11482   }
11483
11484   CheckSaveEngineSnapshot(player);
11485 }
11486
11487 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11488 {
11489   int left      = player_action & JOY_LEFT;
11490   int right     = player_action & JOY_RIGHT;
11491   int up        = player_action & JOY_UP;
11492   int down      = player_action & JOY_DOWN;
11493   int button1   = player_action & JOY_BUTTON_1;
11494   int button2   = player_action & JOY_BUTTON_2;
11495   int dx        = (left ? -1 : right ? 1 : 0);
11496   int dy        = (up   ? -1 : down  ? 1 : 0);
11497
11498   if (!player->active || tape.pausing)
11499     return 0;
11500
11501   if (player_action)
11502   {
11503     if (button1)
11504       SnapField(player, dx, dy);
11505     else
11506     {
11507       if (button2)
11508         DropElement(player);
11509
11510       MovePlayer(player, dx, dy);
11511     }
11512
11513     CheckSingleStepMode(player);
11514
11515     SetPlayerWaiting(player, FALSE);
11516
11517     return player_action;
11518   }
11519   else
11520   {
11521     // no actions for this player (no input at player's configured device)
11522
11523     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11524     SnapField(player, 0, 0);
11525     CheckGravityMovementWhenNotMoving(player);
11526
11527     if (player->MovPos == 0)
11528       SetPlayerWaiting(player, TRUE);
11529
11530     if (player->MovPos == 0)    // needed for tape.playing
11531       player->is_moving = FALSE;
11532
11533     player->is_dropping = FALSE;
11534     player->is_dropping_pressed = FALSE;
11535     player->drop_pressed_delay = 0;
11536
11537     CheckSingleStepMode(player);
11538
11539     return 0;
11540   }
11541 }
11542
11543 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11544                                          byte *tape_action)
11545 {
11546   if (!tape.use_mouse_actions)
11547     return;
11548
11549   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11550   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11551   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11552 }
11553
11554 static void SetTapeActionFromMouseAction(byte *tape_action,
11555                                          struct MouseActionInfo *mouse_action)
11556 {
11557   if (!tape.use_mouse_actions)
11558     return;
11559
11560   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11561   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11562   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11563 }
11564
11565 static void CheckLevelSolved(void)
11566 {
11567   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11568   {
11569     if (game_em.level_solved &&
11570         !game_em.game_over)                             // game won
11571     {
11572       LevelSolved();
11573
11574       game_em.game_over = TRUE;
11575
11576       game.all_players_gone = TRUE;
11577     }
11578
11579     if (game_em.game_over)                              // game lost
11580       game.all_players_gone = TRUE;
11581   }
11582   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11583   {
11584     if (game_sp.level_solved &&
11585         !game_sp.game_over)                             // game won
11586     {
11587       LevelSolved();
11588
11589       game_sp.game_over = TRUE;
11590
11591       game.all_players_gone = TRUE;
11592     }
11593
11594     if (game_sp.game_over)                              // game lost
11595       game.all_players_gone = TRUE;
11596   }
11597   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11598   {
11599     if (game_mm.level_solved &&
11600         !game_mm.game_over)                             // game won
11601     {
11602       LevelSolved();
11603
11604       game_mm.game_over = TRUE;
11605
11606       game.all_players_gone = TRUE;
11607     }
11608
11609     if (game_mm.game_over)                              // game lost
11610       game.all_players_gone = TRUE;
11611   }
11612 }
11613
11614 static void CheckLevelTime_StepCounter(void)
11615 {
11616   int i;
11617
11618   TimePlayed++;
11619
11620   if (TimeLeft > 0)
11621   {
11622     TimeLeft--;
11623
11624     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11625       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11626
11627     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11628
11629     DisplayGameControlValues();
11630
11631     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11632       for (i = 0; i < MAX_PLAYERS; i++)
11633         KillPlayer(&stored_player[i]);
11634   }
11635   else if (game.no_time_limit && !game.all_players_gone)
11636   {
11637     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11638
11639     DisplayGameControlValues();
11640   }
11641 }
11642
11643 static void CheckLevelTime(void)
11644 {
11645   int i;
11646
11647   if (TimeFrames >= FRAMES_PER_SECOND)
11648   {
11649     TimeFrames = 0;
11650     TapeTime++;
11651
11652     for (i = 0; i < MAX_PLAYERS; i++)
11653     {
11654       struct PlayerInfo *player = &stored_player[i];
11655
11656       if (SHIELD_ON(player))
11657       {
11658         player->shield_normal_time_left--;
11659
11660         if (player->shield_deadly_time_left > 0)
11661           player->shield_deadly_time_left--;
11662       }
11663     }
11664
11665     if (!game.LevelSolved && !level.use_step_counter)
11666     {
11667       TimePlayed++;
11668
11669       if (TimeLeft > 0)
11670       {
11671         TimeLeft--;
11672
11673         if (TimeLeft <= 10 && setup.time_limit)
11674           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11675
11676         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11677            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11678
11679         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11680
11681         if (!TimeLeft && setup.time_limit)
11682         {
11683           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11684             game_em.lev->killed_out_of_time = TRUE;
11685           else
11686             for (i = 0; i < MAX_PLAYERS; i++)
11687               KillPlayer(&stored_player[i]);
11688         }
11689       }
11690       else if (game.no_time_limit && !game.all_players_gone)
11691       {
11692         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11693       }
11694
11695       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11696     }
11697
11698     if (tape.recording || tape.playing)
11699       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11700   }
11701
11702   if (tape.recording || tape.playing)
11703     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11704
11705   UpdateAndDisplayGameControlValues();
11706 }
11707
11708 void AdvanceFrameAndPlayerCounters(int player_nr)
11709 {
11710   int i;
11711
11712   // advance frame counters (global frame counter and time frame counter)
11713   FrameCounter++;
11714   TimeFrames++;
11715
11716   // advance player counters (counters for move delay, move animation etc.)
11717   for (i = 0; i < MAX_PLAYERS; i++)
11718   {
11719     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11720     int move_delay_value = stored_player[i].move_delay_value;
11721     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11722
11723     if (!advance_player_counters)       // not all players may be affected
11724       continue;
11725
11726     if (move_frames == 0)       // less than one move per game frame
11727     {
11728       int stepsize = TILEX / move_delay_value;
11729       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11730       int count = (stored_player[i].is_moving ?
11731                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11732
11733       if (count % delay == 0)
11734         move_frames = 1;
11735     }
11736
11737     stored_player[i].Frame += move_frames;
11738
11739     if (stored_player[i].MovPos != 0)
11740       stored_player[i].StepFrame += move_frames;
11741
11742     if (stored_player[i].move_delay > 0)
11743       stored_player[i].move_delay--;
11744
11745     // due to bugs in previous versions, counter must count up, not down
11746     if (stored_player[i].push_delay != -1)
11747       stored_player[i].push_delay++;
11748
11749     if (stored_player[i].drop_delay > 0)
11750       stored_player[i].drop_delay--;
11751
11752     if (stored_player[i].is_dropping_pressed)
11753       stored_player[i].drop_pressed_delay++;
11754   }
11755 }
11756
11757 void StartGameActions(boolean init_network_game, boolean record_tape,
11758                       int random_seed)
11759 {
11760   unsigned int new_random_seed = InitRND(random_seed);
11761
11762   if (record_tape)
11763     TapeStartRecording(new_random_seed);
11764
11765   if (init_network_game)
11766   {
11767     SendToServer_LevelFile();
11768     SendToServer_StartPlaying();
11769
11770     return;
11771   }
11772
11773   InitGame();
11774 }
11775
11776 static void GameActionsExt(void)
11777 {
11778 #if 0
11779   static unsigned int game_frame_delay = 0;
11780 #endif
11781   unsigned int game_frame_delay_value;
11782   byte *recorded_player_action;
11783   byte summarized_player_action = 0;
11784   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11785   int i;
11786
11787   // detect endless loops, caused by custom element programming
11788   if (recursion_loop_detected && recursion_loop_depth == 0)
11789   {
11790     char *message = getStringCat3("Internal Error! Element ",
11791                                   EL_NAME(recursion_loop_element),
11792                                   " caused endless loop! Quit the game?");
11793
11794     Warn("element '%s' caused endless loop in game engine",
11795          EL_NAME(recursion_loop_element));
11796
11797     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11798
11799     recursion_loop_detected = FALSE;    // if game should be continued
11800
11801     free(message);
11802
11803     return;
11804   }
11805
11806   if (game.restart_level)
11807     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11808
11809   CheckLevelSolved();
11810
11811   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11812     GameWon();
11813
11814   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11815     TapeStop();
11816
11817   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11818     return;
11819
11820   game_frame_delay_value =
11821     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11822
11823   if (tape.playing && tape.warp_forward && !tape.pausing)
11824     game_frame_delay_value = 0;
11825
11826   SetVideoFrameDelay(game_frame_delay_value);
11827
11828   // (de)activate virtual buttons depending on current game status
11829   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11830   {
11831     if (game.all_players_gone)  // if no players there to be controlled anymore
11832       SetOverlayActive(FALSE);
11833     else if (!tape.playing)     // if game continues after tape stopped playing
11834       SetOverlayActive(TRUE);
11835   }
11836
11837 #if 0
11838 #if 0
11839   // ---------- main game synchronization point ----------
11840
11841   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11842
11843   Debug("game:playing:skip", "skip == %d", skip);
11844
11845 #else
11846   // ---------- main game synchronization point ----------
11847
11848   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11849 #endif
11850 #endif
11851
11852   if (network_playing && !network_player_action_received)
11853   {
11854     // try to get network player actions in time
11855
11856     // last chance to get network player actions without main loop delay
11857     HandleNetworking();
11858
11859     // game was quit by network peer
11860     if (game_status != GAME_MODE_PLAYING)
11861       return;
11862
11863     // check if network player actions still missing and game still running
11864     if (!network_player_action_received && !checkGameEnded())
11865       return;           // failed to get network player actions in time
11866
11867     // do not yet reset "network_player_action_received" (for tape.pausing)
11868   }
11869
11870   if (tape.pausing)
11871     return;
11872
11873   // at this point we know that we really continue executing the game
11874
11875   network_player_action_received = FALSE;
11876
11877   // when playing tape, read previously recorded player input from tape data
11878   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11879
11880   local_player->effective_mouse_action = local_player->mouse_action;
11881
11882   if (recorded_player_action != NULL)
11883     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11884                                  recorded_player_action);
11885
11886   // TapePlayAction() may return NULL when toggling to "pause before death"
11887   if (tape.pausing)
11888     return;
11889
11890   if (tape.set_centered_player)
11891   {
11892     game.centered_player_nr_next = tape.centered_player_nr_next;
11893     game.set_centered_player = TRUE;
11894   }
11895
11896   for (i = 0; i < MAX_PLAYERS; i++)
11897   {
11898     summarized_player_action |= stored_player[i].action;
11899
11900     if (!network_playing && (game.team_mode || tape.playing))
11901       stored_player[i].effective_action = stored_player[i].action;
11902   }
11903
11904   if (network_playing && !checkGameEnded())
11905     SendToServer_MovePlayer(summarized_player_action);
11906
11907   // summarize all actions at local players mapped input device position
11908   // (this allows using different input devices in single player mode)
11909   if (!network.enabled && !game.team_mode)
11910     stored_player[map_player_action[local_player->index_nr]].effective_action =
11911       summarized_player_action;
11912
11913   // summarize all actions at centered player in local team mode
11914   if (tape.recording &&
11915       setup.team_mode && !network.enabled &&
11916       setup.input_on_focus &&
11917       game.centered_player_nr != -1)
11918   {
11919     for (i = 0; i < MAX_PLAYERS; i++)
11920       stored_player[map_player_action[i]].effective_action =
11921         (i == game.centered_player_nr ? summarized_player_action : 0);
11922   }
11923
11924   if (recorded_player_action != NULL)
11925     for (i = 0; i < MAX_PLAYERS; i++)
11926       stored_player[i].effective_action = recorded_player_action[i];
11927
11928   for (i = 0; i < MAX_PLAYERS; i++)
11929   {
11930     tape_action[i] = stored_player[i].effective_action;
11931
11932     /* (this may happen in the RND game engine if a player was not present on
11933        the playfield on level start, but appeared later from a custom element */
11934     if (setup.team_mode &&
11935         tape.recording &&
11936         tape_action[i] &&
11937         !tape.player_participates[i])
11938       tape.player_participates[i] = TRUE;
11939   }
11940
11941   SetTapeActionFromMouseAction(tape_action,
11942                                &local_player->effective_mouse_action);
11943
11944   // only record actions from input devices, but not programmed actions
11945   if (tape.recording)
11946     TapeRecordAction(tape_action);
11947
11948   // remember if game was played (especially after tape stopped playing)
11949   if (!tape.playing && summarized_player_action)
11950     game.GamePlayed = TRUE;
11951
11952 #if USE_NEW_PLAYER_ASSIGNMENTS
11953   // !!! also map player actions in single player mode !!!
11954   // if (game.team_mode)
11955   if (1)
11956   {
11957     byte mapped_action[MAX_PLAYERS];
11958
11959 #if DEBUG_PLAYER_ACTIONS
11960     for (i = 0; i < MAX_PLAYERS; i++)
11961       DebugContinued("", "%d, ", stored_player[i].effective_action);
11962 #endif
11963
11964     for (i = 0; i < MAX_PLAYERS; i++)
11965       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11966
11967     for (i = 0; i < MAX_PLAYERS; i++)
11968       stored_player[i].effective_action = mapped_action[i];
11969
11970 #if DEBUG_PLAYER_ACTIONS
11971     DebugContinued("", "=> ");
11972     for (i = 0; i < MAX_PLAYERS; i++)
11973       DebugContinued("", "%d, ", stored_player[i].effective_action);
11974     DebugContinued("game:playing:player", "\n");
11975 #endif
11976   }
11977 #if DEBUG_PLAYER_ACTIONS
11978   else
11979   {
11980     for (i = 0; i < MAX_PLAYERS; i++)
11981       DebugContinued("", "%d, ", stored_player[i].effective_action);
11982     DebugContinued("game:playing:player", "\n");
11983   }
11984 #endif
11985 #endif
11986
11987   for (i = 0; i < MAX_PLAYERS; i++)
11988   {
11989     // allow engine snapshot in case of changed movement attempt
11990     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11991         (stored_player[i].effective_action & KEY_MOTION))
11992       game.snapshot.changed_action = TRUE;
11993
11994     // allow engine snapshot in case of snapping/dropping attempt
11995     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11996         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11997       game.snapshot.changed_action = TRUE;
11998
11999     game.snapshot.last_action[i] = stored_player[i].effective_action;
12000   }
12001
12002   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12003   {
12004     GameActions_EM_Main();
12005   }
12006   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12007   {
12008     GameActions_SP_Main();
12009   }
12010   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12011   {
12012     GameActions_MM_Main();
12013   }
12014   else
12015   {
12016     GameActions_RND_Main();
12017   }
12018
12019   BlitScreenToBitmap(backbuffer);
12020
12021   CheckLevelSolved();
12022   CheckLevelTime();
12023
12024   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12025
12026   if (global.show_frames_per_second)
12027   {
12028     static unsigned int fps_counter = 0;
12029     static int fps_frames = 0;
12030     unsigned int fps_delay_ms = Counter() - fps_counter;
12031
12032     fps_frames++;
12033
12034     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12035     {
12036       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12037
12038       fps_frames = 0;
12039       fps_counter = Counter();
12040
12041       // always draw FPS to screen after FPS value was updated
12042       redraw_mask |= REDRAW_FPS;
12043     }
12044
12045     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12046     if (GetDrawDeactivationMask() == REDRAW_NONE)
12047       redraw_mask |= REDRAW_FPS;
12048   }
12049 }
12050
12051 static void GameActions_CheckSaveEngineSnapshot(void)
12052 {
12053   if (!game.snapshot.save_snapshot)
12054     return;
12055
12056   // clear flag for saving snapshot _before_ saving snapshot
12057   game.snapshot.save_snapshot = FALSE;
12058
12059   SaveEngineSnapshotToList();
12060 }
12061
12062 void GameActions(void)
12063 {
12064   GameActionsExt();
12065
12066   GameActions_CheckSaveEngineSnapshot();
12067 }
12068
12069 void GameActions_EM_Main(void)
12070 {
12071   byte effective_action[MAX_PLAYERS];
12072   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12073   int i;
12074
12075   for (i = 0; i < MAX_PLAYERS; i++)
12076     effective_action[i] = stored_player[i].effective_action;
12077
12078   GameActions_EM(effective_action, warp_mode);
12079 }
12080
12081 void GameActions_SP_Main(void)
12082 {
12083   byte effective_action[MAX_PLAYERS];
12084   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12085   int i;
12086
12087   for (i = 0; i < MAX_PLAYERS; i++)
12088     effective_action[i] = stored_player[i].effective_action;
12089
12090   GameActions_SP(effective_action, warp_mode);
12091
12092   for (i = 0; i < MAX_PLAYERS; i++)
12093   {
12094     if (stored_player[i].force_dropping)
12095       stored_player[i].action |= KEY_BUTTON_DROP;
12096
12097     stored_player[i].force_dropping = FALSE;
12098   }
12099 }
12100
12101 void GameActions_MM_Main(void)
12102 {
12103   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12104
12105   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12106 }
12107
12108 void GameActions_RND_Main(void)
12109 {
12110   GameActions_RND();
12111 }
12112
12113 void GameActions_RND(void)
12114 {
12115   static struct MouseActionInfo mouse_action_last = { 0 };
12116   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12117   int magic_wall_x = 0, magic_wall_y = 0;
12118   int i, x, y, element, graphic, last_gfx_frame;
12119
12120   InitPlayfieldScanModeVars();
12121
12122   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12123   {
12124     SCAN_PLAYFIELD(x, y)
12125     {
12126       ChangeCount[x][y] = 0;
12127       ChangeEvent[x][y] = -1;
12128     }
12129   }
12130
12131   if (game.set_centered_player)
12132   {
12133     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12134
12135     // switching to "all players" only possible if all players fit to screen
12136     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12137     {
12138       game.centered_player_nr_next = game.centered_player_nr;
12139       game.set_centered_player = FALSE;
12140     }
12141
12142     // do not switch focus to non-existing (or non-active) player
12143     if (game.centered_player_nr_next >= 0 &&
12144         !stored_player[game.centered_player_nr_next].active)
12145     {
12146       game.centered_player_nr_next = game.centered_player_nr;
12147       game.set_centered_player = FALSE;
12148     }
12149   }
12150
12151   if (game.set_centered_player &&
12152       ScreenMovPos == 0)        // screen currently aligned at tile position
12153   {
12154     int sx, sy;
12155
12156     if (game.centered_player_nr_next == -1)
12157     {
12158       setScreenCenteredToAllPlayers(&sx, &sy);
12159     }
12160     else
12161     {
12162       sx = stored_player[game.centered_player_nr_next].jx;
12163       sy = stored_player[game.centered_player_nr_next].jy;
12164     }
12165
12166     game.centered_player_nr = game.centered_player_nr_next;
12167     game.set_centered_player = FALSE;
12168
12169     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12170     DrawGameDoorValues();
12171   }
12172
12173   // check single step mode (set flag and clear again if any player is active)
12174   game.enter_single_step_mode =
12175     (tape.single_step && tape.recording && !tape.pausing);
12176
12177   for (i = 0; i < MAX_PLAYERS; i++)
12178   {
12179     int actual_player_action = stored_player[i].effective_action;
12180
12181 #if 1
12182     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12183        - rnd_equinox_tetrachloride 048
12184        - rnd_equinox_tetrachloride_ii 096
12185        - rnd_emanuel_schmieg 002
12186        - doctor_sloan_ww 001, 020
12187     */
12188     if (stored_player[i].MovPos == 0)
12189       CheckGravityMovement(&stored_player[i]);
12190 #endif
12191
12192     // overwrite programmed action with tape action
12193     if (stored_player[i].programmed_action)
12194       actual_player_action = stored_player[i].programmed_action;
12195
12196     PlayerActions(&stored_player[i], actual_player_action);
12197
12198     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12199   }
12200
12201   // single step pause mode may already have been toggled by "ScrollPlayer()"
12202   if (game.enter_single_step_mode && !tape.pausing)
12203     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12204
12205   ScrollScreen(NULL, SCROLL_GO_ON);
12206
12207   /* for backwards compatibility, the following code emulates a fixed bug that
12208      occured when pushing elements (causing elements that just made their last
12209      pushing step to already (if possible) make their first falling step in the
12210      same game frame, which is bad); this code is also needed to use the famous
12211      "spring push bug" which is used in older levels and might be wanted to be
12212      used also in newer levels, but in this case the buggy pushing code is only
12213      affecting the "spring" element and no other elements */
12214
12215   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12216   {
12217     for (i = 0; i < MAX_PLAYERS; i++)
12218     {
12219       struct PlayerInfo *player = &stored_player[i];
12220       int x = player->jx;
12221       int y = player->jy;
12222
12223       if (player->active && player->is_pushing && player->is_moving &&
12224           IS_MOVING(x, y) &&
12225           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12226            Tile[x][y] == EL_SPRING))
12227       {
12228         ContinueMoving(x, y);
12229
12230         // continue moving after pushing (this is actually a bug)
12231         if (!IS_MOVING(x, y))
12232           Stop[x][y] = FALSE;
12233       }
12234     }
12235   }
12236
12237   SCAN_PLAYFIELD(x, y)
12238   {
12239     Last[x][y] = Tile[x][y];
12240
12241     ChangeCount[x][y] = 0;
12242     ChangeEvent[x][y] = -1;
12243
12244     // this must be handled before main playfield loop
12245     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12246     {
12247       MovDelay[x][y]--;
12248       if (MovDelay[x][y] <= 0)
12249         RemoveField(x, y);
12250     }
12251
12252     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12253     {
12254       MovDelay[x][y]--;
12255       if (MovDelay[x][y] <= 0)
12256       {
12257         int element = Store[x][y];
12258         int move_direction = MovDir[x][y];
12259         int player_index_bit = Store2[x][y];
12260
12261         Store[x][y] = 0;
12262         Store2[x][y] = 0;
12263
12264         RemoveField(x, y);
12265         TEST_DrawLevelField(x, y);
12266
12267         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12268
12269         if (IS_ENVELOPE(element))
12270           local_player->show_envelope = element;
12271       }
12272     }
12273
12274 #if DEBUG
12275     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12276     {
12277       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12278             x, y);
12279       Debug("game:playing:GameActions_RND", "This should never happen!");
12280
12281       ChangePage[x][y] = -1;
12282     }
12283 #endif
12284
12285     Stop[x][y] = FALSE;
12286     if (WasJustMoving[x][y] > 0)
12287       WasJustMoving[x][y]--;
12288     if (WasJustFalling[x][y] > 0)
12289       WasJustFalling[x][y]--;
12290     if (CheckCollision[x][y] > 0)
12291       CheckCollision[x][y]--;
12292     if (CheckImpact[x][y] > 0)
12293       CheckImpact[x][y]--;
12294
12295     GfxFrame[x][y]++;
12296
12297     /* reset finished pushing action (not done in ContinueMoving() to allow
12298        continuous pushing animation for elements with zero push delay) */
12299     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12300     {
12301       ResetGfxAnimation(x, y);
12302       TEST_DrawLevelField(x, y);
12303     }
12304
12305 #if DEBUG
12306     if (IS_BLOCKED(x, y))
12307     {
12308       int oldx, oldy;
12309
12310       Blocked2Moving(x, y, &oldx, &oldy);
12311       if (!IS_MOVING(oldx, oldy))
12312       {
12313         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12314         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12315         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12316         Debug("game:playing:GameActions_RND", "This should never happen!");
12317       }
12318     }
12319 #endif
12320   }
12321
12322   if (mouse_action.button)
12323   {
12324     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12325     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12326
12327     x = mouse_action.lx;
12328     y = mouse_action.ly;
12329     element = Tile[x][y];
12330
12331     if (new_button)
12332     {
12333       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12334       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12335                                          ch_button);
12336     }
12337
12338     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12339     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12340                                        ch_button);
12341
12342     if (level.use_step_counter)
12343     {
12344       boolean counted_click = FALSE;
12345
12346       // element clicked that can change when clicked/pressed
12347       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12348           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12349            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12350         counted_click = TRUE;
12351
12352       // element clicked that can trigger change when clicked/pressed
12353       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12354           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12355         counted_click = TRUE;
12356
12357       if (new_button && counted_click)
12358         CheckLevelTime_StepCounter();
12359     }
12360   }
12361
12362   SCAN_PLAYFIELD(x, y)
12363   {
12364     element = Tile[x][y];
12365     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12366     last_gfx_frame = GfxFrame[x][y];
12367
12368     if (element == EL_EMPTY)
12369       graphic = el2img(GfxElementEmpty[x][y]);
12370
12371     ResetGfxFrame(x, y);
12372
12373     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12374       DrawLevelGraphicAnimation(x, y, graphic);
12375
12376     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12377         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12378       ResetRandomAnimationValue(x, y);
12379
12380     SetRandomAnimationValue(x, y);
12381
12382     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12383
12384     if (IS_INACTIVE(element))
12385     {
12386       if (IS_ANIMATED(graphic))
12387         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12388
12389       continue;
12390     }
12391
12392     // this may take place after moving, so 'element' may have changed
12393     if (IS_CHANGING(x, y) &&
12394         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12395     {
12396       int page = element_info[element].event_page_nr[CE_DELAY];
12397
12398       HandleElementChange(x, y, page);
12399
12400       element = Tile[x][y];
12401       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12402     }
12403
12404     CheckNextToConditions(x, y);
12405
12406     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12407     {
12408       StartMoving(x, y);
12409
12410       element = Tile[x][y];
12411       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12412
12413       if (IS_ANIMATED(graphic) &&
12414           !IS_MOVING(x, y) &&
12415           !Stop[x][y])
12416         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12417
12418       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12419         TEST_DrawTwinkleOnField(x, y);
12420     }
12421     else if (element == EL_ACID)
12422     {
12423       if (!Stop[x][y])
12424         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12425     }
12426     else if ((element == EL_EXIT_OPEN ||
12427               element == EL_EM_EXIT_OPEN ||
12428               element == EL_SP_EXIT_OPEN ||
12429               element == EL_STEEL_EXIT_OPEN ||
12430               element == EL_EM_STEEL_EXIT_OPEN ||
12431               element == EL_SP_TERMINAL ||
12432               element == EL_SP_TERMINAL_ACTIVE ||
12433               element == EL_EXTRA_TIME ||
12434               element == EL_SHIELD_NORMAL ||
12435               element == EL_SHIELD_DEADLY) &&
12436              IS_ANIMATED(graphic))
12437       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12438     else if (IS_MOVING(x, y))
12439       ContinueMoving(x, y);
12440     else if (IS_ACTIVE_BOMB(element))
12441       CheckDynamite(x, y);
12442     else if (element == EL_AMOEBA_GROWING)
12443       AmoebaGrowing(x, y);
12444     else if (element == EL_AMOEBA_SHRINKING)
12445       AmoebaShrinking(x, y);
12446
12447 #if !USE_NEW_AMOEBA_CODE
12448     else if (IS_AMOEBALIVE(element))
12449       AmoebaReproduce(x, y);
12450 #endif
12451
12452     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12453       Life(x, y);
12454     else if (element == EL_EXIT_CLOSED)
12455       CheckExit(x, y);
12456     else if (element == EL_EM_EXIT_CLOSED)
12457       CheckExitEM(x, y);
12458     else if (element == EL_STEEL_EXIT_CLOSED)
12459       CheckExitSteel(x, y);
12460     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12461       CheckExitSteelEM(x, y);
12462     else if (element == EL_SP_EXIT_CLOSED)
12463       CheckExitSP(x, y);
12464     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12465              element == EL_EXPANDABLE_STEELWALL_GROWING)
12466       MauerWaechst(x, y);
12467     else if (element == EL_EXPANDABLE_WALL ||
12468              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12469              element == EL_EXPANDABLE_WALL_VERTICAL ||
12470              element == EL_EXPANDABLE_WALL_ANY ||
12471              element == EL_BD_EXPANDABLE_WALL)
12472       MauerAbleger(x, y);
12473     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12474              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12475              element == EL_EXPANDABLE_STEELWALL_ANY)
12476       MauerAblegerStahl(x, y);
12477     else if (element == EL_FLAMES)
12478       CheckForDragon(x, y);
12479     else if (element == EL_EXPLOSION)
12480       ; // drawing of correct explosion animation is handled separately
12481     else if (element == EL_ELEMENT_SNAPPING ||
12482              element == EL_DIAGONAL_SHRINKING ||
12483              element == EL_DIAGONAL_GROWING)
12484     {
12485       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12486
12487       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12488     }
12489     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12490       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12491
12492     if (IS_BELT_ACTIVE(element))
12493       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12494
12495     if (game.magic_wall_active)
12496     {
12497       int jx = local_player->jx, jy = local_player->jy;
12498
12499       // play the element sound at the position nearest to the player
12500       if ((element == EL_MAGIC_WALL_FULL ||
12501            element == EL_MAGIC_WALL_ACTIVE ||
12502            element == EL_MAGIC_WALL_EMPTYING ||
12503            element == EL_BD_MAGIC_WALL_FULL ||
12504            element == EL_BD_MAGIC_WALL_ACTIVE ||
12505            element == EL_BD_MAGIC_WALL_EMPTYING ||
12506            element == EL_DC_MAGIC_WALL_FULL ||
12507            element == EL_DC_MAGIC_WALL_ACTIVE ||
12508            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12509           ABS(x - jx) + ABS(y - jy) <
12510           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12511       {
12512         magic_wall_x = x;
12513         magic_wall_y = y;
12514       }
12515     }
12516   }
12517
12518 #if USE_NEW_AMOEBA_CODE
12519   // new experimental amoeba growth stuff
12520   if (!(FrameCounter % 8))
12521   {
12522     static unsigned int random = 1684108901;
12523
12524     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12525     {
12526       x = RND(lev_fieldx);
12527       y = RND(lev_fieldy);
12528       element = Tile[x][y];
12529
12530       if (!IS_PLAYER(x,y) &&
12531           (element == EL_EMPTY ||
12532            CAN_GROW_INTO(element) ||
12533            element == EL_QUICKSAND_EMPTY ||
12534            element == EL_QUICKSAND_FAST_EMPTY ||
12535            element == EL_ACID_SPLASH_LEFT ||
12536            element == EL_ACID_SPLASH_RIGHT))
12537       {
12538         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12539             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12540             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12541             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12542           Tile[x][y] = EL_AMOEBA_DROP;
12543       }
12544
12545       random = random * 129 + 1;
12546     }
12547   }
12548 #endif
12549
12550   game.explosions_delayed = FALSE;
12551
12552   SCAN_PLAYFIELD(x, y)
12553   {
12554     element = Tile[x][y];
12555
12556     if (ExplodeField[x][y])
12557       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12558     else if (element == EL_EXPLOSION)
12559       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12560
12561     ExplodeField[x][y] = EX_TYPE_NONE;
12562   }
12563
12564   game.explosions_delayed = TRUE;
12565
12566   if (game.magic_wall_active)
12567   {
12568     if (!(game.magic_wall_time_left % 4))
12569     {
12570       int element = Tile[magic_wall_x][magic_wall_y];
12571
12572       if (element == EL_BD_MAGIC_WALL_FULL ||
12573           element == EL_BD_MAGIC_WALL_ACTIVE ||
12574           element == EL_BD_MAGIC_WALL_EMPTYING)
12575         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12576       else if (element == EL_DC_MAGIC_WALL_FULL ||
12577                element == EL_DC_MAGIC_WALL_ACTIVE ||
12578                element == EL_DC_MAGIC_WALL_EMPTYING)
12579         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12580       else
12581         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12582     }
12583
12584     if (game.magic_wall_time_left > 0)
12585     {
12586       game.magic_wall_time_left--;
12587
12588       if (!game.magic_wall_time_left)
12589       {
12590         SCAN_PLAYFIELD(x, y)
12591         {
12592           element = Tile[x][y];
12593
12594           if (element == EL_MAGIC_WALL_ACTIVE ||
12595               element == EL_MAGIC_WALL_FULL)
12596           {
12597             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12598             TEST_DrawLevelField(x, y);
12599           }
12600           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12601                    element == EL_BD_MAGIC_WALL_FULL)
12602           {
12603             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12604             TEST_DrawLevelField(x, y);
12605           }
12606           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12607                    element == EL_DC_MAGIC_WALL_FULL)
12608           {
12609             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12610             TEST_DrawLevelField(x, y);
12611           }
12612         }
12613
12614         game.magic_wall_active = FALSE;
12615       }
12616     }
12617   }
12618
12619   if (game.light_time_left > 0)
12620   {
12621     game.light_time_left--;
12622
12623     if (game.light_time_left == 0)
12624       RedrawAllLightSwitchesAndInvisibleElements();
12625   }
12626
12627   if (game.timegate_time_left > 0)
12628   {
12629     game.timegate_time_left--;
12630
12631     if (game.timegate_time_left == 0)
12632       CloseAllOpenTimegates();
12633   }
12634
12635   if (game.lenses_time_left > 0)
12636   {
12637     game.lenses_time_left--;
12638
12639     if (game.lenses_time_left == 0)
12640       RedrawAllInvisibleElementsForLenses();
12641   }
12642
12643   if (game.magnify_time_left > 0)
12644   {
12645     game.magnify_time_left--;
12646
12647     if (game.magnify_time_left == 0)
12648       RedrawAllInvisibleElementsForMagnifier();
12649   }
12650
12651   for (i = 0; i < MAX_PLAYERS; i++)
12652   {
12653     struct PlayerInfo *player = &stored_player[i];
12654
12655     if (SHIELD_ON(player))
12656     {
12657       if (player->shield_deadly_time_left)
12658         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12659       else if (player->shield_normal_time_left)
12660         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12661     }
12662   }
12663
12664 #if USE_DELAYED_GFX_REDRAW
12665   SCAN_PLAYFIELD(x, y)
12666   {
12667     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12668     {
12669       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12670          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12671
12672       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12673         DrawLevelField(x, y);
12674
12675       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12676         DrawLevelFieldCrumbled(x, y);
12677
12678       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12679         DrawLevelFieldCrumbledNeighbours(x, y);
12680
12681       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12682         DrawTwinkleOnField(x, y);
12683     }
12684
12685     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12686   }
12687 #endif
12688
12689   DrawAllPlayers();
12690   PlayAllPlayersSound();
12691
12692   for (i = 0; i < MAX_PLAYERS; i++)
12693   {
12694     struct PlayerInfo *player = &stored_player[i];
12695
12696     if (player->show_envelope != 0 && (!player->active ||
12697                                        player->MovPos == 0))
12698     {
12699       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12700
12701       player->show_envelope = 0;
12702     }
12703   }
12704
12705   // use random number generator in every frame to make it less predictable
12706   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12707     RND(1);
12708
12709   mouse_action_last = mouse_action;
12710 }
12711
12712 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12713 {
12714   int min_x = x, min_y = y, max_x = x, max_y = y;
12715   int scr_fieldx = getScreenFieldSizeX();
12716   int scr_fieldy = getScreenFieldSizeY();
12717   int i;
12718
12719   for (i = 0; i < MAX_PLAYERS; i++)
12720   {
12721     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12722
12723     if (!stored_player[i].active || &stored_player[i] == player)
12724       continue;
12725
12726     min_x = MIN(min_x, jx);
12727     min_y = MIN(min_y, jy);
12728     max_x = MAX(max_x, jx);
12729     max_y = MAX(max_y, jy);
12730   }
12731
12732   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12733 }
12734
12735 static boolean AllPlayersInVisibleScreen(void)
12736 {
12737   int i;
12738
12739   for (i = 0; i < MAX_PLAYERS; i++)
12740   {
12741     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12742
12743     if (!stored_player[i].active)
12744       continue;
12745
12746     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12747       return FALSE;
12748   }
12749
12750   return TRUE;
12751 }
12752
12753 void ScrollLevel(int dx, int dy)
12754 {
12755   int scroll_offset = 2 * TILEX_VAR;
12756   int x, y;
12757
12758   BlitBitmap(drawto_field, drawto_field,
12759              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12760              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12761              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12762              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12763              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12764              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12765
12766   if (dx != 0)
12767   {
12768     x = (dx == 1 ? BX1 : BX2);
12769     for (y = BY1; y <= BY2; y++)
12770       DrawScreenField(x, y);
12771   }
12772
12773   if (dy != 0)
12774   {
12775     y = (dy == 1 ? BY1 : BY2);
12776     for (x = BX1; x <= BX2; x++)
12777       DrawScreenField(x, y);
12778   }
12779
12780   redraw_mask |= REDRAW_FIELD;
12781 }
12782
12783 static boolean canFallDown(struct PlayerInfo *player)
12784 {
12785   int jx = player->jx, jy = player->jy;
12786
12787   return (IN_LEV_FIELD(jx, jy + 1) &&
12788           (IS_FREE(jx, jy + 1) ||
12789            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12790           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12791           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12792 }
12793
12794 static boolean canPassField(int x, int y, int move_dir)
12795 {
12796   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12797   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12798   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12799   int nextx = x + dx;
12800   int nexty = y + dy;
12801   int element = Tile[x][y];
12802
12803   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12804           !CAN_MOVE(element) &&
12805           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12806           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12807           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12808 }
12809
12810 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12811 {
12812   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12813   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12814   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12815   int newx = x + dx;
12816   int newy = y + dy;
12817
12818   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12819           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12820           (IS_DIGGABLE(Tile[newx][newy]) ||
12821            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12822            canPassField(newx, newy, move_dir)));
12823 }
12824
12825 static void CheckGravityMovement(struct PlayerInfo *player)
12826 {
12827   if (player->gravity && !player->programmed_action)
12828   {
12829     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12830     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12831     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12832     int jx = player->jx, jy = player->jy;
12833     boolean player_is_moving_to_valid_field =
12834       (!player_is_snapping &&
12835        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12836         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12837     boolean player_can_fall_down = canFallDown(player);
12838
12839     if (player_can_fall_down &&
12840         !player_is_moving_to_valid_field)
12841       player->programmed_action = MV_DOWN;
12842   }
12843 }
12844
12845 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12846 {
12847   return CheckGravityMovement(player);
12848
12849   if (player->gravity && !player->programmed_action)
12850   {
12851     int jx = player->jx, jy = player->jy;
12852     boolean field_under_player_is_free =
12853       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12854     boolean player_is_standing_on_valid_field =
12855       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12856        (IS_WALKABLE(Tile[jx][jy]) &&
12857         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12858
12859     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12860       player->programmed_action = MV_DOWN;
12861   }
12862 }
12863
12864 /*
12865   MovePlayerOneStep()
12866   -----------------------------------------------------------------------------
12867   dx, dy:               direction (non-diagonal) to try to move the player to
12868   real_dx, real_dy:     direction as read from input device (can be diagonal)
12869 */
12870
12871 boolean MovePlayerOneStep(struct PlayerInfo *player,
12872                           int dx, int dy, int real_dx, int real_dy)
12873 {
12874   int jx = player->jx, jy = player->jy;
12875   int new_jx = jx + dx, new_jy = jy + dy;
12876   int can_move;
12877   boolean player_can_move = !player->cannot_move;
12878
12879   if (!player->active || (!dx && !dy))
12880     return MP_NO_ACTION;
12881
12882   player->MovDir = (dx < 0 ? MV_LEFT :
12883                     dx > 0 ? MV_RIGHT :
12884                     dy < 0 ? MV_UP :
12885                     dy > 0 ? MV_DOWN :  MV_NONE);
12886
12887   if (!IN_LEV_FIELD(new_jx, new_jy))
12888     return MP_NO_ACTION;
12889
12890   if (!player_can_move)
12891   {
12892     if (player->MovPos == 0)
12893     {
12894       player->is_moving = FALSE;
12895       player->is_digging = FALSE;
12896       player->is_collecting = FALSE;
12897       player->is_snapping = FALSE;
12898       player->is_pushing = FALSE;
12899     }
12900   }
12901
12902   if (!network.enabled && game.centered_player_nr == -1 &&
12903       !AllPlayersInSight(player, new_jx, new_jy))
12904     return MP_NO_ACTION;
12905
12906   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12907   if (can_move != MP_MOVING)
12908     return can_move;
12909
12910   // check if DigField() has caused relocation of the player
12911   if (player->jx != jx || player->jy != jy)
12912     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12913
12914   StorePlayer[jx][jy] = 0;
12915   player->last_jx = jx;
12916   player->last_jy = jy;
12917   player->jx = new_jx;
12918   player->jy = new_jy;
12919   StorePlayer[new_jx][new_jy] = player->element_nr;
12920
12921   if (player->move_delay_value_next != -1)
12922   {
12923     player->move_delay_value = player->move_delay_value_next;
12924     player->move_delay_value_next = -1;
12925   }
12926
12927   player->MovPos =
12928     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12929
12930   player->step_counter++;
12931
12932   PlayerVisit[jx][jy] = FrameCounter;
12933
12934   player->is_moving = TRUE;
12935
12936 #if 1
12937   // should better be called in MovePlayer(), but this breaks some tapes
12938   ScrollPlayer(player, SCROLL_INIT);
12939 #endif
12940
12941   return MP_MOVING;
12942 }
12943
12944 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12945 {
12946   int jx = player->jx, jy = player->jy;
12947   int old_jx = jx, old_jy = jy;
12948   int moved = MP_NO_ACTION;
12949
12950   if (!player->active)
12951     return FALSE;
12952
12953   if (!dx && !dy)
12954   {
12955     if (player->MovPos == 0)
12956     {
12957       player->is_moving = FALSE;
12958       player->is_digging = FALSE;
12959       player->is_collecting = FALSE;
12960       player->is_snapping = FALSE;
12961       player->is_pushing = FALSE;
12962     }
12963
12964     return FALSE;
12965   }
12966
12967   if (player->move_delay > 0)
12968     return FALSE;
12969
12970   player->move_delay = -1;              // set to "uninitialized" value
12971
12972   // store if player is automatically moved to next field
12973   player->is_auto_moving = (player->programmed_action != MV_NONE);
12974
12975   // remove the last programmed player action
12976   player->programmed_action = 0;
12977
12978   if (player->MovPos)
12979   {
12980     // should only happen if pre-1.2 tape recordings are played
12981     // this is only for backward compatibility
12982
12983     int original_move_delay_value = player->move_delay_value;
12984
12985 #if DEBUG
12986     Debug("game:playing:MovePlayer",
12987           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12988           tape.counter);
12989 #endif
12990
12991     // scroll remaining steps with finest movement resolution
12992     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12993
12994     while (player->MovPos)
12995     {
12996       ScrollPlayer(player, SCROLL_GO_ON);
12997       ScrollScreen(NULL, SCROLL_GO_ON);
12998
12999       AdvanceFrameAndPlayerCounters(player->index_nr);
13000
13001       DrawAllPlayers();
13002       BackToFront_WithFrameDelay(0);
13003     }
13004
13005     player->move_delay_value = original_move_delay_value;
13006   }
13007
13008   player->is_active = FALSE;
13009
13010   if (player->last_move_dir & MV_HORIZONTAL)
13011   {
13012     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13013       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13014   }
13015   else
13016   {
13017     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13018       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13019   }
13020
13021   if (!moved && !player->is_active)
13022   {
13023     player->is_moving = FALSE;
13024     player->is_digging = FALSE;
13025     player->is_collecting = FALSE;
13026     player->is_snapping = FALSE;
13027     player->is_pushing = FALSE;
13028   }
13029
13030   jx = player->jx;
13031   jy = player->jy;
13032
13033   if (moved & MP_MOVING && !ScreenMovPos &&
13034       (player->index_nr == game.centered_player_nr ||
13035        game.centered_player_nr == -1))
13036   {
13037     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13038
13039     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13040     {
13041       // actual player has left the screen -- scroll in that direction
13042       if (jx != old_jx)         // player has moved horizontally
13043         scroll_x += (jx - old_jx);
13044       else                      // player has moved vertically
13045         scroll_y += (jy - old_jy);
13046     }
13047     else
13048     {
13049       int offset_raw = game.scroll_delay_value;
13050
13051       if (jx != old_jx)         // player has moved horizontally
13052       {
13053         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13054         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13055         int new_scroll_x = jx - MIDPOSX + offset_x;
13056
13057         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13058             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13059           scroll_x = new_scroll_x;
13060
13061         // don't scroll over playfield boundaries
13062         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13063
13064         // don't scroll more than one field at a time
13065         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13066
13067         // don't scroll against the player's moving direction
13068         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13069             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13070           scroll_x = old_scroll_x;
13071       }
13072       else                      // player has moved vertically
13073       {
13074         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13075         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13076         int new_scroll_y = jy - MIDPOSY + offset_y;
13077
13078         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13079             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13080           scroll_y = new_scroll_y;
13081
13082         // don't scroll over playfield boundaries
13083         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13084
13085         // don't scroll more than one field at a time
13086         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13087
13088         // don't scroll against the player's moving direction
13089         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13090             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13091           scroll_y = old_scroll_y;
13092       }
13093     }
13094
13095     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13096     {
13097       if (!network.enabled && game.centered_player_nr == -1 &&
13098           !AllPlayersInVisibleScreen())
13099       {
13100         scroll_x = old_scroll_x;
13101         scroll_y = old_scroll_y;
13102       }
13103       else
13104       {
13105         ScrollScreen(player, SCROLL_INIT);
13106         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13107       }
13108     }
13109   }
13110
13111   player->StepFrame = 0;
13112
13113   if (moved & MP_MOVING)
13114   {
13115     if (old_jx != jx && old_jy == jy)
13116       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13117     else if (old_jx == jx && old_jy != jy)
13118       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13119
13120     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13121
13122     player->last_move_dir = player->MovDir;
13123     player->is_moving = TRUE;
13124     player->is_snapping = FALSE;
13125     player->is_switching = FALSE;
13126     player->is_dropping = FALSE;
13127     player->is_dropping_pressed = FALSE;
13128     player->drop_pressed_delay = 0;
13129
13130 #if 0
13131     // should better be called here than above, but this breaks some tapes
13132     ScrollPlayer(player, SCROLL_INIT);
13133 #endif
13134   }
13135   else
13136   {
13137     CheckGravityMovementWhenNotMoving(player);
13138
13139     player->is_moving = FALSE;
13140
13141     /* at this point, the player is allowed to move, but cannot move right now
13142        (e.g. because of something blocking the way) -- ensure that the player
13143        is also allowed to move in the next frame (in old versions before 3.1.1,
13144        the player was forced to wait again for eight frames before next try) */
13145
13146     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13147       player->move_delay = 0;   // allow direct movement in the next frame
13148   }
13149
13150   if (player->move_delay == -1)         // not yet initialized by DigField()
13151     player->move_delay = player->move_delay_value;
13152
13153   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13154   {
13155     TestIfPlayerTouchesBadThing(jx, jy);
13156     TestIfPlayerTouchesCustomElement(jx, jy);
13157   }
13158
13159   if (!player->active)
13160     RemovePlayer(player);
13161
13162   return moved;
13163 }
13164
13165 void ScrollPlayer(struct PlayerInfo *player, int mode)
13166 {
13167   int jx = player->jx, jy = player->jy;
13168   int last_jx = player->last_jx, last_jy = player->last_jy;
13169   int move_stepsize = TILEX / player->move_delay_value;
13170
13171   if (!player->active)
13172     return;
13173
13174   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13175     return;
13176
13177   if (mode == SCROLL_INIT)
13178   {
13179     player->actual_frame_counter = FrameCounter;
13180     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13181
13182     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13183         Tile[last_jx][last_jy] == EL_EMPTY)
13184     {
13185       int last_field_block_delay = 0;   // start with no blocking at all
13186       int block_delay_adjustment = player->block_delay_adjustment;
13187
13188       // if player blocks last field, add delay for exactly one move
13189       if (player->block_last_field)
13190       {
13191         last_field_block_delay += player->move_delay_value;
13192
13193         // when blocking enabled, prevent moving up despite gravity
13194         if (player->gravity && player->MovDir == MV_UP)
13195           block_delay_adjustment = -1;
13196       }
13197
13198       // add block delay adjustment (also possible when not blocking)
13199       last_field_block_delay += block_delay_adjustment;
13200
13201       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13202       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13203     }
13204
13205     if (player->MovPos != 0)    // player has not yet reached destination
13206       return;
13207   }
13208   else if (!FrameReached(&player->actual_frame_counter, 1))
13209     return;
13210
13211   if (player->MovPos != 0)
13212   {
13213     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13214     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13215
13216     // before DrawPlayer() to draw correct player graphic for this case
13217     if (player->MovPos == 0)
13218       CheckGravityMovement(player);
13219   }
13220
13221   if (player->MovPos == 0)      // player reached destination field
13222   {
13223     if (player->move_delay_reset_counter > 0)
13224     {
13225       player->move_delay_reset_counter--;
13226
13227       if (player->move_delay_reset_counter == 0)
13228       {
13229         // continue with normal speed after quickly moving through gate
13230         HALVE_PLAYER_SPEED(player);
13231
13232         // be able to make the next move without delay
13233         player->move_delay = 0;
13234       }
13235     }
13236
13237     player->last_jx = jx;
13238     player->last_jy = jy;
13239
13240     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13241         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13242         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13243         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13244         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13245         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13246         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13247         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13248     {
13249       ExitPlayer(player);
13250
13251       if (game.players_still_needed == 0 &&
13252           (game.friends_still_needed == 0 ||
13253            IS_SP_ELEMENT(Tile[jx][jy])))
13254         LevelSolved();
13255     }
13256
13257     // this breaks one level: "machine", level 000
13258     {
13259       int move_direction = player->MovDir;
13260       int enter_side = MV_DIR_OPPOSITE(move_direction);
13261       int leave_side = move_direction;
13262       int old_jx = last_jx;
13263       int old_jy = last_jy;
13264       int old_element = Tile[old_jx][old_jy];
13265       int new_element = Tile[jx][jy];
13266
13267       if (IS_CUSTOM_ELEMENT(old_element))
13268         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13269                                    CE_LEFT_BY_PLAYER,
13270                                    player->index_bit, leave_side);
13271
13272       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13273                                           CE_PLAYER_LEAVES_X,
13274                                           player->index_bit, leave_side);
13275
13276       if (IS_CUSTOM_ELEMENT(new_element))
13277         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13278                                    player->index_bit, enter_side);
13279
13280       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13281                                           CE_PLAYER_ENTERS_X,
13282                                           player->index_bit, enter_side);
13283
13284       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13285                                         CE_MOVE_OF_X, move_direction);
13286     }
13287
13288     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13289     {
13290       TestIfPlayerTouchesBadThing(jx, jy);
13291       TestIfPlayerTouchesCustomElement(jx, jy);
13292
13293       /* needed because pushed element has not yet reached its destination,
13294          so it would trigger a change event at its previous field location */
13295       if (!player->is_pushing)
13296         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13297
13298       if (level.finish_dig_collect &&
13299           (player->is_digging || player->is_collecting))
13300       {
13301         int last_element = player->last_removed_element;
13302         int move_direction = player->MovDir;
13303         int enter_side = MV_DIR_OPPOSITE(move_direction);
13304         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13305                             CE_PLAYER_COLLECTS_X);
13306
13307         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13308                                             player->index_bit, enter_side);
13309
13310         player->last_removed_element = EL_UNDEFINED;
13311       }
13312
13313       if (!player->active)
13314         RemovePlayer(player);
13315     }
13316
13317     if (level.use_step_counter)
13318       CheckLevelTime_StepCounter();
13319
13320     if (tape.single_step && tape.recording && !tape.pausing &&
13321         !player->programmed_action)
13322       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13323
13324     if (!player->programmed_action)
13325       CheckSaveEngineSnapshot(player);
13326   }
13327 }
13328
13329 void ScrollScreen(struct PlayerInfo *player, int mode)
13330 {
13331   static unsigned int screen_frame_counter = 0;
13332
13333   if (mode == SCROLL_INIT)
13334   {
13335     // set scrolling step size according to actual player's moving speed
13336     ScrollStepSize = TILEX / player->move_delay_value;
13337
13338     screen_frame_counter = FrameCounter;
13339     ScreenMovDir = player->MovDir;
13340     ScreenMovPos = player->MovPos;
13341     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13342     return;
13343   }
13344   else if (!FrameReached(&screen_frame_counter, 1))
13345     return;
13346
13347   if (ScreenMovPos)
13348   {
13349     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13350     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13351     redraw_mask |= REDRAW_FIELD;
13352   }
13353   else
13354     ScreenMovDir = MV_NONE;
13355 }
13356
13357 void CheckNextToConditions(int x, int y)
13358 {
13359   int element = Tile[x][y];
13360
13361   if (IS_PLAYER(x, y))
13362     TestIfPlayerNextToCustomElement(x, y);
13363
13364   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13365       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13366     TestIfElementNextToCustomElement(x, y);
13367 }
13368
13369 void TestIfPlayerNextToCustomElement(int x, int y)
13370 {
13371   static int xy[4][2] =
13372   {
13373     { 0, -1 },
13374     { -1, 0 },
13375     { +1, 0 },
13376     { 0, +1 }
13377   };
13378   static int trigger_sides[4][2] =
13379   {
13380     // center side       border side
13381     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13382     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13383     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13384     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13385   };
13386   int i;
13387
13388   if (!IS_PLAYER(x, y))
13389     return;
13390
13391   struct PlayerInfo *player = PLAYERINFO(x, y);
13392
13393   if (player->is_moving)
13394     return;
13395
13396   for (i = 0; i < NUM_DIRECTIONS; i++)
13397   {
13398     int xx = x + xy[i][0];
13399     int yy = y + xy[i][1];
13400     int border_side = trigger_sides[i][1];
13401     int border_element;
13402
13403     if (!IN_LEV_FIELD(xx, yy))
13404       continue;
13405
13406     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13407       continue;         // center and border element not connected
13408
13409     border_element = Tile[xx][yy];
13410
13411     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13412                                player->index_bit, border_side);
13413     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13414                                         CE_PLAYER_NEXT_TO_X,
13415                                         player->index_bit, border_side);
13416
13417     /* use player element that is initially defined in the level playfield,
13418        not the player element that corresponds to the runtime player number
13419        (example: a level that contains EL_PLAYER_3 as the only player would
13420        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13421
13422     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13423                              CE_NEXT_TO_X, border_side);
13424   }
13425 }
13426
13427 void TestIfPlayerTouchesCustomElement(int x, int y)
13428 {
13429   static int xy[4][2] =
13430   {
13431     { 0, -1 },
13432     { -1, 0 },
13433     { +1, 0 },
13434     { 0, +1 }
13435   };
13436   static int trigger_sides[4][2] =
13437   {
13438     // center side       border side
13439     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13440     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13441     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13442     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13443   };
13444   static int touch_dir[4] =
13445   {
13446     MV_LEFT | MV_RIGHT,
13447     MV_UP   | MV_DOWN,
13448     MV_UP   | MV_DOWN,
13449     MV_LEFT | MV_RIGHT
13450   };
13451   int center_element = Tile[x][y];      // should always be non-moving!
13452   int i;
13453
13454   for (i = 0; i < NUM_DIRECTIONS; i++)
13455   {
13456     int xx = x + xy[i][0];
13457     int yy = y + xy[i][1];
13458     int center_side = trigger_sides[i][0];
13459     int border_side = trigger_sides[i][1];
13460     int border_element;
13461
13462     if (!IN_LEV_FIELD(xx, yy))
13463       continue;
13464
13465     if (IS_PLAYER(x, y))                // player found at center element
13466     {
13467       struct PlayerInfo *player = PLAYERINFO(x, y);
13468
13469       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13470         border_element = Tile[xx][yy];          // may be moving!
13471       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13472         border_element = Tile[xx][yy];
13473       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13474         border_element = MovingOrBlocked2Element(xx, yy);
13475       else
13476         continue;               // center and border element do not touch
13477
13478       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13479                                  player->index_bit, border_side);
13480       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13481                                           CE_PLAYER_TOUCHES_X,
13482                                           player->index_bit, border_side);
13483
13484       {
13485         /* use player element that is initially defined in the level playfield,
13486            not the player element that corresponds to the runtime player number
13487            (example: a level that contains EL_PLAYER_3 as the only player would
13488            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13489         int player_element = PLAYERINFO(x, y)->initial_element;
13490
13491         CheckElementChangeBySide(xx, yy, border_element, player_element,
13492                                  CE_TOUCHING_X, border_side);
13493       }
13494     }
13495     else if (IS_PLAYER(xx, yy))         // player found at border element
13496     {
13497       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13498
13499       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13500       {
13501         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13502           continue;             // center and border element do not touch
13503       }
13504
13505       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13506                                  player->index_bit, center_side);
13507       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13508                                           CE_PLAYER_TOUCHES_X,
13509                                           player->index_bit, center_side);
13510
13511       {
13512         /* use player element that is initially defined in the level playfield,
13513            not the player element that corresponds to the runtime player number
13514            (example: a level that contains EL_PLAYER_3 as the only player would
13515            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13516         int player_element = PLAYERINFO(xx, yy)->initial_element;
13517
13518         CheckElementChangeBySide(x, y, center_element, player_element,
13519                                  CE_TOUCHING_X, center_side);
13520       }
13521
13522       break;
13523     }
13524   }
13525 }
13526
13527 void TestIfElementNextToCustomElement(int x, int y)
13528 {
13529   static int xy[4][2] =
13530   {
13531     { 0, -1 },
13532     { -1, 0 },
13533     { +1, 0 },
13534     { 0, +1 }
13535   };
13536   static int trigger_sides[4][2] =
13537   {
13538     // center side      border side
13539     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13540     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13541     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13542     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13543   };
13544   int center_element = Tile[x][y];      // should always be non-moving!
13545   int i;
13546
13547   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13548     return;
13549
13550   for (i = 0; i < NUM_DIRECTIONS; i++)
13551   {
13552     int xx = x + xy[i][0];
13553     int yy = y + xy[i][1];
13554     int border_side = trigger_sides[i][1];
13555     int border_element;
13556
13557     if (!IN_LEV_FIELD(xx, yy))
13558       continue;
13559
13560     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13561       continue;                 // center and border element not connected
13562
13563     border_element = Tile[xx][yy];
13564
13565     // check for change of center element (but change it only once)
13566     if (CheckElementChangeBySide(x, y, center_element, border_element,
13567                                  CE_NEXT_TO_X, border_side))
13568       break;
13569   }
13570 }
13571
13572 void TestIfElementTouchesCustomElement(int x, int y)
13573 {
13574   static int xy[4][2] =
13575   {
13576     { 0, -1 },
13577     { -1, 0 },
13578     { +1, 0 },
13579     { 0, +1 }
13580   };
13581   static int trigger_sides[4][2] =
13582   {
13583     // center side      border side
13584     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13585     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13586     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13587     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13588   };
13589   static int touch_dir[4] =
13590   {
13591     MV_LEFT | MV_RIGHT,
13592     MV_UP   | MV_DOWN,
13593     MV_UP   | MV_DOWN,
13594     MV_LEFT | MV_RIGHT
13595   };
13596   boolean change_center_element = FALSE;
13597   int center_element = Tile[x][y];      // should always be non-moving!
13598   int border_element_old[NUM_DIRECTIONS];
13599   int i;
13600
13601   for (i = 0; i < NUM_DIRECTIONS; i++)
13602   {
13603     int xx = x + xy[i][0];
13604     int yy = y + xy[i][1];
13605     int border_element;
13606
13607     border_element_old[i] = -1;
13608
13609     if (!IN_LEV_FIELD(xx, yy))
13610       continue;
13611
13612     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13613       border_element = Tile[xx][yy];    // may be moving!
13614     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13615       border_element = Tile[xx][yy];
13616     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13617       border_element = MovingOrBlocked2Element(xx, yy);
13618     else
13619       continue;                 // center and border element do not touch
13620
13621     border_element_old[i] = border_element;
13622   }
13623
13624   for (i = 0; i < NUM_DIRECTIONS; i++)
13625   {
13626     int xx = x + xy[i][0];
13627     int yy = y + xy[i][1];
13628     int center_side = trigger_sides[i][0];
13629     int border_element = border_element_old[i];
13630
13631     if (border_element == -1)
13632       continue;
13633
13634     // check for change of border element
13635     CheckElementChangeBySide(xx, yy, border_element, center_element,
13636                              CE_TOUCHING_X, center_side);
13637
13638     // (center element cannot be player, so we dont have to check this here)
13639   }
13640
13641   for (i = 0; i < NUM_DIRECTIONS; i++)
13642   {
13643     int xx = x + xy[i][0];
13644     int yy = y + xy[i][1];
13645     int border_side = trigger_sides[i][1];
13646     int border_element = border_element_old[i];
13647
13648     if (border_element == -1)
13649       continue;
13650
13651     // check for change of center element (but change it only once)
13652     if (!change_center_element)
13653       change_center_element =
13654         CheckElementChangeBySide(x, y, center_element, border_element,
13655                                  CE_TOUCHING_X, border_side);
13656
13657     if (IS_PLAYER(xx, yy))
13658     {
13659       /* use player element that is initially defined in the level playfield,
13660          not the player element that corresponds to the runtime player number
13661          (example: a level that contains EL_PLAYER_3 as the only player would
13662          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13663       int player_element = PLAYERINFO(xx, yy)->initial_element;
13664
13665       CheckElementChangeBySide(x, y, center_element, player_element,
13666                                CE_TOUCHING_X, border_side);
13667     }
13668   }
13669 }
13670
13671 void TestIfElementHitsCustomElement(int x, int y, int direction)
13672 {
13673   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13674   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13675   int hitx = x + dx, hity = y + dy;
13676   int hitting_element = Tile[x][y];
13677   int touched_element;
13678
13679   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13680     return;
13681
13682   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13683                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13684
13685   if (IN_LEV_FIELD(hitx, hity))
13686   {
13687     int opposite_direction = MV_DIR_OPPOSITE(direction);
13688     int hitting_side = direction;
13689     int touched_side = opposite_direction;
13690     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13691                           MovDir[hitx][hity] != direction ||
13692                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13693
13694     object_hit = TRUE;
13695
13696     if (object_hit)
13697     {
13698       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13699                                CE_HITTING_X, touched_side);
13700
13701       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13702                                CE_HIT_BY_X, hitting_side);
13703
13704       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13705                                CE_HIT_BY_SOMETHING, opposite_direction);
13706
13707       if (IS_PLAYER(hitx, hity))
13708       {
13709         /* use player element that is initially defined in the level playfield,
13710            not the player element that corresponds to the runtime player number
13711            (example: a level that contains EL_PLAYER_3 as the only player would
13712            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13713         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13714
13715         CheckElementChangeBySide(x, y, hitting_element, player_element,
13716                                  CE_HITTING_X, touched_side);
13717       }
13718     }
13719   }
13720
13721   // "hitting something" is also true when hitting the playfield border
13722   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13723                            CE_HITTING_SOMETHING, direction);
13724 }
13725
13726 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13727 {
13728   int i, kill_x = -1, kill_y = -1;
13729
13730   int bad_element = -1;
13731   static int test_xy[4][2] =
13732   {
13733     { 0, -1 },
13734     { -1, 0 },
13735     { +1, 0 },
13736     { 0, +1 }
13737   };
13738   static int test_dir[4] =
13739   {
13740     MV_UP,
13741     MV_LEFT,
13742     MV_RIGHT,
13743     MV_DOWN
13744   };
13745
13746   for (i = 0; i < NUM_DIRECTIONS; i++)
13747   {
13748     int test_x, test_y, test_move_dir, test_element;
13749
13750     test_x = good_x + test_xy[i][0];
13751     test_y = good_y + test_xy[i][1];
13752
13753     if (!IN_LEV_FIELD(test_x, test_y))
13754       continue;
13755
13756     test_move_dir =
13757       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13758
13759     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13760
13761     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13762        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13763     */
13764     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13765         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13766     {
13767       kill_x = test_x;
13768       kill_y = test_y;
13769       bad_element = test_element;
13770
13771       break;
13772     }
13773   }
13774
13775   if (kill_x != -1 || kill_y != -1)
13776   {
13777     if (IS_PLAYER(good_x, good_y))
13778     {
13779       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13780
13781       if (player->shield_deadly_time_left > 0 &&
13782           !IS_INDESTRUCTIBLE(bad_element))
13783         Bang(kill_x, kill_y);
13784       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13785         KillPlayer(player);
13786     }
13787     else
13788       Bang(good_x, good_y);
13789   }
13790 }
13791
13792 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13793 {
13794   int i, kill_x = -1, kill_y = -1;
13795   int bad_element = Tile[bad_x][bad_y];
13796   static int test_xy[4][2] =
13797   {
13798     { 0, -1 },
13799     { -1, 0 },
13800     { +1, 0 },
13801     { 0, +1 }
13802   };
13803   static int touch_dir[4] =
13804   {
13805     MV_LEFT | MV_RIGHT,
13806     MV_UP   | MV_DOWN,
13807     MV_UP   | MV_DOWN,
13808     MV_LEFT | MV_RIGHT
13809   };
13810   static int test_dir[4] =
13811   {
13812     MV_UP,
13813     MV_LEFT,
13814     MV_RIGHT,
13815     MV_DOWN
13816   };
13817
13818   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13819     return;
13820
13821   for (i = 0; i < NUM_DIRECTIONS; i++)
13822   {
13823     int test_x, test_y, test_move_dir, test_element;
13824
13825     test_x = bad_x + test_xy[i][0];
13826     test_y = bad_y + test_xy[i][1];
13827
13828     if (!IN_LEV_FIELD(test_x, test_y))
13829       continue;
13830
13831     test_move_dir =
13832       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13833
13834     test_element = Tile[test_x][test_y];
13835
13836     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13837        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13838     */
13839     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13840         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13841     {
13842       // good thing is player or penguin that does not move away
13843       if (IS_PLAYER(test_x, test_y))
13844       {
13845         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13846
13847         if (bad_element == EL_ROBOT && player->is_moving)
13848           continue;     // robot does not kill player if he is moving
13849
13850         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13851         {
13852           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13853             continue;           // center and border element do not touch
13854         }
13855
13856         kill_x = test_x;
13857         kill_y = test_y;
13858
13859         break;
13860       }
13861       else if (test_element == EL_PENGUIN)
13862       {
13863         kill_x = test_x;
13864         kill_y = test_y;
13865
13866         break;
13867       }
13868     }
13869   }
13870
13871   if (kill_x != -1 || kill_y != -1)
13872   {
13873     if (IS_PLAYER(kill_x, kill_y))
13874     {
13875       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13876
13877       if (player->shield_deadly_time_left > 0 &&
13878           !IS_INDESTRUCTIBLE(bad_element))
13879         Bang(bad_x, bad_y);
13880       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13881         KillPlayer(player);
13882     }
13883     else
13884       Bang(kill_x, kill_y);
13885   }
13886 }
13887
13888 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13889 {
13890   int bad_element = Tile[bad_x][bad_y];
13891   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13892   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13893   int test_x = bad_x + dx, test_y = bad_y + dy;
13894   int test_move_dir, test_element;
13895   int kill_x = -1, kill_y = -1;
13896
13897   if (!IN_LEV_FIELD(test_x, test_y))
13898     return;
13899
13900   test_move_dir =
13901     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13902
13903   test_element = Tile[test_x][test_y];
13904
13905   if (test_move_dir != bad_move_dir)
13906   {
13907     // good thing can be player or penguin that does not move away
13908     if (IS_PLAYER(test_x, test_y))
13909     {
13910       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13911
13912       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13913          player as being hit when he is moving towards the bad thing, because
13914          the "get hit by" condition would be lost after the player stops) */
13915       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13916         return;         // player moves away from bad thing
13917
13918       kill_x = test_x;
13919       kill_y = test_y;
13920     }
13921     else if (test_element == EL_PENGUIN)
13922     {
13923       kill_x = test_x;
13924       kill_y = test_y;
13925     }
13926   }
13927
13928   if (kill_x != -1 || kill_y != -1)
13929   {
13930     if (IS_PLAYER(kill_x, kill_y))
13931     {
13932       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13933
13934       if (player->shield_deadly_time_left > 0 &&
13935           !IS_INDESTRUCTIBLE(bad_element))
13936         Bang(bad_x, bad_y);
13937       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13938         KillPlayer(player);
13939     }
13940     else
13941       Bang(kill_x, kill_y);
13942   }
13943 }
13944
13945 void TestIfPlayerTouchesBadThing(int x, int y)
13946 {
13947   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13948 }
13949
13950 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13951 {
13952   TestIfGoodThingHitsBadThing(x, y, move_dir);
13953 }
13954
13955 void TestIfBadThingTouchesPlayer(int x, int y)
13956 {
13957   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13958 }
13959
13960 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13961 {
13962   TestIfBadThingHitsGoodThing(x, y, move_dir);
13963 }
13964
13965 void TestIfFriendTouchesBadThing(int x, int y)
13966 {
13967   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13968 }
13969
13970 void TestIfBadThingTouchesFriend(int x, int y)
13971 {
13972   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13973 }
13974
13975 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13976 {
13977   int i, kill_x = bad_x, kill_y = bad_y;
13978   static int xy[4][2] =
13979   {
13980     { 0, -1 },
13981     { -1, 0 },
13982     { +1, 0 },
13983     { 0, +1 }
13984   };
13985
13986   for (i = 0; i < NUM_DIRECTIONS; i++)
13987   {
13988     int x, y, element;
13989
13990     x = bad_x + xy[i][0];
13991     y = bad_y + xy[i][1];
13992     if (!IN_LEV_FIELD(x, y))
13993       continue;
13994
13995     element = Tile[x][y];
13996     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13997         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13998     {
13999       kill_x = x;
14000       kill_y = y;
14001       break;
14002     }
14003   }
14004
14005   if (kill_x != bad_x || kill_y != bad_y)
14006     Bang(bad_x, bad_y);
14007 }
14008
14009 void KillPlayer(struct PlayerInfo *player)
14010 {
14011   int jx = player->jx, jy = player->jy;
14012
14013   if (!player->active)
14014     return;
14015
14016 #if 0
14017   Debug("game:playing:KillPlayer",
14018         "0: killed == %d, active == %d, reanimated == %d",
14019         player->killed, player->active, player->reanimated);
14020 #endif
14021
14022   /* the following code was introduced to prevent an infinite loop when calling
14023      -> Bang()
14024      -> CheckTriggeredElementChangeExt()
14025      -> ExecuteCustomElementAction()
14026      -> KillPlayer()
14027      -> (infinitely repeating the above sequence of function calls)
14028      which occurs when killing the player while having a CE with the setting
14029      "kill player X when explosion of <player X>"; the solution using a new
14030      field "player->killed" was chosen for backwards compatibility, although
14031      clever use of the fields "player->active" etc. would probably also work */
14032 #if 1
14033   if (player->killed)
14034     return;
14035 #endif
14036
14037   player->killed = TRUE;
14038
14039   // remove accessible field at the player's position
14040   Tile[jx][jy] = EL_EMPTY;
14041
14042   // deactivate shield (else Bang()/Explode() would not work right)
14043   player->shield_normal_time_left = 0;
14044   player->shield_deadly_time_left = 0;
14045
14046 #if 0
14047   Debug("game:playing:KillPlayer",
14048         "1: killed == %d, active == %d, reanimated == %d",
14049         player->killed, player->active, player->reanimated);
14050 #endif
14051
14052   Bang(jx, jy);
14053
14054 #if 0
14055   Debug("game:playing:KillPlayer",
14056         "2: killed == %d, active == %d, reanimated == %d",
14057         player->killed, player->active, player->reanimated);
14058 #endif
14059
14060   if (player->reanimated)       // killed player may have been reanimated
14061     player->killed = player->reanimated = FALSE;
14062   else
14063     BuryPlayer(player);
14064 }
14065
14066 static void KillPlayerUnlessEnemyProtected(int x, int y)
14067 {
14068   if (!PLAYER_ENEMY_PROTECTED(x, y))
14069     KillPlayer(PLAYERINFO(x, y));
14070 }
14071
14072 static void KillPlayerUnlessExplosionProtected(int x, int y)
14073 {
14074   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14075     KillPlayer(PLAYERINFO(x, y));
14076 }
14077
14078 void BuryPlayer(struct PlayerInfo *player)
14079 {
14080   int jx = player->jx, jy = player->jy;
14081
14082   if (!player->active)
14083     return;
14084
14085   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14086   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14087
14088   RemovePlayer(player);
14089
14090   player->buried = TRUE;
14091
14092   if (game.all_players_gone)
14093     game.GameOver = TRUE;
14094 }
14095
14096 void RemovePlayer(struct PlayerInfo *player)
14097 {
14098   int jx = player->jx, jy = player->jy;
14099   int i, found = FALSE;
14100
14101   player->present = FALSE;
14102   player->active = FALSE;
14103
14104   // required for some CE actions (even if the player is not active anymore)
14105   player->MovPos = 0;
14106
14107   if (!ExplodeField[jx][jy])
14108     StorePlayer[jx][jy] = 0;
14109
14110   if (player->is_moving)
14111     TEST_DrawLevelField(player->last_jx, player->last_jy);
14112
14113   for (i = 0; i < MAX_PLAYERS; i++)
14114     if (stored_player[i].active)
14115       found = TRUE;
14116
14117   if (!found)
14118   {
14119     game.all_players_gone = TRUE;
14120     game.GameOver = TRUE;
14121   }
14122
14123   game.exit_x = game.robot_wheel_x = jx;
14124   game.exit_y = game.robot_wheel_y = jy;
14125 }
14126
14127 void ExitPlayer(struct PlayerInfo *player)
14128 {
14129   DrawPlayer(player);   // needed here only to cleanup last field
14130   RemovePlayer(player);
14131
14132   if (game.players_still_needed > 0)
14133     game.players_still_needed--;
14134 }
14135
14136 static void SetFieldForSnapping(int x, int y, int element, int direction,
14137                                 int player_index_bit)
14138 {
14139   struct ElementInfo *ei = &element_info[element];
14140   int direction_bit = MV_DIR_TO_BIT(direction);
14141   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14142   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14143                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14144
14145   Tile[x][y] = EL_ELEMENT_SNAPPING;
14146   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14147   MovDir[x][y] = direction;
14148   Store[x][y] = element;
14149   Store2[x][y] = player_index_bit;
14150
14151   ResetGfxAnimation(x, y);
14152
14153   GfxElement[x][y] = element;
14154   GfxAction[x][y] = action;
14155   GfxDir[x][y] = direction;
14156   GfxFrame[x][y] = -1;
14157 }
14158
14159 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14160                                    int player_index_bit)
14161 {
14162   TestIfElementTouchesCustomElement(x, y);      // for empty space
14163
14164   if (level.finish_dig_collect)
14165   {
14166     int dig_side = MV_DIR_OPPOSITE(direction);
14167     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14168                         CE_PLAYER_COLLECTS_X);
14169
14170     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14171                                         player_index_bit, dig_side);
14172     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14173                                         player_index_bit, dig_side);
14174   }
14175 }
14176
14177 /*
14178   =============================================================================
14179   checkDiagonalPushing()
14180   -----------------------------------------------------------------------------
14181   check if diagonal input device direction results in pushing of object
14182   (by checking if the alternative direction is walkable, diggable, ...)
14183   =============================================================================
14184 */
14185
14186 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14187                                     int x, int y, int real_dx, int real_dy)
14188 {
14189   int jx, jy, dx, dy, xx, yy;
14190
14191   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14192     return TRUE;
14193
14194   // diagonal direction: check alternative direction
14195   jx = player->jx;
14196   jy = player->jy;
14197   dx = x - jx;
14198   dy = y - jy;
14199   xx = jx + (dx == 0 ? real_dx : 0);
14200   yy = jy + (dy == 0 ? real_dy : 0);
14201
14202   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14203 }
14204
14205 /*
14206   =============================================================================
14207   DigField()
14208   -----------------------------------------------------------------------------
14209   x, y:                 field next to player (non-diagonal) to try to dig to
14210   real_dx, real_dy:     direction as read from input device (can be diagonal)
14211   =============================================================================
14212 */
14213
14214 static int DigField(struct PlayerInfo *player,
14215                     int oldx, int oldy, int x, int y,
14216                     int real_dx, int real_dy, int mode)
14217 {
14218   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14219   boolean player_was_pushing = player->is_pushing;
14220   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14221   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14222   int jx = oldx, jy = oldy;
14223   int dx = x - jx, dy = y - jy;
14224   int nextx = x + dx, nexty = y + dy;
14225   int move_direction = (dx == -1 ? MV_LEFT  :
14226                         dx == +1 ? MV_RIGHT :
14227                         dy == -1 ? MV_UP    :
14228                         dy == +1 ? MV_DOWN  : MV_NONE);
14229   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14230   int dig_side = MV_DIR_OPPOSITE(move_direction);
14231   int old_element = Tile[jx][jy];
14232   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14233   int collect_count;
14234
14235   if (is_player)                // function can also be called by EL_PENGUIN
14236   {
14237     if (player->MovPos == 0)
14238     {
14239       player->is_digging = FALSE;
14240       player->is_collecting = FALSE;
14241     }
14242
14243     if (player->MovPos == 0)    // last pushing move finished
14244       player->is_pushing = FALSE;
14245
14246     if (mode == DF_NO_PUSH)     // player just stopped pushing
14247     {
14248       player->is_switching = FALSE;
14249       player->push_delay = -1;
14250
14251       return MP_NO_ACTION;
14252     }
14253   }
14254   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14255     old_element = Back[jx][jy];
14256
14257   // in case of element dropped at player position, check background
14258   else if (Back[jx][jy] != EL_EMPTY &&
14259            game.engine_version >= VERSION_IDENT(2,2,0,0))
14260     old_element = Back[jx][jy];
14261
14262   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14263     return MP_NO_ACTION;        // field has no opening in this direction
14264
14265   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14266     return MP_NO_ACTION;        // field has no opening in this direction
14267
14268   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14269   {
14270     SplashAcid(x, y);
14271
14272     Tile[jx][jy] = player->artwork_element;
14273     InitMovingField(jx, jy, MV_DOWN);
14274     Store[jx][jy] = EL_ACID;
14275     ContinueMoving(jx, jy);
14276     BuryPlayer(player);
14277
14278     return MP_DONT_RUN_INTO;
14279   }
14280
14281   if (player_can_move && DONT_RUN_INTO(element))
14282   {
14283     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14284
14285     return MP_DONT_RUN_INTO;
14286   }
14287
14288   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14289     return MP_NO_ACTION;
14290
14291   collect_count = element_info[element].collect_count_initial;
14292
14293   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14294     return MP_NO_ACTION;
14295
14296   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14297     player_can_move = player_can_move_or_snap;
14298
14299   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14300       game.engine_version >= VERSION_IDENT(2,2,0,0))
14301   {
14302     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14303                                player->index_bit, dig_side);
14304     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14305                                         player->index_bit, dig_side);
14306
14307     if (element == EL_DC_LANDMINE)
14308       Bang(x, y);
14309
14310     if (Tile[x][y] != element)          // field changed by snapping
14311       return MP_ACTION;
14312
14313     return MP_NO_ACTION;
14314   }
14315
14316   if (player->gravity && is_player && !player->is_auto_moving &&
14317       canFallDown(player) && move_direction != MV_DOWN &&
14318       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14319     return MP_NO_ACTION;        // player cannot walk here due to gravity
14320
14321   if (player_can_move &&
14322       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14323   {
14324     int sound_element = SND_ELEMENT(element);
14325     int sound_action = ACTION_WALKING;
14326
14327     if (IS_RND_GATE(element))
14328     {
14329       if (!player->key[RND_GATE_NR(element)])
14330         return MP_NO_ACTION;
14331     }
14332     else if (IS_RND_GATE_GRAY(element))
14333     {
14334       if (!player->key[RND_GATE_GRAY_NR(element)])
14335         return MP_NO_ACTION;
14336     }
14337     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14338     {
14339       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14340         return MP_NO_ACTION;
14341     }
14342     else if (element == EL_EXIT_OPEN ||
14343              element == EL_EM_EXIT_OPEN ||
14344              element == EL_EM_EXIT_OPENING ||
14345              element == EL_STEEL_EXIT_OPEN ||
14346              element == EL_EM_STEEL_EXIT_OPEN ||
14347              element == EL_EM_STEEL_EXIT_OPENING ||
14348              element == EL_SP_EXIT_OPEN ||
14349              element == EL_SP_EXIT_OPENING)
14350     {
14351       sound_action = ACTION_PASSING;    // player is passing exit
14352     }
14353     else if (element == EL_EMPTY)
14354     {
14355       sound_action = ACTION_MOVING;             // nothing to walk on
14356     }
14357
14358     // play sound from background or player, whatever is available
14359     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14360       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14361     else
14362       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14363   }
14364   else if (player_can_move &&
14365            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14366   {
14367     if (!ACCESS_FROM(element, opposite_direction))
14368       return MP_NO_ACTION;      // field not accessible from this direction
14369
14370     if (CAN_MOVE(element))      // only fixed elements can be passed!
14371       return MP_NO_ACTION;
14372
14373     if (IS_EM_GATE(element))
14374     {
14375       if (!player->key[EM_GATE_NR(element)])
14376         return MP_NO_ACTION;
14377     }
14378     else if (IS_EM_GATE_GRAY(element))
14379     {
14380       if (!player->key[EM_GATE_GRAY_NR(element)])
14381         return MP_NO_ACTION;
14382     }
14383     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14384     {
14385       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14386         return MP_NO_ACTION;
14387     }
14388     else if (IS_EMC_GATE(element))
14389     {
14390       if (!player->key[EMC_GATE_NR(element)])
14391         return MP_NO_ACTION;
14392     }
14393     else if (IS_EMC_GATE_GRAY(element))
14394     {
14395       if (!player->key[EMC_GATE_GRAY_NR(element)])
14396         return MP_NO_ACTION;
14397     }
14398     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14399     {
14400       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14401         return MP_NO_ACTION;
14402     }
14403     else if (element == EL_DC_GATE_WHITE ||
14404              element == EL_DC_GATE_WHITE_GRAY ||
14405              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14406     {
14407       if (player->num_white_keys == 0)
14408         return MP_NO_ACTION;
14409
14410       player->num_white_keys--;
14411     }
14412     else if (IS_SP_PORT(element))
14413     {
14414       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14415           element == EL_SP_GRAVITY_PORT_RIGHT ||
14416           element == EL_SP_GRAVITY_PORT_UP ||
14417           element == EL_SP_GRAVITY_PORT_DOWN)
14418         player->gravity = !player->gravity;
14419       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14420                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14421                element == EL_SP_GRAVITY_ON_PORT_UP ||
14422                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14423         player->gravity = TRUE;
14424       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14425                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14426                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14427                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14428         player->gravity = FALSE;
14429     }
14430
14431     // automatically move to the next field with double speed
14432     player->programmed_action = move_direction;
14433
14434     if (player->move_delay_reset_counter == 0)
14435     {
14436       player->move_delay_reset_counter = 2;     // two double speed steps
14437
14438       DOUBLE_PLAYER_SPEED(player);
14439     }
14440
14441     PlayLevelSoundAction(x, y, ACTION_PASSING);
14442   }
14443   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14444   {
14445     RemoveField(x, y);
14446
14447     if (mode != DF_SNAP)
14448     {
14449       GfxElement[x][y] = GFX_ELEMENT(element);
14450       player->is_digging = TRUE;
14451     }
14452
14453     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14454
14455     // use old behaviour for old levels (digging)
14456     if (!level.finish_dig_collect)
14457     {
14458       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14459                                           player->index_bit, dig_side);
14460
14461       // if digging triggered player relocation, finish digging tile
14462       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14463         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14464     }
14465
14466     if (mode == DF_SNAP)
14467     {
14468       if (level.block_snap_field)
14469         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14470       else
14471         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14472
14473       // use old behaviour for old levels (snapping)
14474       if (!level.finish_dig_collect)
14475         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14476                                             player->index_bit, dig_side);
14477     }
14478   }
14479   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14480   {
14481     RemoveField(x, y);
14482
14483     if (is_player && mode != DF_SNAP)
14484     {
14485       GfxElement[x][y] = element;
14486       player->is_collecting = TRUE;
14487     }
14488
14489     if (element == EL_SPEED_PILL)
14490     {
14491       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14492     }
14493     else if (element == EL_EXTRA_TIME && level.time > 0)
14494     {
14495       TimeLeft += level.extra_time;
14496
14497       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14498
14499       DisplayGameControlValues();
14500     }
14501     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14502     {
14503       player->shield_normal_time_left += level.shield_normal_time;
14504       if (element == EL_SHIELD_DEADLY)
14505         player->shield_deadly_time_left += level.shield_deadly_time;
14506     }
14507     else if (element == EL_DYNAMITE ||
14508              element == EL_EM_DYNAMITE ||
14509              element == EL_SP_DISK_RED)
14510     {
14511       if (player->inventory_size < MAX_INVENTORY_SIZE)
14512         player->inventory_element[player->inventory_size++] = element;
14513
14514       DrawGameDoorValues();
14515     }
14516     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14517     {
14518       player->dynabomb_count++;
14519       player->dynabombs_left++;
14520     }
14521     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14522     {
14523       player->dynabomb_size++;
14524     }
14525     else if (element == EL_DYNABOMB_INCREASE_POWER)
14526     {
14527       player->dynabomb_xl = TRUE;
14528     }
14529     else if (IS_KEY(element))
14530     {
14531       player->key[KEY_NR(element)] = TRUE;
14532
14533       DrawGameDoorValues();
14534     }
14535     else if (element == EL_DC_KEY_WHITE)
14536     {
14537       player->num_white_keys++;
14538
14539       // display white keys?
14540       // DrawGameDoorValues();
14541     }
14542     else if (IS_ENVELOPE(element))
14543     {
14544       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14545
14546       if (!wait_for_snapping)
14547         player->show_envelope = element;
14548     }
14549     else if (element == EL_EMC_LENSES)
14550     {
14551       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14552
14553       RedrawAllInvisibleElementsForLenses();
14554     }
14555     else if (element == EL_EMC_MAGNIFIER)
14556     {
14557       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14558
14559       RedrawAllInvisibleElementsForMagnifier();
14560     }
14561     else if (IS_DROPPABLE(element) ||
14562              IS_THROWABLE(element))     // can be collected and dropped
14563     {
14564       int i;
14565
14566       if (collect_count == 0)
14567         player->inventory_infinite_element = element;
14568       else
14569         for (i = 0; i < collect_count; i++)
14570           if (player->inventory_size < MAX_INVENTORY_SIZE)
14571             player->inventory_element[player->inventory_size++] = element;
14572
14573       DrawGameDoorValues();
14574     }
14575     else if (collect_count > 0)
14576     {
14577       game.gems_still_needed -= collect_count;
14578       if (game.gems_still_needed < 0)
14579         game.gems_still_needed = 0;
14580
14581       game.snapshot.collected_item = TRUE;
14582
14583       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14584
14585       DisplayGameControlValues();
14586     }
14587
14588     RaiseScoreElement(element);
14589     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14590
14591     // use old behaviour for old levels (collecting)
14592     if (!level.finish_dig_collect && is_player)
14593     {
14594       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14595                                           player->index_bit, dig_side);
14596
14597       // if collecting triggered player relocation, finish collecting tile
14598       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14599         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14600     }
14601
14602     if (mode == DF_SNAP)
14603     {
14604       if (level.block_snap_field)
14605         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14606       else
14607         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14608
14609       // use old behaviour for old levels (snapping)
14610       if (!level.finish_dig_collect)
14611         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14612                                             player->index_bit, dig_side);
14613     }
14614   }
14615   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14616   {
14617     if (mode == DF_SNAP && element != EL_BD_ROCK)
14618       return MP_NO_ACTION;
14619
14620     if (CAN_FALL(element) && dy)
14621       return MP_NO_ACTION;
14622
14623     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14624         !(element == EL_SPRING && level.use_spring_bug))
14625       return MP_NO_ACTION;
14626
14627     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14628         ((move_direction & MV_VERTICAL &&
14629           ((element_info[element].move_pattern & MV_LEFT &&
14630             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14631            (element_info[element].move_pattern & MV_RIGHT &&
14632             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14633          (move_direction & MV_HORIZONTAL &&
14634           ((element_info[element].move_pattern & MV_UP &&
14635             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14636            (element_info[element].move_pattern & MV_DOWN &&
14637             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14638       return MP_NO_ACTION;
14639
14640     // do not push elements already moving away faster than player
14641     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14642         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14643       return MP_NO_ACTION;
14644
14645     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14646     {
14647       if (player->push_delay_value == -1 || !player_was_pushing)
14648         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14649     }
14650     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14651     {
14652       if (player->push_delay_value == -1)
14653         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14654     }
14655     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14656     {
14657       if (!player->is_pushing)
14658         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14659     }
14660
14661     player->is_pushing = TRUE;
14662     player->is_active = TRUE;
14663
14664     if (!(IN_LEV_FIELD(nextx, nexty) &&
14665           (IS_FREE(nextx, nexty) ||
14666            (IS_SB_ELEMENT(element) &&
14667             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14668            (IS_CUSTOM_ELEMENT(element) &&
14669             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14670       return MP_NO_ACTION;
14671
14672     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14673       return MP_NO_ACTION;
14674
14675     if (player->push_delay == -1)       // new pushing; restart delay
14676       player->push_delay = 0;
14677
14678     if (player->push_delay < player->push_delay_value &&
14679         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14680         element != EL_SPRING && element != EL_BALLOON)
14681     {
14682       // make sure that there is no move delay before next try to push
14683       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14684         player->move_delay = 0;
14685
14686       return MP_NO_ACTION;
14687     }
14688
14689     if (IS_CUSTOM_ELEMENT(element) &&
14690         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14691     {
14692       if (!DigFieldByCE(nextx, nexty, element))
14693         return MP_NO_ACTION;
14694     }
14695
14696     if (IS_SB_ELEMENT(element))
14697     {
14698       boolean sokoban_task_solved = FALSE;
14699
14700       if (element == EL_SOKOBAN_FIELD_FULL)
14701       {
14702         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14703
14704         IncrementSokobanFieldsNeeded();
14705         IncrementSokobanObjectsNeeded();
14706       }
14707
14708       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14709       {
14710         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14711
14712         DecrementSokobanFieldsNeeded();
14713         DecrementSokobanObjectsNeeded();
14714
14715         // sokoban object was pushed from empty field to sokoban field
14716         if (Back[x][y] == EL_EMPTY)
14717           sokoban_task_solved = TRUE;
14718       }
14719
14720       Tile[x][y] = EL_SOKOBAN_OBJECT;
14721
14722       if (Back[x][y] == Back[nextx][nexty])
14723         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14724       else if (Back[x][y] != 0)
14725         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14726                                     ACTION_EMPTYING);
14727       else
14728         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14729                                     ACTION_FILLING);
14730
14731       if (sokoban_task_solved &&
14732           game.sokoban_fields_still_needed == 0 &&
14733           game.sokoban_objects_still_needed == 0 &&
14734           level.auto_exit_sokoban)
14735       {
14736         game.players_still_needed = 0;
14737
14738         LevelSolved();
14739
14740         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14741       }
14742     }
14743     else
14744       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14745
14746     InitMovingField(x, y, move_direction);
14747     GfxAction[x][y] = ACTION_PUSHING;
14748
14749     if (mode == DF_SNAP)
14750       ContinueMoving(x, y);
14751     else
14752       MovPos[x][y] = (dx != 0 ? dx : dy);
14753
14754     Pushed[x][y] = TRUE;
14755     Pushed[nextx][nexty] = TRUE;
14756
14757     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14758       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14759     else
14760       player->push_delay_value = -1;    // get new value later
14761
14762     // check for element change _after_ element has been pushed
14763     if (game.use_change_when_pushing_bug)
14764     {
14765       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14766                                  player->index_bit, dig_side);
14767       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14768                                           player->index_bit, dig_side);
14769     }
14770   }
14771   else if (IS_SWITCHABLE(element))
14772   {
14773     if (PLAYER_SWITCHING(player, x, y))
14774     {
14775       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14776                                           player->index_bit, dig_side);
14777
14778       return MP_ACTION;
14779     }
14780
14781     player->is_switching = TRUE;
14782     player->switch_x = x;
14783     player->switch_y = y;
14784
14785     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14786
14787     if (element == EL_ROBOT_WHEEL)
14788     {
14789       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14790
14791       game.robot_wheel_x = x;
14792       game.robot_wheel_y = y;
14793       game.robot_wheel_active = TRUE;
14794
14795       TEST_DrawLevelField(x, y);
14796     }
14797     else if (element == EL_SP_TERMINAL)
14798     {
14799       int xx, yy;
14800
14801       SCAN_PLAYFIELD(xx, yy)
14802       {
14803         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14804         {
14805           Bang(xx, yy);
14806         }
14807         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14808         {
14809           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14810
14811           ResetGfxAnimation(xx, yy);
14812           TEST_DrawLevelField(xx, yy);
14813         }
14814       }
14815     }
14816     else if (IS_BELT_SWITCH(element))
14817     {
14818       ToggleBeltSwitch(x, y);
14819     }
14820     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14821              element == EL_SWITCHGATE_SWITCH_DOWN ||
14822              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14823              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14824     {
14825       ToggleSwitchgateSwitch(x, y);
14826     }
14827     else if (element == EL_LIGHT_SWITCH ||
14828              element == EL_LIGHT_SWITCH_ACTIVE)
14829     {
14830       ToggleLightSwitch(x, y);
14831     }
14832     else if (element == EL_TIMEGATE_SWITCH ||
14833              element == EL_DC_TIMEGATE_SWITCH)
14834     {
14835       ActivateTimegateSwitch(x, y);
14836     }
14837     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14838              element == EL_BALLOON_SWITCH_RIGHT ||
14839              element == EL_BALLOON_SWITCH_UP    ||
14840              element == EL_BALLOON_SWITCH_DOWN  ||
14841              element == EL_BALLOON_SWITCH_NONE  ||
14842              element == EL_BALLOON_SWITCH_ANY)
14843     {
14844       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14845                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14846                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14847                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14848                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14849                              move_direction);
14850     }
14851     else if (element == EL_LAMP)
14852     {
14853       Tile[x][y] = EL_LAMP_ACTIVE;
14854       game.lights_still_needed--;
14855
14856       ResetGfxAnimation(x, y);
14857       TEST_DrawLevelField(x, y);
14858     }
14859     else if (element == EL_TIME_ORB_FULL)
14860     {
14861       Tile[x][y] = EL_TIME_ORB_EMPTY;
14862
14863       if (level.time > 0 || level.use_time_orb_bug)
14864       {
14865         TimeLeft += level.time_orb_time;
14866         game.no_time_limit = FALSE;
14867
14868         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14869
14870         DisplayGameControlValues();
14871       }
14872
14873       ResetGfxAnimation(x, y);
14874       TEST_DrawLevelField(x, y);
14875     }
14876     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14877              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14878     {
14879       int xx, yy;
14880
14881       game.ball_active = !game.ball_active;
14882
14883       SCAN_PLAYFIELD(xx, yy)
14884       {
14885         int e = Tile[xx][yy];
14886
14887         if (game.ball_active)
14888         {
14889           if (e == EL_EMC_MAGIC_BALL)
14890             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14891           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14892             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14893         }
14894         else
14895         {
14896           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14897             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14898           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14899             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14900         }
14901       }
14902     }
14903
14904     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14905                                         player->index_bit, dig_side);
14906
14907     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14908                                         player->index_bit, dig_side);
14909
14910     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14911                                         player->index_bit, dig_side);
14912
14913     return MP_ACTION;
14914   }
14915   else
14916   {
14917     if (!PLAYER_SWITCHING(player, x, y))
14918     {
14919       player->is_switching = TRUE;
14920       player->switch_x = x;
14921       player->switch_y = y;
14922
14923       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14924                                  player->index_bit, dig_side);
14925       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14926                                           player->index_bit, dig_side);
14927
14928       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14929                                  player->index_bit, dig_side);
14930       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14931                                           player->index_bit, dig_side);
14932     }
14933
14934     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14935                                player->index_bit, dig_side);
14936     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14937                                         player->index_bit, dig_side);
14938
14939     return MP_NO_ACTION;
14940   }
14941
14942   player->push_delay = -1;
14943
14944   if (is_player)                // function can also be called by EL_PENGUIN
14945   {
14946     if (Tile[x][y] != element)          // really digged/collected something
14947     {
14948       player->is_collecting = !player->is_digging;
14949       player->is_active = TRUE;
14950
14951       player->last_removed_element = element;
14952     }
14953   }
14954
14955   return MP_MOVING;
14956 }
14957
14958 static boolean DigFieldByCE(int x, int y, int digging_element)
14959 {
14960   int element = Tile[x][y];
14961
14962   if (!IS_FREE(x, y))
14963   {
14964     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14965                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14966                   ACTION_BREAKING);
14967
14968     // no element can dig solid indestructible elements
14969     if (IS_INDESTRUCTIBLE(element) &&
14970         !IS_DIGGABLE(element) &&
14971         !IS_COLLECTIBLE(element))
14972       return FALSE;
14973
14974     if (AmoebaNr[x][y] &&
14975         (element == EL_AMOEBA_FULL ||
14976          element == EL_BD_AMOEBA ||
14977          element == EL_AMOEBA_GROWING))
14978     {
14979       AmoebaCnt[AmoebaNr[x][y]]--;
14980       AmoebaCnt2[AmoebaNr[x][y]]--;
14981     }
14982
14983     if (IS_MOVING(x, y))
14984       RemoveMovingField(x, y);
14985     else
14986     {
14987       RemoveField(x, y);
14988       TEST_DrawLevelField(x, y);
14989     }
14990
14991     // if digged element was about to explode, prevent the explosion
14992     ExplodeField[x][y] = EX_TYPE_NONE;
14993
14994     PlayLevelSoundAction(x, y, action);
14995   }
14996
14997   Store[x][y] = EL_EMPTY;
14998
14999   // this makes it possible to leave the removed element again
15000   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15001     Store[x][y] = element;
15002
15003   return TRUE;
15004 }
15005
15006 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15007 {
15008   int jx = player->jx, jy = player->jy;
15009   int x = jx + dx, y = jy + dy;
15010   int snap_direction = (dx == -1 ? MV_LEFT  :
15011                         dx == +1 ? MV_RIGHT :
15012                         dy == -1 ? MV_UP    :
15013                         dy == +1 ? MV_DOWN  : MV_NONE);
15014   boolean can_continue_snapping = (level.continuous_snapping &&
15015                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15016
15017   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15018     return FALSE;
15019
15020   if (!player->active || !IN_LEV_FIELD(x, y))
15021     return FALSE;
15022
15023   if (dx && dy)
15024     return FALSE;
15025
15026   if (!dx && !dy)
15027   {
15028     if (player->MovPos == 0)
15029       player->is_pushing = FALSE;
15030
15031     player->is_snapping = FALSE;
15032
15033     if (player->MovPos == 0)
15034     {
15035       player->is_moving = FALSE;
15036       player->is_digging = FALSE;
15037       player->is_collecting = FALSE;
15038     }
15039
15040     return FALSE;
15041   }
15042
15043   // prevent snapping with already pressed snap key when not allowed
15044   if (player->is_snapping && !can_continue_snapping)
15045     return FALSE;
15046
15047   player->MovDir = snap_direction;
15048
15049   if (player->MovPos == 0)
15050   {
15051     player->is_moving = FALSE;
15052     player->is_digging = FALSE;
15053     player->is_collecting = FALSE;
15054   }
15055
15056   player->is_dropping = FALSE;
15057   player->is_dropping_pressed = FALSE;
15058   player->drop_pressed_delay = 0;
15059
15060   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15061     return FALSE;
15062
15063   player->is_snapping = TRUE;
15064   player->is_active = TRUE;
15065
15066   if (player->MovPos == 0)
15067   {
15068     player->is_moving = FALSE;
15069     player->is_digging = FALSE;
15070     player->is_collecting = FALSE;
15071   }
15072
15073   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15074     TEST_DrawLevelField(player->last_jx, player->last_jy);
15075
15076   TEST_DrawLevelField(x, y);
15077
15078   return TRUE;
15079 }
15080
15081 static boolean DropElement(struct PlayerInfo *player)
15082 {
15083   int old_element, new_element;
15084   int dropx = player->jx, dropy = player->jy;
15085   int drop_direction = player->MovDir;
15086   int drop_side = drop_direction;
15087   int drop_element = get_next_dropped_element(player);
15088
15089   /* do not drop an element on top of another element; when holding drop key
15090      pressed without moving, dropped element must move away before the next
15091      element can be dropped (this is especially important if the next element
15092      is dynamite, which can be placed on background for historical reasons) */
15093   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15094     return MP_ACTION;
15095
15096   if (IS_THROWABLE(drop_element))
15097   {
15098     dropx += GET_DX_FROM_DIR(drop_direction);
15099     dropy += GET_DY_FROM_DIR(drop_direction);
15100
15101     if (!IN_LEV_FIELD(dropx, dropy))
15102       return FALSE;
15103   }
15104
15105   old_element = Tile[dropx][dropy];     // old element at dropping position
15106   new_element = drop_element;           // default: no change when dropping
15107
15108   // check if player is active, not moving and ready to drop
15109   if (!player->active || player->MovPos || player->drop_delay > 0)
15110     return FALSE;
15111
15112   // check if player has anything that can be dropped
15113   if (new_element == EL_UNDEFINED)
15114     return FALSE;
15115
15116   // only set if player has anything that can be dropped
15117   player->is_dropping_pressed = TRUE;
15118
15119   // check if drop key was pressed long enough for EM style dynamite
15120   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15121     return FALSE;
15122
15123   // check if anything can be dropped at the current position
15124   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15125     return FALSE;
15126
15127   // collected custom elements can only be dropped on empty fields
15128   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15129     return FALSE;
15130
15131   if (old_element != EL_EMPTY)
15132     Back[dropx][dropy] = old_element;   // store old element on this field
15133
15134   ResetGfxAnimation(dropx, dropy);
15135   ResetRandomAnimationValue(dropx, dropy);
15136
15137   if (player->inventory_size > 0 ||
15138       player->inventory_infinite_element != EL_UNDEFINED)
15139   {
15140     if (player->inventory_size > 0)
15141     {
15142       player->inventory_size--;
15143
15144       DrawGameDoorValues();
15145
15146       if (new_element == EL_DYNAMITE)
15147         new_element = EL_DYNAMITE_ACTIVE;
15148       else if (new_element == EL_EM_DYNAMITE)
15149         new_element = EL_EM_DYNAMITE_ACTIVE;
15150       else if (new_element == EL_SP_DISK_RED)
15151         new_element = EL_SP_DISK_RED_ACTIVE;
15152     }
15153
15154     Tile[dropx][dropy] = new_element;
15155
15156     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15157       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15158                           el2img(Tile[dropx][dropy]), 0);
15159
15160     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15161
15162     // needed if previous element just changed to "empty" in the last frame
15163     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15164
15165     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15166                                player->index_bit, drop_side);
15167     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15168                                         CE_PLAYER_DROPS_X,
15169                                         player->index_bit, drop_side);
15170
15171     TestIfElementTouchesCustomElement(dropx, dropy);
15172   }
15173   else          // player is dropping a dyna bomb
15174   {
15175     player->dynabombs_left--;
15176
15177     Tile[dropx][dropy] = new_element;
15178
15179     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15180       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15181                           el2img(Tile[dropx][dropy]), 0);
15182
15183     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15184   }
15185
15186   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15187     InitField_WithBug1(dropx, dropy, FALSE);
15188
15189   new_element = Tile[dropx][dropy];     // element might have changed
15190
15191   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15192       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15193   {
15194     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15195       MovDir[dropx][dropy] = drop_direction;
15196
15197     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15198
15199     // do not cause impact style collision by dropping elements that can fall
15200     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15201   }
15202
15203   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15204   player->is_dropping = TRUE;
15205
15206   player->drop_pressed_delay = 0;
15207   player->is_dropping_pressed = FALSE;
15208
15209   player->drop_x = dropx;
15210   player->drop_y = dropy;
15211
15212   return TRUE;
15213 }
15214
15215 // ----------------------------------------------------------------------------
15216 // game sound playing functions
15217 // ----------------------------------------------------------------------------
15218
15219 static int *loop_sound_frame = NULL;
15220 static int *loop_sound_volume = NULL;
15221
15222 void InitPlayLevelSound(void)
15223 {
15224   int num_sounds = getSoundListSize();
15225
15226   checked_free(loop_sound_frame);
15227   checked_free(loop_sound_volume);
15228
15229   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15230   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15231 }
15232
15233 static void PlayLevelSound(int x, int y, int nr)
15234 {
15235   int sx = SCREENX(x), sy = SCREENY(y);
15236   int volume, stereo_position;
15237   int max_distance = 8;
15238   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15239
15240   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15241       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15242     return;
15243
15244   if (!IN_LEV_FIELD(x, y) ||
15245       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15246       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15247     return;
15248
15249   volume = SOUND_MAX_VOLUME;
15250
15251   if (!IN_SCR_FIELD(sx, sy))
15252   {
15253     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15254     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15255
15256     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15257   }
15258
15259   stereo_position = (SOUND_MAX_LEFT +
15260                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15261                      (SCR_FIELDX + 2 * max_distance));
15262
15263   if (IS_LOOP_SOUND(nr))
15264   {
15265     /* This assures that quieter loop sounds do not overwrite louder ones,
15266        while restarting sound volume comparison with each new game frame. */
15267
15268     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15269       return;
15270
15271     loop_sound_volume[nr] = volume;
15272     loop_sound_frame[nr] = FrameCounter;
15273   }
15274
15275   PlaySoundExt(nr, volume, stereo_position, type);
15276 }
15277
15278 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15279 {
15280   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15281                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15282                  y < LEVELY(BY1) ? LEVELY(BY1) :
15283                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15284                  sound_action);
15285 }
15286
15287 static void PlayLevelSoundAction(int x, int y, int action)
15288 {
15289   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15290 }
15291
15292 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15293 {
15294   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15295
15296   if (sound_effect != SND_UNDEFINED)
15297     PlayLevelSound(x, y, sound_effect);
15298 }
15299
15300 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15301                                               int action)
15302 {
15303   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15304
15305   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15306     PlayLevelSound(x, y, sound_effect);
15307 }
15308
15309 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15310 {
15311   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15312
15313   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15314     PlayLevelSound(x, y, sound_effect);
15315 }
15316
15317 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15318 {
15319   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15320
15321   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15322     StopSound(sound_effect);
15323 }
15324
15325 static int getLevelMusicNr(void)
15326 {
15327   if (levelset.music[level_nr] != MUS_UNDEFINED)
15328     return levelset.music[level_nr];            // from config file
15329   else
15330     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15331 }
15332
15333 static void FadeLevelSounds(void)
15334 {
15335   FadeSounds();
15336 }
15337
15338 static void FadeLevelMusic(void)
15339 {
15340   int music_nr = getLevelMusicNr();
15341   char *curr_music = getCurrentlyPlayingMusicFilename();
15342   char *next_music = getMusicInfoEntryFilename(music_nr);
15343
15344   if (!strEqual(curr_music, next_music))
15345     FadeMusic();
15346 }
15347
15348 void FadeLevelSoundsAndMusic(void)
15349 {
15350   FadeLevelSounds();
15351   FadeLevelMusic();
15352 }
15353
15354 static void PlayLevelMusic(void)
15355 {
15356   int music_nr = getLevelMusicNr();
15357   char *curr_music = getCurrentlyPlayingMusicFilename();
15358   char *next_music = getMusicInfoEntryFilename(music_nr);
15359
15360   if (!strEqual(curr_music, next_music))
15361     PlayMusicLoop(music_nr);
15362 }
15363
15364 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15365 {
15366   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15367   int offset = 0;
15368   int x = xx - offset;
15369   int y = yy - offset;
15370
15371   switch (sample)
15372   {
15373     case SOUND_blank:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15375       break;
15376
15377     case SOUND_roll:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15379       break;
15380
15381     case SOUND_stone:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15383       break;
15384
15385     case SOUND_nut:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15387       break;
15388
15389     case SOUND_crack:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15391       break;
15392
15393     case SOUND_bug:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15395       break;
15396
15397     case SOUND_tank:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15399       break;
15400
15401     case SOUND_android_clone:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15403       break;
15404
15405     case SOUND_android_move:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15407       break;
15408
15409     case SOUND_spring:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15411       break;
15412
15413     case SOUND_slurp:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15415       break;
15416
15417     case SOUND_eater:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15419       break;
15420
15421     case SOUND_eater_eat:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15423       break;
15424
15425     case SOUND_alien:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15427       break;
15428
15429     case SOUND_collect:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15431       break;
15432
15433     case SOUND_diamond:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15435       break;
15436
15437     case SOUND_squash:
15438       // !!! CHECK THIS !!!
15439 #if 1
15440       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15441 #else
15442       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15443 #endif
15444       break;
15445
15446     case SOUND_wonderfall:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15448       break;
15449
15450     case SOUND_drip:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15452       break;
15453
15454     case SOUND_push:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15456       break;
15457
15458     case SOUND_dirt:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15460       break;
15461
15462     case SOUND_acid:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15464       break;
15465
15466     case SOUND_ball:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15468       break;
15469
15470     case SOUND_slide:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15472       break;
15473
15474     case SOUND_wonder:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15476       break;
15477
15478     case SOUND_door:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15480       break;
15481
15482     case SOUND_exit_open:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15484       break;
15485
15486     case SOUND_exit_leave:
15487       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15488       break;
15489
15490     case SOUND_dynamite:
15491       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15492       break;
15493
15494     case SOUND_tick:
15495       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15496       break;
15497
15498     case SOUND_press:
15499       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15500       break;
15501
15502     case SOUND_wheel:
15503       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15504       break;
15505
15506     case SOUND_boom:
15507       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15508       break;
15509
15510     case SOUND_die:
15511       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15512       break;
15513
15514     case SOUND_time:
15515       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15516       break;
15517
15518     default:
15519       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15520       break;
15521   }
15522 }
15523
15524 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15525 {
15526   int element = map_element_SP_to_RND(element_sp);
15527   int action = map_action_SP_to_RND(action_sp);
15528   int offset = (setup.sp_show_border_elements ? 0 : 1);
15529   int x = xx - offset;
15530   int y = yy - offset;
15531
15532   PlayLevelSoundElementAction(x, y, element, action);
15533 }
15534
15535 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15536 {
15537   int element = map_element_MM_to_RND(element_mm);
15538   int action = map_action_MM_to_RND(action_mm);
15539   int offset = 0;
15540   int x = xx - offset;
15541   int y = yy - offset;
15542
15543   if (!IS_MM_ELEMENT(element))
15544     element = EL_MM_DEFAULT;
15545
15546   PlayLevelSoundElementAction(x, y, element, action);
15547 }
15548
15549 void PlaySound_MM(int sound_mm)
15550 {
15551   int sound = map_sound_MM_to_RND(sound_mm);
15552
15553   if (sound == SND_UNDEFINED)
15554     return;
15555
15556   PlaySound(sound);
15557 }
15558
15559 void PlaySoundLoop_MM(int sound_mm)
15560 {
15561   int sound = map_sound_MM_to_RND(sound_mm);
15562
15563   if (sound == SND_UNDEFINED)
15564     return;
15565
15566   PlaySoundLoop(sound);
15567 }
15568
15569 void StopSound_MM(int sound_mm)
15570 {
15571   int sound = map_sound_MM_to_RND(sound_mm);
15572
15573   if (sound == SND_UNDEFINED)
15574     return;
15575
15576   StopSound(sound);
15577 }
15578
15579 void RaiseScore(int value)
15580 {
15581   game.score += value;
15582
15583   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15584
15585   DisplayGameControlValues();
15586 }
15587
15588 void RaiseScoreElement(int element)
15589 {
15590   switch (element)
15591   {
15592     case EL_EMERALD:
15593     case EL_BD_DIAMOND:
15594     case EL_EMERALD_YELLOW:
15595     case EL_EMERALD_RED:
15596     case EL_EMERALD_PURPLE:
15597     case EL_SP_INFOTRON:
15598       RaiseScore(level.score[SC_EMERALD]);
15599       break;
15600     case EL_DIAMOND:
15601       RaiseScore(level.score[SC_DIAMOND]);
15602       break;
15603     case EL_CRYSTAL:
15604       RaiseScore(level.score[SC_CRYSTAL]);
15605       break;
15606     case EL_PEARL:
15607       RaiseScore(level.score[SC_PEARL]);
15608       break;
15609     case EL_BUG:
15610     case EL_BD_BUTTERFLY:
15611     case EL_SP_ELECTRON:
15612       RaiseScore(level.score[SC_BUG]);
15613       break;
15614     case EL_SPACESHIP:
15615     case EL_BD_FIREFLY:
15616     case EL_SP_SNIKSNAK:
15617       RaiseScore(level.score[SC_SPACESHIP]);
15618       break;
15619     case EL_YAMYAM:
15620     case EL_DARK_YAMYAM:
15621       RaiseScore(level.score[SC_YAMYAM]);
15622       break;
15623     case EL_ROBOT:
15624       RaiseScore(level.score[SC_ROBOT]);
15625       break;
15626     case EL_PACMAN:
15627       RaiseScore(level.score[SC_PACMAN]);
15628       break;
15629     case EL_NUT:
15630       RaiseScore(level.score[SC_NUT]);
15631       break;
15632     case EL_DYNAMITE:
15633     case EL_EM_DYNAMITE:
15634     case EL_SP_DISK_RED:
15635     case EL_DYNABOMB_INCREASE_NUMBER:
15636     case EL_DYNABOMB_INCREASE_SIZE:
15637     case EL_DYNABOMB_INCREASE_POWER:
15638       RaiseScore(level.score[SC_DYNAMITE]);
15639       break;
15640     case EL_SHIELD_NORMAL:
15641     case EL_SHIELD_DEADLY:
15642       RaiseScore(level.score[SC_SHIELD]);
15643       break;
15644     case EL_EXTRA_TIME:
15645       RaiseScore(level.extra_time_score);
15646       break;
15647     case EL_KEY_1:
15648     case EL_KEY_2:
15649     case EL_KEY_3:
15650     case EL_KEY_4:
15651     case EL_EM_KEY_1:
15652     case EL_EM_KEY_2:
15653     case EL_EM_KEY_3:
15654     case EL_EM_KEY_4:
15655     case EL_EMC_KEY_5:
15656     case EL_EMC_KEY_6:
15657     case EL_EMC_KEY_7:
15658     case EL_EMC_KEY_8:
15659     case EL_DC_KEY_WHITE:
15660       RaiseScore(level.score[SC_KEY]);
15661       break;
15662     default:
15663       RaiseScore(element_info[element].collect_score);
15664       break;
15665   }
15666 }
15667
15668 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15669 {
15670   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15671   {
15672     if (!quick_quit)
15673     {
15674       // prevent short reactivation of overlay buttons while closing door
15675       SetOverlayActive(FALSE);
15676
15677       // door may still be open due to skipped or envelope style request
15678       CloseDoor(DOOR_CLOSE_1);
15679     }
15680
15681     if (network.enabled)
15682       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15683     else
15684     {
15685       if (quick_quit)
15686         FadeSkipNextFadeIn();
15687
15688       SetGameStatus(GAME_MODE_MAIN);
15689
15690       DrawMainMenu();
15691     }
15692   }
15693   else          // continue playing the game
15694   {
15695     if (tape.playing && tape.deactivate_display)
15696       TapeDeactivateDisplayOff(TRUE);
15697
15698     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15699
15700     if (tape.playing && tape.deactivate_display)
15701       TapeDeactivateDisplayOn();
15702   }
15703 }
15704
15705 void RequestQuitGame(boolean escape_key_pressed)
15706 {
15707   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15708   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15709                         level_editor_test_game);
15710   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15711                           quick_quit);
15712
15713   RequestQuitGameExt(skip_request, quick_quit,
15714                      "Do you really want to quit the game?");
15715 }
15716
15717 void RequestRestartGame(char *message)
15718 {
15719   game.restart_game_message = NULL;
15720
15721   boolean has_started_game = hasStartedNetworkGame();
15722   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15723
15724   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15725   {
15726     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15727   }
15728   else
15729   {
15730     // needed in case of envelope request to close game panel
15731     CloseDoor(DOOR_CLOSE_1);
15732
15733     SetGameStatus(GAME_MODE_MAIN);
15734
15735     DrawMainMenu();
15736   }
15737 }
15738
15739 void CheckGameOver(void)
15740 {
15741   static boolean last_game_over = FALSE;
15742   static int game_over_delay = 0;
15743   int game_over_delay_value = 50;
15744   boolean game_over = checkGameFailed();
15745
15746   // do not handle game over if request dialog is already active
15747   if (game.request_active)
15748     return;
15749
15750   // do not ask to play again if game was never actually played
15751   if (!game.GamePlayed)
15752     return;
15753
15754   if (!game_over)
15755   {
15756     last_game_over = FALSE;
15757     game_over_delay = game_over_delay_value;
15758
15759     return;
15760   }
15761
15762   if (game_over_delay > 0)
15763   {
15764     game_over_delay--;
15765
15766     return;
15767   }
15768
15769   if (last_game_over != game_over)
15770     game.restart_game_message = (hasStartedNetworkGame() ?
15771                                  "Game over! Play it again?" :
15772                                  "Game over!");
15773
15774   last_game_over = game_over;
15775 }
15776
15777 boolean checkGameSolved(void)
15778 {
15779   // set for all game engines if level was solved
15780   return game.LevelSolved_GameEnd;
15781 }
15782
15783 boolean checkGameFailed(void)
15784 {
15785   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15786     return (game_em.game_over && !game_em.level_solved);
15787   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15788     return (game_sp.game_over && !game_sp.level_solved);
15789   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15790     return (game_mm.game_over && !game_mm.level_solved);
15791   else                          // GAME_ENGINE_TYPE_RND
15792     return (game.GameOver && !game.LevelSolved);
15793 }
15794
15795 boolean checkGameEnded(void)
15796 {
15797   return (checkGameSolved() || checkGameFailed());
15798 }
15799
15800
15801 // ----------------------------------------------------------------------------
15802 // random generator functions
15803 // ----------------------------------------------------------------------------
15804
15805 unsigned int InitEngineRandom_RND(int seed)
15806 {
15807   game.num_random_calls = 0;
15808
15809   return InitEngineRandom(seed);
15810 }
15811
15812 unsigned int RND(int max)
15813 {
15814   if (max > 0)
15815   {
15816     game.num_random_calls++;
15817
15818     return GetEngineRandom(max);
15819   }
15820
15821   return 0;
15822 }
15823
15824
15825 // ----------------------------------------------------------------------------
15826 // game engine snapshot handling functions
15827 // ----------------------------------------------------------------------------
15828
15829 struct EngineSnapshotInfo
15830 {
15831   // runtime values for custom element collect score
15832   int collect_score[NUM_CUSTOM_ELEMENTS];
15833
15834   // runtime values for group element choice position
15835   int choice_pos[NUM_GROUP_ELEMENTS];
15836
15837   // runtime values for belt position animations
15838   int belt_graphic[4][NUM_BELT_PARTS];
15839   int belt_anim_mode[4][NUM_BELT_PARTS];
15840 };
15841
15842 static struct EngineSnapshotInfo engine_snapshot_rnd;
15843 static char *snapshot_level_identifier = NULL;
15844 static int snapshot_level_nr = -1;
15845
15846 static void SaveEngineSnapshotValues_RND(void)
15847 {
15848   static int belt_base_active_element[4] =
15849   {
15850     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15851     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15852     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15853     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15854   };
15855   int i, j;
15856
15857   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15858   {
15859     int element = EL_CUSTOM_START + i;
15860
15861     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15862   }
15863
15864   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15865   {
15866     int element = EL_GROUP_START + i;
15867
15868     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15869   }
15870
15871   for (i = 0; i < 4; i++)
15872   {
15873     for (j = 0; j < NUM_BELT_PARTS; j++)
15874     {
15875       int element = belt_base_active_element[i] + j;
15876       int graphic = el2img(element);
15877       int anim_mode = graphic_info[graphic].anim_mode;
15878
15879       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15880       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15881     }
15882   }
15883 }
15884
15885 static void LoadEngineSnapshotValues_RND(void)
15886 {
15887   unsigned int num_random_calls = game.num_random_calls;
15888   int i, j;
15889
15890   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15891   {
15892     int element = EL_CUSTOM_START + i;
15893
15894     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15895   }
15896
15897   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15898   {
15899     int element = EL_GROUP_START + i;
15900
15901     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15902   }
15903
15904   for (i = 0; i < 4; i++)
15905   {
15906     for (j = 0; j < NUM_BELT_PARTS; j++)
15907     {
15908       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15909       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15910
15911       graphic_info[graphic].anim_mode = anim_mode;
15912     }
15913   }
15914
15915   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15916   {
15917     InitRND(tape.random_seed);
15918     for (i = 0; i < num_random_calls; i++)
15919       RND(1);
15920   }
15921
15922   if (game.num_random_calls != num_random_calls)
15923   {
15924     Error("number of random calls out of sync");
15925     Error("number of random calls should be %d", num_random_calls);
15926     Error("number of random calls is %d", game.num_random_calls);
15927
15928     Fail("this should not happen -- please debug");
15929   }
15930 }
15931
15932 void FreeEngineSnapshotSingle(void)
15933 {
15934   FreeSnapshotSingle();
15935
15936   setString(&snapshot_level_identifier, NULL);
15937   snapshot_level_nr = -1;
15938 }
15939
15940 void FreeEngineSnapshotList(void)
15941 {
15942   FreeSnapshotList();
15943 }
15944
15945 static ListNode *SaveEngineSnapshotBuffers(void)
15946 {
15947   ListNode *buffers = NULL;
15948
15949   // copy some special values to a structure better suited for the snapshot
15950
15951   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15952     SaveEngineSnapshotValues_RND();
15953   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15954     SaveEngineSnapshotValues_EM();
15955   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15956     SaveEngineSnapshotValues_SP(&buffers);
15957   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15958     SaveEngineSnapshotValues_MM(&buffers);
15959
15960   // save values stored in special snapshot structure
15961
15962   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15963     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15964   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15965     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15966   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15967     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15968   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15969     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15970
15971   // save further RND engine values
15972
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15982
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15986
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15988
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15991
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16010
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16013
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16017
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16020
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16027
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16030
16031 #if 0
16032   ListNode *node = engine_snapshot_list_rnd;
16033   int num_bytes = 0;
16034
16035   while (node != NULL)
16036   {
16037     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16038
16039     node = node->next;
16040   }
16041
16042   Debug("game:playing:SaveEngineSnapshotBuffers",
16043         "size of engine snapshot: %d bytes", num_bytes);
16044 #endif
16045
16046   return buffers;
16047 }
16048
16049 void SaveEngineSnapshotSingle(void)
16050 {
16051   ListNode *buffers = SaveEngineSnapshotBuffers();
16052
16053   // finally save all snapshot buffers to single snapshot
16054   SaveSnapshotSingle(buffers);
16055
16056   // save level identification information
16057   setString(&snapshot_level_identifier, leveldir_current->identifier);
16058   snapshot_level_nr = level_nr;
16059 }
16060
16061 boolean CheckSaveEngineSnapshotToList(void)
16062 {
16063   boolean save_snapshot =
16064     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16065      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16066       game.snapshot.changed_action) ||
16067      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16068       game.snapshot.collected_item));
16069
16070   game.snapshot.changed_action = FALSE;
16071   game.snapshot.collected_item = FALSE;
16072   game.snapshot.save_snapshot = save_snapshot;
16073
16074   return save_snapshot;
16075 }
16076
16077 void SaveEngineSnapshotToList(void)
16078 {
16079   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16080       tape.quick_resume)
16081     return;
16082
16083   ListNode *buffers = SaveEngineSnapshotBuffers();
16084
16085   // finally save all snapshot buffers to snapshot list
16086   SaveSnapshotToList(buffers);
16087 }
16088
16089 void SaveEngineSnapshotToListInitial(void)
16090 {
16091   FreeEngineSnapshotList();
16092
16093   SaveEngineSnapshotToList();
16094 }
16095
16096 static void LoadEngineSnapshotValues(void)
16097 {
16098   // restore special values from snapshot structure
16099
16100   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16101     LoadEngineSnapshotValues_RND();
16102   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16103     LoadEngineSnapshotValues_EM();
16104   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16105     LoadEngineSnapshotValues_SP();
16106   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16107     LoadEngineSnapshotValues_MM();
16108 }
16109
16110 void LoadEngineSnapshotSingle(void)
16111 {
16112   LoadSnapshotSingle();
16113
16114   LoadEngineSnapshotValues();
16115 }
16116
16117 static void LoadEngineSnapshot_Undo(int steps)
16118 {
16119   LoadSnapshotFromList_Older(steps);
16120
16121   LoadEngineSnapshotValues();
16122 }
16123
16124 static void LoadEngineSnapshot_Redo(int steps)
16125 {
16126   LoadSnapshotFromList_Newer(steps);
16127
16128   LoadEngineSnapshotValues();
16129 }
16130
16131 boolean CheckEngineSnapshotSingle(void)
16132 {
16133   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16134           snapshot_level_nr == level_nr);
16135 }
16136
16137 boolean CheckEngineSnapshotList(void)
16138 {
16139   return CheckSnapshotList();
16140 }
16141
16142
16143 // ---------- new game button stuff -------------------------------------------
16144
16145 static struct
16146 {
16147   int graphic;
16148   struct XY *pos;
16149   int gadget_id;
16150   boolean *setup_value;
16151   boolean allowed_on_tape;
16152   boolean is_touch_button;
16153   char *infotext;
16154 } gamebutton_info[NUM_GAME_BUTTONS] =
16155 {
16156   {
16157     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16158     GAME_CTRL_ID_STOP,                          NULL,
16159     TRUE, FALSE,                                "stop game"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16163     GAME_CTRL_ID_PAUSE,                         NULL,
16164     TRUE, FALSE,                                "pause game"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16168     GAME_CTRL_ID_PLAY,                          NULL,
16169     TRUE, FALSE,                                "play game"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16173     GAME_CTRL_ID_UNDO,                          NULL,
16174     TRUE, FALSE,                                "undo step"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16178     GAME_CTRL_ID_REDO,                          NULL,
16179     TRUE, FALSE,                                "redo step"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16183     GAME_CTRL_ID_SAVE,                          NULL,
16184     TRUE, FALSE,                                "save game"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16188     GAME_CTRL_ID_PAUSE2,                        NULL,
16189     TRUE, FALSE,                                "pause game"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16193     GAME_CTRL_ID_LOAD,                          NULL,
16194     TRUE, FALSE,                                "load game"
16195   },
16196   {
16197     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16198     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16199     FALSE, FALSE,                               "stop game"
16200   },
16201   {
16202     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16203     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16204     FALSE, FALSE,                               "pause game"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16208     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16209     FALSE, FALSE,                               "play game"
16210   },
16211   {
16212     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16213     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16214     FALSE, TRUE,                                "stop game"
16215   },
16216   {
16217     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16218     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16219     FALSE, TRUE,                                "pause game"
16220   },
16221   {
16222     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16223     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16224     TRUE, FALSE,                                "background music on/off"
16225   },
16226   {
16227     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16228     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16229     TRUE, FALSE,                                "sound loops on/off"
16230   },
16231   {
16232     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16233     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16234     TRUE, FALSE,                                "normal sounds on/off"
16235   },
16236   {
16237     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16238     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16239     FALSE, FALSE,                               "background music on/off"
16240   },
16241   {
16242     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16243     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16244     FALSE, FALSE,                               "sound loops on/off"
16245   },
16246   {
16247     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16248     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16249     FALSE, FALSE,                               "normal sounds on/off"
16250   }
16251 };
16252
16253 void CreateGameButtons(void)
16254 {
16255   int i;
16256
16257   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16258   {
16259     int graphic = gamebutton_info[i].graphic;
16260     struct GraphicInfo *gfx = &graphic_info[graphic];
16261     struct XY *pos = gamebutton_info[i].pos;
16262     struct GadgetInfo *gi;
16263     int button_type;
16264     boolean checked;
16265     unsigned int event_mask;
16266     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16267     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16268     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16269     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16270     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16271     int gd_x   = gfx->src_x;
16272     int gd_y   = gfx->src_y;
16273     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16274     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16275     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16276     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16277     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16278     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16279     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16280     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16281     int id = i;
16282
16283     if (gfx->bitmap == NULL)
16284     {
16285       game_gadget[id] = NULL;
16286
16287       continue;
16288     }
16289
16290     if (id == GAME_CTRL_ID_STOP ||
16291         id == GAME_CTRL_ID_PANEL_STOP ||
16292         id == GAME_CTRL_ID_TOUCH_STOP ||
16293         id == GAME_CTRL_ID_PLAY ||
16294         id == GAME_CTRL_ID_PANEL_PLAY ||
16295         id == GAME_CTRL_ID_SAVE ||
16296         id == GAME_CTRL_ID_LOAD)
16297     {
16298       button_type = GD_TYPE_NORMAL_BUTTON;
16299       checked = FALSE;
16300       event_mask = GD_EVENT_RELEASED;
16301     }
16302     else if (id == GAME_CTRL_ID_UNDO ||
16303              id == GAME_CTRL_ID_REDO)
16304     {
16305       button_type = GD_TYPE_NORMAL_BUTTON;
16306       checked = FALSE;
16307       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16308     }
16309     else
16310     {
16311       button_type = GD_TYPE_CHECK_BUTTON;
16312       checked = (gamebutton_info[i].setup_value != NULL ?
16313                  *gamebutton_info[i].setup_value : FALSE);
16314       event_mask = GD_EVENT_PRESSED;
16315     }
16316
16317     gi = CreateGadget(GDI_CUSTOM_ID, id,
16318                       GDI_IMAGE_ID, graphic,
16319                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16320                       GDI_X, base_x + x,
16321                       GDI_Y, base_y + y,
16322                       GDI_WIDTH, gfx->width,
16323                       GDI_HEIGHT, gfx->height,
16324                       GDI_TYPE, button_type,
16325                       GDI_STATE, GD_BUTTON_UNPRESSED,
16326                       GDI_CHECKED, checked,
16327                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16328                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16329                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16330                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16331                       GDI_DIRECT_DRAW, FALSE,
16332                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16333                       GDI_EVENT_MASK, event_mask,
16334                       GDI_CALLBACK_ACTION, HandleGameButtons,
16335                       GDI_END);
16336
16337     if (gi == NULL)
16338       Fail("cannot create gadget");
16339
16340     game_gadget[id] = gi;
16341   }
16342 }
16343
16344 void FreeGameButtons(void)
16345 {
16346   int i;
16347
16348   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16349     FreeGadget(game_gadget[i]);
16350 }
16351
16352 static void UnmapGameButtonsAtSamePosition(int id)
16353 {
16354   int i;
16355
16356   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16357     if (i != id &&
16358         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16359         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16360       UnmapGadget(game_gadget[i]);
16361 }
16362
16363 static void UnmapGameButtonsAtSamePosition_All(void)
16364 {
16365   if (setup.show_load_save_buttons)
16366   {
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16368     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16369     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16370   }
16371   else if (setup.show_undo_redo_buttons)
16372   {
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16374     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16375     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16376   }
16377   else
16378   {
16379     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16380     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16381     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16382
16383     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16384     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16385     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16386   }
16387 }
16388
16389 void MapLoadSaveButtons(void)
16390 {
16391   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16392   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16393
16394   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16395   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16396 }
16397
16398 void MapUndoRedoButtons(void)
16399 {
16400   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16401   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16402
16403   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16404   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16405 }
16406
16407 void ModifyPauseButtons(void)
16408 {
16409   static int ids[] =
16410   {
16411     GAME_CTRL_ID_PAUSE,
16412     GAME_CTRL_ID_PAUSE2,
16413     GAME_CTRL_ID_PANEL_PAUSE,
16414     GAME_CTRL_ID_TOUCH_PAUSE,
16415     -1
16416   };
16417   int i;
16418
16419   for (i = 0; ids[i] > -1; i++)
16420     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16421 }
16422
16423 static void MapGameButtonsExt(boolean on_tape)
16424 {
16425   int i;
16426
16427   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16428     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16429       MapGadget(game_gadget[i]);
16430
16431   UnmapGameButtonsAtSamePosition_All();
16432
16433   RedrawGameButtons();
16434 }
16435
16436 static void UnmapGameButtonsExt(boolean on_tape)
16437 {
16438   int i;
16439
16440   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16441     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16442       UnmapGadget(game_gadget[i]);
16443 }
16444
16445 static void RedrawGameButtonsExt(boolean on_tape)
16446 {
16447   int i;
16448
16449   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16450     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16451       RedrawGadget(game_gadget[i]);
16452 }
16453
16454 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16455 {
16456   if (gi == NULL)
16457     return;
16458
16459   gi->checked = state;
16460 }
16461
16462 static void RedrawSoundButtonGadget(int id)
16463 {
16464   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16465              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16466              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16467              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16468              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16469              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16470              id);
16471
16472   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16473   RedrawGadget(game_gadget[id2]);
16474 }
16475
16476 void MapGameButtons(void)
16477 {
16478   MapGameButtonsExt(FALSE);
16479 }
16480
16481 void UnmapGameButtons(void)
16482 {
16483   UnmapGameButtonsExt(FALSE);
16484 }
16485
16486 void RedrawGameButtons(void)
16487 {
16488   RedrawGameButtonsExt(FALSE);
16489 }
16490
16491 void MapGameButtonsOnTape(void)
16492 {
16493   MapGameButtonsExt(TRUE);
16494 }
16495
16496 void UnmapGameButtonsOnTape(void)
16497 {
16498   UnmapGameButtonsExt(TRUE);
16499 }
16500
16501 void RedrawGameButtonsOnTape(void)
16502 {
16503   RedrawGameButtonsExt(TRUE);
16504 }
16505
16506 static void GameUndoRedoExt(void)
16507 {
16508   ClearPlayerAction();
16509
16510   tape.pausing = TRUE;
16511
16512   RedrawPlayfield();
16513   UpdateAndDisplayGameControlValues();
16514
16515   DrawCompleteVideoDisplay();
16516   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16517   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16518   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16519
16520   ModifyPauseButtons();
16521
16522   BackToFront();
16523 }
16524
16525 static void GameUndo(int steps)
16526 {
16527   if (!CheckEngineSnapshotList())
16528     return;
16529
16530   int tape_property_bits = tape.property_bits;
16531
16532   LoadEngineSnapshot_Undo(steps);
16533
16534   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16535
16536   GameUndoRedoExt();
16537 }
16538
16539 static void GameRedo(int steps)
16540 {
16541   if (!CheckEngineSnapshotList())
16542     return;
16543
16544   int tape_property_bits = tape.property_bits;
16545
16546   LoadEngineSnapshot_Redo(steps);
16547
16548   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16549
16550   GameUndoRedoExt();
16551 }
16552
16553 static void HandleGameButtonsExt(int id, int button)
16554 {
16555   static boolean game_undo_executed = FALSE;
16556   int steps = BUTTON_STEPSIZE(button);
16557   boolean handle_game_buttons =
16558     (game_status == GAME_MODE_PLAYING ||
16559      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16560
16561   if (!handle_game_buttons)
16562     return;
16563
16564   switch (id)
16565   {
16566     case GAME_CTRL_ID_STOP:
16567     case GAME_CTRL_ID_PANEL_STOP:
16568     case GAME_CTRL_ID_TOUCH_STOP:
16569       if (game_status == GAME_MODE_MAIN)
16570         break;
16571
16572       if (tape.playing)
16573         TapeStop();
16574       else
16575         RequestQuitGame(FALSE);
16576
16577       break;
16578
16579     case GAME_CTRL_ID_PAUSE:
16580     case GAME_CTRL_ID_PAUSE2:
16581     case GAME_CTRL_ID_PANEL_PAUSE:
16582     case GAME_CTRL_ID_TOUCH_PAUSE:
16583       if (network.enabled && game_status == GAME_MODE_PLAYING)
16584       {
16585         if (tape.pausing)
16586           SendToServer_ContinuePlaying();
16587         else
16588           SendToServer_PausePlaying();
16589       }
16590       else
16591         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16592
16593       game_undo_executed = FALSE;
16594
16595       break;
16596
16597     case GAME_CTRL_ID_PLAY:
16598     case GAME_CTRL_ID_PANEL_PLAY:
16599       if (game_status == GAME_MODE_MAIN)
16600       {
16601         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16602       }
16603       else if (tape.pausing)
16604       {
16605         if (network.enabled)
16606           SendToServer_ContinuePlaying();
16607         else
16608           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16609       }
16610       break;
16611
16612     case GAME_CTRL_ID_UNDO:
16613       // Important: When using "save snapshot when collecting an item" mode,
16614       // load last (current) snapshot for first "undo" after pressing "pause"
16615       // (else the last-but-one snapshot would be loaded, because the snapshot
16616       // pointer already points to the last snapshot when pressing "pause",
16617       // which is fine for "every step/move" mode, but not for "every collect")
16618       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16619           !game_undo_executed)
16620         steps--;
16621
16622       game_undo_executed = TRUE;
16623
16624       GameUndo(steps);
16625       break;
16626
16627     case GAME_CTRL_ID_REDO:
16628       GameRedo(steps);
16629       break;
16630
16631     case GAME_CTRL_ID_SAVE:
16632       TapeQuickSave();
16633       break;
16634
16635     case GAME_CTRL_ID_LOAD:
16636       TapeQuickLoad();
16637       break;
16638
16639     case SOUND_CTRL_ID_MUSIC:
16640     case SOUND_CTRL_ID_PANEL_MUSIC:
16641       if (setup.sound_music)
16642       { 
16643         setup.sound_music = FALSE;
16644
16645         FadeMusic();
16646       }
16647       else if (audio.music_available)
16648       { 
16649         setup.sound = setup.sound_music = TRUE;
16650
16651         SetAudioMode(setup.sound);
16652
16653         if (game_status == GAME_MODE_PLAYING)
16654           PlayLevelMusic();
16655       }
16656
16657       RedrawSoundButtonGadget(id);
16658
16659       break;
16660
16661     case SOUND_CTRL_ID_LOOPS:
16662     case SOUND_CTRL_ID_PANEL_LOOPS:
16663       if (setup.sound_loops)
16664         setup.sound_loops = FALSE;
16665       else if (audio.loops_available)
16666       {
16667         setup.sound = setup.sound_loops = TRUE;
16668
16669         SetAudioMode(setup.sound);
16670       }
16671
16672       RedrawSoundButtonGadget(id);
16673
16674       break;
16675
16676     case SOUND_CTRL_ID_SIMPLE:
16677     case SOUND_CTRL_ID_PANEL_SIMPLE:
16678       if (setup.sound_simple)
16679         setup.sound_simple = FALSE;
16680       else if (audio.sound_available)
16681       {
16682         setup.sound = setup.sound_simple = TRUE;
16683
16684         SetAudioMode(setup.sound);
16685       }
16686
16687       RedrawSoundButtonGadget(id);
16688
16689       break;
16690
16691     default:
16692       break;
16693   }
16694 }
16695
16696 static void HandleGameButtons(struct GadgetInfo *gi)
16697 {
16698   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16699 }
16700
16701 void HandleSoundButtonKeys(Key key)
16702 {
16703   if (key == setup.shortcut.sound_simple)
16704     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16705   else if (key == setup.shortcut.sound_loops)
16706     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16707   else if (key == setup.shortcut.sound_music)
16708     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16709 }