added using server score if higher than (potentially truncated) local score
[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_TIME)
2685       {
2686         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2687
2688         if (use_dynamic_size)           // use dynamic number of digits
2689         {
2690           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2691           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2692           int size2 = size1 + 1;
2693           int font1 = pos->font;
2694           int font2 = pos->font_alt;
2695
2696           size = (value < value_change ? size1 : size2);
2697           font = (value < value_change ? font1 : font2);
2698         }
2699       }
2700
2701       // correct text size if "digits" is zero or less
2702       if (size <= 0)
2703         size = strlen(int2str(value, size));
2704
2705       // dynamically correct text alignment
2706       pos->width = size * getFontWidth(font);
2707
2708       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                   int2str(value, size), font, mask_mode);
2710     }
2711     else if (type == TYPE_ELEMENT)
2712     {
2713       int element, graphic;
2714       Bitmap *src_bitmap;
2715       int src_x, src_y;
2716       int width, height;
2717       int dst_x = PANEL_XPOS(pos);
2718       int dst_y = PANEL_YPOS(pos);
2719
2720       if (value != EL_UNDEFINED && value != EL_EMPTY)
2721       {
2722         element = value;
2723         graphic = el2panelimg(value);
2724
2725 #if 0
2726         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2727               element, EL_NAME(element), size);
2728 #endif
2729
2730         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2731           size = TILESIZE;
2732
2733         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2734                               &src_x, &src_y);
2735
2736         width  = graphic_info[graphic].width  * size / TILESIZE;
2737         height = graphic_info[graphic].height * size / TILESIZE;
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_GRAPHIC)
2748     {
2749       int graphic        = gpc->graphic;
2750       int graphic_active = gpc->graphic_active;
2751       Bitmap *src_bitmap;
2752       int src_x, src_y;
2753       int width, height;
2754       int dst_x = PANEL_XPOS(pos);
2755       int dst_y = PANEL_YPOS(pos);
2756       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2757                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2758
2759       if (graphic != IMG_UNDEFINED && !skip)
2760       {
2761         if (pos->style == STYLE_REVERSE)
2762           value = 100 - value;
2763
2764         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2765
2766         if (pos->direction & MV_HORIZONTAL)
2767         {
2768           width  = graphic_info[graphic_active].width * value / 100;
2769           height = graphic_info[graphic_active].height;
2770
2771           if (pos->direction == MV_LEFT)
2772           {
2773             src_x += graphic_info[graphic_active].width - width;
2774             dst_x += graphic_info[graphic_active].width - width;
2775           }
2776         }
2777         else
2778         {
2779           width  = graphic_info[graphic_active].width;
2780           height = graphic_info[graphic_active].height * value / 100;
2781
2782           if (pos->direction == MV_UP)
2783           {
2784             src_y += graphic_info[graphic_active].height - height;
2785             dst_y += graphic_info[graphic_active].height - height;
2786           }
2787         }
2788
2789         if (draw_masked)
2790           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2791                            dst_x, dst_y);
2792         else
2793           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2794                      dst_x, dst_y);
2795
2796         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2797
2798         if (pos->direction & MV_HORIZONTAL)
2799         {
2800           if (pos->direction == MV_RIGHT)
2801           {
2802             src_x += width;
2803             dst_x += width;
2804           }
2805           else
2806           {
2807             dst_x = PANEL_XPOS(pos);
2808           }
2809
2810           width = graphic_info[graphic].width - width;
2811         }
2812         else
2813         {
2814           if (pos->direction == MV_DOWN)
2815           {
2816             src_y += height;
2817             dst_y += height;
2818           }
2819           else
2820           {
2821             dst_y = PANEL_YPOS(pos);
2822           }
2823
2824           height = graphic_info[graphic].height - height;
2825         }
2826
2827         if (draw_masked)
2828           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2829                            dst_x, dst_y);
2830         else
2831           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2832                      dst_x, dst_y);
2833       }
2834     }
2835     else if (type == TYPE_STRING)
2836     {
2837       boolean active = (value != 0);
2838       char *state_normal = "off";
2839       char *state_active = "on";
2840       char *state = (active ? state_active : state_normal);
2841       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2842                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2843                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2844                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2845
2846       if (nr == GAME_PANEL_GRAVITY_STATE)
2847       {
2848         int font1 = pos->font;          // (used for normal state)
2849         int font2 = pos->font_alt;      // (used for active state)
2850
2851         font = (active ? font2 : font1);
2852       }
2853
2854       if (s != NULL)
2855       {
2856         char *s_cut;
2857
2858         if (size <= 0)
2859         {
2860           // don't truncate output if "chars" is zero or less
2861           size = strlen(s);
2862
2863           // dynamically correct text alignment
2864           pos->width = size * getFontWidth(font);
2865         }
2866
2867         s_cut = getStringCopyN(s, size);
2868
2869         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2870                     s_cut, font, mask_mode);
2871
2872         free(s_cut);
2873       }
2874     }
2875
2876     redraw_mask |= REDRAW_DOOR_1;
2877   }
2878
2879   SetGameStatus(GAME_MODE_PLAYING);
2880 }
2881
2882 void UpdateAndDisplayGameControlValues(void)
2883 {
2884   if (tape.deactivate_display)
2885     return;
2886
2887   UpdateGameControlValues();
2888   DisplayGameControlValues();
2889 }
2890
2891 void UpdateGameDoorValues(void)
2892 {
2893   UpdateGameControlValues();
2894 }
2895
2896 void DrawGameDoorValues(void)
2897 {
2898   DisplayGameControlValues();
2899 }
2900
2901
2902 // ============================================================================
2903 // InitGameEngine()
2904 // ----------------------------------------------------------------------------
2905 // initialize game engine due to level / tape version number
2906 // ============================================================================
2907
2908 static void InitGameEngine(void)
2909 {
2910   int i, j, k, l, x, y;
2911
2912   // set game engine from tape file when re-playing, else from level file
2913   game.engine_version = (tape.playing ? tape.engine_version :
2914                          level.game_version);
2915
2916   // set single or multi-player game mode (needed for re-playing tapes)
2917   game.team_mode = setup.team_mode;
2918
2919   if (tape.playing)
2920   {
2921     int num_players = 0;
2922
2923     for (i = 0; i < MAX_PLAYERS; i++)
2924       if (tape.player_participates[i])
2925         num_players++;
2926
2927     // multi-player tapes contain input data for more than one player
2928     game.team_mode = (num_players > 1);
2929   }
2930
2931 #if 0
2932   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2933         level.game_version);
2934   Debug("game:init:level", "          tape.file_version   == %06d",
2935         tape.file_version);
2936   Debug("game:init:level", "          tape.game_version   == %06d",
2937         tape.game_version);
2938   Debug("game:init:level", "          tape.engine_version == %06d",
2939         tape.engine_version);
2940   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2941         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2942 #endif
2943
2944   // --------------------------------------------------------------------------
2945   // set flags for bugs and changes according to active game engine version
2946   // --------------------------------------------------------------------------
2947
2948   /*
2949     Summary of bugfix:
2950     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2951
2952     Bug was introduced in version:
2953     2.0.1
2954
2955     Bug was fixed in version:
2956     4.2.0.0
2957
2958     Description:
2959     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2960     but the property "can fall" was missing, which caused some levels to be
2961     unsolvable. This was fixed in version 4.2.0.0.
2962
2963     Affected levels/tapes:
2964     An example for a tape that was fixed by this bugfix is tape 029 from the
2965     level set "rnd_sam_bateman".
2966     The wrong behaviour will still be used for all levels or tapes that were
2967     created/recorded with it. An example for this is tape 023 from the level
2968     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2969   */
2970
2971   boolean use_amoeba_dropping_cannot_fall_bug =
2972     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2973       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2974      (tape.playing &&
2975       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2976       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2977
2978   /*
2979     Summary of bugfix/change:
2980     Fixed move speed of elements entering or leaving magic wall.
2981
2982     Fixed/changed in version:
2983     2.0.1
2984
2985     Description:
2986     Before 2.0.1, move speed of elements entering or leaving magic wall was
2987     twice as fast as it is now.
2988     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2989
2990     Affected levels/tapes:
2991     The first condition is generally needed for all levels/tapes before version
2992     2.0.1, which might use the old behaviour before it was changed; known tapes
2993     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2994     The second condition is an exception from the above case and is needed for
2995     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2996     above, but before it was known that this change would break tapes like the
2997     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2998     although the engine version while recording maybe was before 2.0.1. There
2999     are a lot of tapes that are affected by this exception, like tape 006 from
3000     the level set "rnd_conor_mancone".
3001   */
3002
3003   boolean use_old_move_stepsize_for_magic_wall =
3004     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3005      !(tape.playing &&
3006        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3007        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3008
3009   /*
3010     Summary of bugfix/change:
3011     Fixed handling for custom elements that change when pushed by the player.
3012
3013     Fixed/changed in version:
3014     3.1.0
3015
3016     Description:
3017     Before 3.1.0, custom elements that "change when pushing" changed directly
3018     after the player started pushing them (until then handled in "DigField()").
3019     Since 3.1.0, these custom elements are not changed until the "pushing"
3020     move of the element is finished (now handled in "ContinueMoving()").
3021
3022     Affected levels/tapes:
3023     The first condition is generally needed for all levels/tapes before version
3024     3.1.0, which might use the old behaviour before it was changed; known tapes
3025     that are affected are some tapes from the level set "Walpurgis Gardens" by
3026     Jamie Cullen.
3027     The second condition is an exception from the above case and is needed for
3028     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3029     above (including some development versions of 3.1.0), but before it was
3030     known that this change would break tapes like the above and was fixed in
3031     3.1.1, so that the changed behaviour was active although the engine version
3032     while recording maybe was before 3.1.0. There is at least one tape that is
3033     affected by this exception, which is the tape for the one-level set "Bug
3034     Machine" by Juergen Bonhagen.
3035   */
3036
3037   game.use_change_when_pushing_bug =
3038     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3039      !(tape.playing &&
3040        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3041        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3042
3043   /*
3044     Summary of bugfix/change:
3045     Fixed handling for blocking the field the player leaves when moving.
3046
3047     Fixed/changed in version:
3048     3.1.1
3049
3050     Description:
3051     Before 3.1.1, when "block last field when moving" was enabled, the field
3052     the player is leaving when moving was blocked for the time of the move,
3053     and was directly unblocked afterwards. This resulted in the last field
3054     being blocked for exactly one less than the number of frames of one player
3055     move. Additionally, even when blocking was disabled, the last field was
3056     blocked for exactly one frame.
3057     Since 3.1.1, due to changes in player movement handling, the last field
3058     is not blocked at all when blocking is disabled. When blocking is enabled,
3059     the last field is blocked for exactly the number of frames of one player
3060     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3061     last field is blocked for exactly one more than the number of frames of
3062     one player move.
3063
3064     Affected levels/tapes:
3065     (!!! yet to be determined -- probably many !!!)
3066   */
3067
3068   game.use_block_last_field_bug =
3069     (game.engine_version < VERSION_IDENT(3,1,1,0));
3070
3071   /* various special flags and settings for native Emerald Mine game engine */
3072
3073   game_em.use_single_button =
3074     (game.engine_version > VERSION_IDENT(4,0,0,2));
3075
3076   game_em.use_snap_key_bug =
3077     (game.engine_version < VERSION_IDENT(4,0,1,0));
3078
3079   game_em.use_random_bug =
3080     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3081
3082   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3083
3084   game_em.use_old_explosions            = use_old_em_engine;
3085   game_em.use_old_android               = use_old_em_engine;
3086   game_em.use_old_push_elements         = use_old_em_engine;
3087   game_em.use_old_push_into_acid        = use_old_em_engine;
3088
3089   game_em.use_wrap_around               = !use_old_em_engine;
3090
3091   // --------------------------------------------------------------------------
3092
3093   // set maximal allowed number of custom element changes per game frame
3094   game.max_num_changes_per_frame = 1;
3095
3096   // default scan direction: scan playfield from top/left to bottom/right
3097   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3098
3099   // dynamically adjust element properties according to game engine version
3100   InitElementPropertiesEngine(game.engine_version);
3101
3102   // ---------- initialize special element properties -------------------------
3103
3104   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3105   if (use_amoeba_dropping_cannot_fall_bug)
3106     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3107
3108   // ---------- initialize player's initial move delay ------------------------
3109
3110   // dynamically adjust player properties according to level information
3111   for (i = 0; i < MAX_PLAYERS; i++)
3112     game.initial_move_delay_value[i] =
3113       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3114
3115   // dynamically adjust player properties according to game engine version
3116   for (i = 0; i < MAX_PLAYERS; i++)
3117     game.initial_move_delay[i] =
3118       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3119        game.initial_move_delay_value[i] : 0);
3120
3121   // ---------- initialize player's initial push delay ------------------------
3122
3123   // dynamically adjust player properties according to game engine version
3124   game.initial_push_delay_value =
3125     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3126
3127   // ---------- initialize changing elements ----------------------------------
3128
3129   // initialize changing elements information
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131   {
3132     struct ElementInfo *ei = &element_info[i];
3133
3134     // this pointer might have been changed in the level editor
3135     ei->change = &ei->change_page[0];
3136
3137     if (!IS_CUSTOM_ELEMENT(i))
3138     {
3139       ei->change->target_element = EL_EMPTY_SPACE;
3140       ei->change->delay_fixed = 0;
3141       ei->change->delay_random = 0;
3142       ei->change->delay_frames = 1;
3143     }
3144
3145     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3146     {
3147       ei->has_change_event[j] = FALSE;
3148
3149       ei->event_page_nr[j] = 0;
3150       ei->event_page[j] = &ei->change_page[0];
3151     }
3152   }
3153
3154   // add changing elements from pre-defined list
3155   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3156   {
3157     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3158     struct ElementInfo *ei = &element_info[ch_delay->element];
3159
3160     ei->change->target_element       = ch_delay->target_element;
3161     ei->change->delay_fixed          = ch_delay->change_delay;
3162
3163     ei->change->pre_change_function  = ch_delay->pre_change_function;
3164     ei->change->change_function      = ch_delay->change_function;
3165     ei->change->post_change_function = ch_delay->post_change_function;
3166
3167     ei->change->can_change = TRUE;
3168     ei->change->can_change_or_has_action = TRUE;
3169
3170     ei->has_change_event[CE_DELAY] = TRUE;
3171
3172     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3173     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3174   }
3175
3176   // ---------- initialize internal run-time variables ------------------------
3177
3178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3179   {
3180     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3181
3182     for (j = 0; j < ei->num_change_pages; j++)
3183     {
3184       ei->change_page[j].can_change_or_has_action =
3185         (ei->change_page[j].can_change |
3186          ei->change_page[j].has_action);
3187     }
3188   }
3189
3190   // add change events from custom element configuration
3191   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3192   {
3193     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3194
3195     for (j = 0; j < ei->num_change_pages; j++)
3196     {
3197       if (!ei->change_page[j].can_change_or_has_action)
3198         continue;
3199
3200       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3201       {
3202         // only add event page for the first page found with this event
3203         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3204         {
3205           ei->has_change_event[k] = TRUE;
3206
3207           ei->event_page_nr[k] = j;
3208           ei->event_page[k] = &ei->change_page[j];
3209         }
3210       }
3211     }
3212   }
3213
3214   // ---------- initialize reference elements in change conditions ------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     int element = EL_CUSTOM_START + i;
3219     struct ElementInfo *ei = &element_info[element];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       int trigger_element = ei->change_page[j].initial_trigger_element;
3224
3225       if (trigger_element >= EL_PREV_CE_8 &&
3226           trigger_element <= EL_NEXT_CE_8)
3227         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3228
3229       ei->change_page[j].trigger_element = trigger_element;
3230     }
3231   }
3232
3233   // ---------- initialize run-time trigger player and element ----------------
3234
3235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3236   {
3237     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3242       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3243       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3244       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3245       ei->change_page[j].actual_trigger_ce_value = 0;
3246       ei->change_page[j].actual_trigger_ce_score = 0;
3247     }
3248   }
3249
3250   // ---------- initialize trigger events -------------------------------------
3251
3252   // initialize trigger events information
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3255       trigger_events[i][j] = FALSE;
3256
3257   // add trigger events from element change event properties
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259   {
3260     struct ElementInfo *ei = &element_info[i];
3261
3262     for (j = 0; j < ei->num_change_pages; j++)
3263     {
3264       if (!ei->change_page[j].can_change_or_has_action)
3265         continue;
3266
3267       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3268       {
3269         int trigger_element = ei->change_page[j].trigger_element;
3270
3271         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3272         {
3273           if (ei->change_page[j].has_event[k])
3274           {
3275             if (IS_GROUP_ELEMENT(trigger_element))
3276             {
3277               struct ElementGroupInfo *group =
3278                 element_info[trigger_element].group;
3279
3280               for (l = 0; l < group->num_elements_resolved; l++)
3281                 trigger_events[group->element_resolved[l]][k] = TRUE;
3282             }
3283             else if (trigger_element == EL_ANY_ELEMENT)
3284               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3285                 trigger_events[l][k] = TRUE;
3286             else
3287               trigger_events[trigger_element][k] = TRUE;
3288           }
3289         }
3290       }
3291     }
3292   }
3293
3294   // ---------- initialize push delay -----------------------------------------
3295
3296   // initialize push delay values to default
3297   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3298   {
3299     if (!IS_CUSTOM_ELEMENT(i))
3300     {
3301       // set default push delay values (corrected since version 3.0.7-1)
3302       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3303       {
3304         element_info[i].push_delay_fixed = 2;
3305         element_info[i].push_delay_random = 8;
3306       }
3307       else
3308       {
3309         element_info[i].push_delay_fixed = 8;
3310         element_info[i].push_delay_random = 8;
3311       }
3312     }
3313   }
3314
3315   // set push delay value for certain elements from pre-defined list
3316   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3317   {
3318     int e = push_delay_list[i].element;
3319
3320     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3321     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3322   }
3323
3324   // set push delay value for Supaplex elements for newer engine versions
3325   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3326   {
3327     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3328     {
3329       if (IS_SP_ELEMENT(i))
3330       {
3331         // set SP push delay to just enough to push under a falling zonk
3332         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3333
3334         element_info[i].push_delay_fixed  = delay;
3335         element_info[i].push_delay_random = 0;
3336       }
3337     }
3338   }
3339
3340   // ---------- initialize move stepsize --------------------------------------
3341
3342   // initialize move stepsize values to default
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344     if (!IS_CUSTOM_ELEMENT(i))
3345       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3346
3347   // set move stepsize value for certain elements from pre-defined list
3348   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3349   {
3350     int e = move_stepsize_list[i].element;
3351
3352     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3353
3354     // set move stepsize value for certain elements for older engine versions
3355     if (use_old_move_stepsize_for_magic_wall)
3356     {
3357       if (e == EL_MAGIC_WALL_FILLING ||
3358           e == EL_MAGIC_WALL_EMPTYING ||
3359           e == EL_BD_MAGIC_WALL_FILLING ||
3360           e == EL_BD_MAGIC_WALL_EMPTYING)
3361         element_info[e].move_stepsize *= 2;
3362     }
3363   }
3364
3365   // ---------- initialize collect score --------------------------------------
3366
3367   // initialize collect score values for custom elements from initial value
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (IS_CUSTOM_ELEMENT(i))
3370       element_info[i].collect_score = element_info[i].collect_score_initial;
3371
3372   // ---------- initialize collect count --------------------------------------
3373
3374   // initialize collect count values for non-custom elements
3375   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3376     if (!IS_CUSTOM_ELEMENT(i))
3377       element_info[i].collect_count_initial = 0;
3378
3379   // add collect count values for all elements from pre-defined list
3380   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3381     element_info[collect_count_list[i].element].collect_count_initial =
3382       collect_count_list[i].count;
3383
3384   // ---------- initialize access direction -----------------------------------
3385
3386   // initialize access direction values to default (access from every side)
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388     if (!IS_CUSTOM_ELEMENT(i))
3389       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3390
3391   // set access direction value for certain elements from pre-defined list
3392   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3393     element_info[access_direction_list[i].element].access_direction =
3394       access_direction_list[i].direction;
3395
3396   // ---------- initialize explosion content ----------------------------------
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398   {
3399     if (IS_CUSTOM_ELEMENT(i))
3400       continue;
3401
3402     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3403     {
3404       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3405
3406       element_info[i].content.e[x][y] =
3407         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3408          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3409          i == EL_PLAYER_3 ? EL_EMERALD :
3410          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3411          i == EL_MOLE ? EL_EMERALD_RED :
3412          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3413          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3414          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3415          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3416          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3417          i == EL_WALL_EMERALD ? EL_EMERALD :
3418          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3419          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3420          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3421          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3422          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3423          i == EL_WALL_PEARL ? EL_PEARL :
3424          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3425          EL_EMPTY);
3426     }
3427   }
3428
3429   // ---------- initialize recursion detection --------------------------------
3430   recursion_loop_depth = 0;
3431   recursion_loop_detected = FALSE;
3432   recursion_loop_element = EL_UNDEFINED;
3433
3434   // ---------- initialize graphics engine ------------------------------------
3435   game.scroll_delay_value =
3436     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3437      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3438      !setup.forced_scroll_delay           ? 0 :
3439      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3440   game.scroll_delay_value =
3441     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3442
3443   // ---------- initialize game engine snapshots ------------------------------
3444   for (i = 0; i < MAX_PLAYERS; i++)
3445     game.snapshot.last_action[i] = 0;
3446   game.snapshot.changed_action = FALSE;
3447   game.snapshot.collected_item = FALSE;
3448   game.snapshot.mode =
3449     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3450      SNAPSHOT_MODE_EVERY_STEP :
3451      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3452      SNAPSHOT_MODE_EVERY_MOVE :
3453      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3454      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3455   game.snapshot.save_snapshot = FALSE;
3456
3457   // ---------- initialize level time for Supaplex engine ---------------------
3458   // Supaplex levels with time limit currently unsupported -- should be added
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3460     level.time = 0;
3461
3462   // ---------- initialize flags for handling game actions --------------------
3463
3464   // set flags for game actions to default values
3465   game.use_key_actions = TRUE;
3466   game.use_mouse_actions = FALSE;
3467
3468   // when using Mirror Magic game engine, handle mouse events only
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3470   {
3471     game.use_key_actions = FALSE;
3472     game.use_mouse_actions = TRUE;
3473   }
3474
3475   // check for custom elements with mouse click events
3476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3477   {
3478     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3479     {
3480       int element = EL_CUSTOM_START + i;
3481
3482       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3483           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3484           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3485           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3486         game.use_mouse_actions = TRUE;
3487     }
3488   }
3489 }
3490
3491 static int get_num_special_action(int element, int action_first,
3492                                   int action_last)
3493 {
3494   int num_special_action = 0;
3495   int i, j;
3496
3497   for (i = action_first; i <= action_last; i++)
3498   {
3499     boolean found = FALSE;
3500
3501     for (j = 0; j < NUM_DIRECTIONS; j++)
3502       if (el_act_dir2img(element, i, j) !=
3503           el_act_dir2img(element, ACTION_DEFAULT, j))
3504         found = TRUE;
3505
3506     if (found)
3507       num_special_action++;
3508     else
3509       break;
3510   }
3511
3512   return num_special_action;
3513 }
3514
3515
3516 // ============================================================================
3517 // InitGame()
3518 // ----------------------------------------------------------------------------
3519 // initialize and start new game
3520 // ============================================================================
3521
3522 #if DEBUG_INIT_PLAYER
3523 static void DebugPrintPlayerStatus(char *message)
3524 {
3525   int i;
3526
3527   if (!options.debug)
3528     return;
3529
3530   Debug("game:init:player", "%s:", message);
3531
3532   for (i = 0; i < MAX_PLAYERS; i++)
3533   {
3534     struct PlayerInfo *player = &stored_player[i];
3535
3536     Debug("game:init:player",
3537           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3538           i + 1,
3539           player->present,
3540           player->connected,
3541           player->connected_locally,
3542           player->connected_network,
3543           player->active,
3544           (local_player == player ? " (local player)" : ""));
3545   }
3546 }
3547 #endif
3548
3549 void InitGame(void)
3550 {
3551   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3552   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3553   int fade_mask = REDRAW_FIELD;
3554
3555   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3556   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3557   int initial_move_dir = MV_DOWN;
3558   int i, j, x, y;
3559
3560   // required here to update video display before fading (FIX THIS)
3561   DrawMaskedBorder(REDRAW_DOOR_2);
3562
3563   if (!game.restart_level)
3564     CloseDoor(DOOR_CLOSE_1);
3565
3566   SetGameStatus(GAME_MODE_PLAYING);
3567
3568   if (level_editor_test_game)
3569     FadeSkipNextFadeOut();
3570   else
3571     FadeSetEnterScreen();
3572
3573   if (CheckFadeAll())
3574     fade_mask = REDRAW_ALL;
3575
3576   FadeLevelSoundsAndMusic();
3577
3578   ExpireSoundLoops(TRUE);
3579
3580   FadeOut(fade_mask);
3581
3582   if (level_editor_test_game)
3583     FadeSkipNextFadeIn();
3584
3585   // needed if different viewport properties defined for playing
3586   ChangeViewportPropertiesIfNeeded();
3587
3588   ClearField();
3589
3590   DrawCompleteVideoDisplay();
3591
3592   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3593
3594   InitGameEngine();
3595   InitGameControlValues();
3596
3597   if (tape.recording)
3598   {
3599     // initialize tape actions from game when recording tape
3600     tape.use_key_actions   = game.use_key_actions;
3601     tape.use_mouse_actions = game.use_mouse_actions;
3602
3603     // initialize visible playfield size when recording tape (for team mode)
3604     tape.scr_fieldx = SCR_FIELDX;
3605     tape.scr_fieldy = SCR_FIELDY;
3606   }
3607
3608   // don't play tapes over network
3609   network_playing = (network.enabled && !tape.playing);
3610
3611   for (i = 0; i < MAX_PLAYERS; i++)
3612   {
3613     struct PlayerInfo *player = &stored_player[i];
3614
3615     player->index_nr = i;
3616     player->index_bit = (1 << i);
3617     player->element_nr = EL_PLAYER_1 + i;
3618
3619     player->present = FALSE;
3620     player->active = FALSE;
3621     player->mapped = FALSE;
3622
3623     player->killed = FALSE;
3624     player->reanimated = FALSE;
3625     player->buried = FALSE;
3626
3627     player->action = 0;
3628     player->effective_action = 0;
3629     player->programmed_action = 0;
3630     player->snap_action = 0;
3631
3632     player->mouse_action.lx = 0;
3633     player->mouse_action.ly = 0;
3634     player->mouse_action.button = 0;
3635     player->mouse_action.button_hint = 0;
3636
3637     player->effective_mouse_action.lx = 0;
3638     player->effective_mouse_action.ly = 0;
3639     player->effective_mouse_action.button = 0;
3640     player->effective_mouse_action.button_hint = 0;
3641
3642     for (j = 0; j < MAX_NUM_KEYS; j++)
3643       player->key[j] = FALSE;
3644
3645     player->num_white_keys = 0;
3646
3647     player->dynabomb_count = 0;
3648     player->dynabomb_size = 1;
3649     player->dynabombs_left = 0;
3650     player->dynabomb_xl = FALSE;
3651
3652     player->MovDir = initial_move_dir;
3653     player->MovPos = 0;
3654     player->GfxPos = 0;
3655     player->GfxDir = initial_move_dir;
3656     player->GfxAction = ACTION_DEFAULT;
3657     player->Frame = 0;
3658     player->StepFrame = 0;
3659
3660     player->initial_element = player->element_nr;
3661     player->artwork_element =
3662       (level.use_artwork_element[i] ? level.artwork_element[i] :
3663        player->element_nr);
3664     player->use_murphy = FALSE;
3665
3666     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3667     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3668
3669     player->gravity = level.initial_player_gravity[i];
3670
3671     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3672
3673     player->actual_frame_counter = 0;
3674
3675     player->step_counter = 0;
3676
3677     player->last_move_dir = initial_move_dir;
3678
3679     player->is_active = FALSE;
3680
3681     player->is_waiting = FALSE;
3682     player->is_moving = FALSE;
3683     player->is_auto_moving = FALSE;
3684     player->is_digging = FALSE;
3685     player->is_snapping = FALSE;
3686     player->is_collecting = FALSE;
3687     player->is_pushing = FALSE;
3688     player->is_switching = FALSE;
3689     player->is_dropping = FALSE;
3690     player->is_dropping_pressed = FALSE;
3691
3692     player->is_bored = FALSE;
3693     player->is_sleeping = FALSE;
3694
3695     player->was_waiting = TRUE;
3696     player->was_moving = FALSE;
3697     player->was_snapping = FALSE;
3698     player->was_dropping = FALSE;
3699
3700     player->force_dropping = FALSE;
3701
3702     player->frame_counter_bored = -1;
3703     player->frame_counter_sleeping = -1;
3704
3705     player->anim_delay_counter = 0;
3706     player->post_delay_counter = 0;
3707
3708     player->dir_waiting = initial_move_dir;
3709     player->action_waiting = ACTION_DEFAULT;
3710     player->last_action_waiting = ACTION_DEFAULT;
3711     player->special_action_bored = ACTION_DEFAULT;
3712     player->special_action_sleeping = ACTION_DEFAULT;
3713
3714     player->switch_x = -1;
3715     player->switch_y = -1;
3716
3717     player->drop_x = -1;
3718     player->drop_y = -1;
3719
3720     player->show_envelope = 0;
3721
3722     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3723
3724     player->push_delay       = -1;      // initialized when pushing starts
3725     player->push_delay_value = game.initial_push_delay_value;
3726
3727     player->drop_delay = 0;
3728     player->drop_pressed_delay = 0;
3729
3730     player->last_jx = -1;
3731     player->last_jy = -1;
3732     player->jx = -1;
3733     player->jy = -1;
3734
3735     player->shield_normal_time_left = 0;
3736     player->shield_deadly_time_left = 0;
3737
3738     player->last_removed_element = EL_UNDEFINED;
3739
3740     player->inventory_infinite_element = EL_UNDEFINED;
3741     player->inventory_size = 0;
3742
3743     if (level.use_initial_inventory[i])
3744     {
3745       for (j = 0; j < level.initial_inventory_size[i]; j++)
3746       {
3747         int element = level.initial_inventory_content[i][j];
3748         int collect_count = element_info[element].collect_count_initial;
3749         int k;
3750
3751         if (!IS_CUSTOM_ELEMENT(element))
3752           collect_count = 1;
3753
3754         if (collect_count == 0)
3755           player->inventory_infinite_element = element;
3756         else
3757           for (k = 0; k < collect_count; k++)
3758             if (player->inventory_size < MAX_INVENTORY_SIZE)
3759               player->inventory_element[player->inventory_size++] = element;
3760       }
3761     }
3762
3763     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3764     SnapField(player, 0, 0);
3765
3766     map_player_action[i] = i;
3767   }
3768
3769   network_player_action_received = FALSE;
3770
3771   // initial null action
3772   if (network_playing)
3773     SendToServer_MovePlayer(MV_NONE);
3774
3775   FrameCounter = 0;
3776   TimeFrames = 0;
3777   TimePlayed = 0;
3778   TimeLeft = level.time;
3779   TapeTime = 0;
3780
3781   ScreenMovDir = MV_NONE;
3782   ScreenMovPos = 0;
3783   ScreenGfxPos = 0;
3784
3785   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3786
3787   game.robot_wheel_x = -1;
3788   game.robot_wheel_y = -1;
3789
3790   game.exit_x = -1;
3791   game.exit_y = -1;
3792
3793   game.all_players_gone = FALSE;
3794
3795   game.LevelSolved = FALSE;
3796   game.GameOver = FALSE;
3797
3798   game.GamePlayed = !tape.playing;
3799
3800   game.LevelSolved_GameWon = FALSE;
3801   game.LevelSolved_GameEnd = FALSE;
3802   game.LevelSolved_SaveTape = FALSE;
3803   game.LevelSolved_SaveScore = FALSE;
3804
3805   game.LevelSolved_CountingTime = 0;
3806   game.LevelSolved_CountingScore = 0;
3807   game.LevelSolved_CountingHealth = 0;
3808
3809   game.panel.active = TRUE;
3810
3811   game.no_time_limit = (level.time == 0);
3812
3813   game.yamyam_content_nr = 0;
3814   game.robot_wheel_active = FALSE;
3815   game.magic_wall_active = FALSE;
3816   game.magic_wall_time_left = 0;
3817   game.light_time_left = 0;
3818   game.timegate_time_left = 0;
3819   game.switchgate_pos = 0;
3820   game.wind_direction = level.wind_direction_initial;
3821
3822   game.time_final = 0;
3823   game.score_time_final = 0;
3824
3825   game.score = 0;
3826   game.score_final = 0;
3827
3828   game.health = MAX_HEALTH;
3829   game.health_final = MAX_HEALTH;
3830
3831   game.gems_still_needed = level.gems_needed;
3832   game.sokoban_fields_still_needed = 0;
3833   game.sokoban_objects_still_needed = 0;
3834   game.lights_still_needed = 0;
3835   game.players_still_needed = 0;
3836   game.friends_still_needed = 0;
3837
3838   game.lenses_time_left = 0;
3839   game.magnify_time_left = 0;
3840
3841   game.ball_active = level.ball_active_initial;
3842   game.ball_content_nr = 0;
3843
3844   game.explosions_delayed = TRUE;
3845
3846   game.envelope_active = FALSE;
3847
3848   // special case: set custom artwork setting to initial value
3849   game.use_masked_elements = game.use_masked_elements_initial;
3850
3851   for (i = 0; i < NUM_BELTS; i++)
3852   {
3853     game.belt_dir[i] = MV_NONE;
3854     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3855   }
3856
3857   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3858     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3859
3860 #if DEBUG_INIT_PLAYER
3861   DebugPrintPlayerStatus("Player status at level initialization");
3862 #endif
3863
3864   SCAN_PLAYFIELD(x, y)
3865   {
3866     Tile[x][y] = Last[x][y] = level.field[x][y];
3867     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3868     ChangeDelay[x][y] = 0;
3869     ChangePage[x][y] = -1;
3870     CustomValue[x][y] = 0;              // initialized in InitField()
3871     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3872     AmoebaNr[x][y] = 0;
3873     WasJustMoving[x][y] = 0;
3874     WasJustFalling[x][y] = 0;
3875     CheckCollision[x][y] = 0;
3876     CheckImpact[x][y] = 0;
3877     Stop[x][y] = FALSE;
3878     Pushed[x][y] = FALSE;
3879
3880     ChangeCount[x][y] = 0;
3881     ChangeEvent[x][y] = -1;
3882
3883     ExplodePhase[x][y] = 0;
3884     ExplodeDelay[x][y] = 0;
3885     ExplodeField[x][y] = EX_TYPE_NONE;
3886
3887     RunnerVisit[x][y] = 0;
3888     PlayerVisit[x][y] = 0;
3889
3890     GfxFrame[x][y] = 0;
3891     GfxRandom[x][y] = INIT_GFX_RANDOM();
3892     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3893     GfxElement[x][y] = EL_UNDEFINED;
3894     GfxElementEmpty[x][y] = EL_EMPTY;
3895     GfxAction[x][y] = ACTION_DEFAULT;
3896     GfxDir[x][y] = MV_NONE;
3897     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3898   }
3899
3900   SCAN_PLAYFIELD(x, y)
3901   {
3902     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3903       emulate_bd = FALSE;
3904     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3905       emulate_sp = FALSE;
3906
3907     InitField(x, y, TRUE);
3908
3909     ResetGfxAnimation(x, y);
3910   }
3911
3912   InitBeltMovement();
3913
3914   for (i = 0; i < MAX_PLAYERS; i++)
3915   {
3916     struct PlayerInfo *player = &stored_player[i];
3917
3918     // set number of special actions for bored and sleeping animation
3919     player->num_special_action_bored =
3920       get_num_special_action(player->artwork_element,
3921                              ACTION_BORING_1, ACTION_BORING_LAST);
3922     player->num_special_action_sleeping =
3923       get_num_special_action(player->artwork_element,
3924                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3925   }
3926
3927   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3928                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3929
3930   // initialize type of slippery elements
3931   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3932   {
3933     if (!IS_CUSTOM_ELEMENT(i))
3934     {
3935       // default: elements slip down either to the left or right randomly
3936       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3937
3938       // SP style elements prefer to slip down on the left side
3939       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3940         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3941
3942       // BD style elements prefer to slip down on the left side
3943       if (game.emulation == EMU_BOULDERDASH)
3944         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3945     }
3946   }
3947
3948   // initialize explosion and ignition delay
3949   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3950   {
3951     if (!IS_CUSTOM_ELEMENT(i))
3952     {
3953       int num_phase = 8;
3954       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3955                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3956                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3957       int last_phase = (num_phase + 1) * delay;
3958       int half_phase = (num_phase / 2) * delay;
3959
3960       element_info[i].explosion_delay = last_phase - 1;
3961       element_info[i].ignition_delay = half_phase;
3962
3963       if (i == EL_BLACK_ORB)
3964         element_info[i].ignition_delay = 1;
3965     }
3966   }
3967
3968   // correct non-moving belts to start moving left
3969   for (i = 0; i < NUM_BELTS; i++)
3970     if (game.belt_dir[i] == MV_NONE)
3971       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3972
3973 #if USE_NEW_PLAYER_ASSIGNMENTS
3974   // use preferred player also in local single-player mode
3975   if (!network.enabled && !game.team_mode)
3976   {
3977     int new_index_nr = setup.network_player_nr;
3978
3979     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3980     {
3981       for (i = 0; i < MAX_PLAYERS; i++)
3982         stored_player[i].connected_locally = FALSE;
3983
3984       stored_player[new_index_nr].connected_locally = TRUE;
3985     }
3986   }
3987
3988   for (i = 0; i < MAX_PLAYERS; i++)
3989   {
3990     stored_player[i].connected = FALSE;
3991
3992     // in network game mode, the local player might not be the first player
3993     if (stored_player[i].connected_locally)
3994       local_player = &stored_player[i];
3995   }
3996
3997   if (!network.enabled)
3998     local_player->connected = TRUE;
3999
4000   if (tape.playing)
4001   {
4002     for (i = 0; i < MAX_PLAYERS; i++)
4003       stored_player[i].connected = tape.player_participates[i];
4004   }
4005   else if (network.enabled)
4006   {
4007     // add team mode players connected over the network (needed for correct
4008     // assignment of player figures from level to locally playing players)
4009
4010     for (i = 0; i < MAX_PLAYERS; i++)
4011       if (stored_player[i].connected_network)
4012         stored_player[i].connected = TRUE;
4013   }
4014   else if (game.team_mode)
4015   {
4016     // try to guess locally connected team mode players (needed for correct
4017     // assignment of player figures from level to locally playing players)
4018
4019     for (i = 0; i < MAX_PLAYERS; i++)
4020       if (setup.input[i].use_joystick ||
4021           setup.input[i].key.left != KSYM_UNDEFINED)
4022         stored_player[i].connected = TRUE;
4023   }
4024
4025 #if DEBUG_INIT_PLAYER
4026   DebugPrintPlayerStatus("Player status after level initialization");
4027 #endif
4028
4029 #if DEBUG_INIT_PLAYER
4030   Debug("game:init:player", "Reassigning players ...");
4031 #endif
4032
4033   // check if any connected player was not found in playfield
4034   for (i = 0; i < MAX_PLAYERS; i++)
4035   {
4036     struct PlayerInfo *player = &stored_player[i];
4037
4038     if (player->connected && !player->present)
4039     {
4040       struct PlayerInfo *field_player = NULL;
4041
4042 #if DEBUG_INIT_PLAYER
4043       Debug("game:init:player",
4044             "- looking for field player for player %d ...", i + 1);
4045 #endif
4046
4047       // assign first free player found that is present in the playfield
4048
4049       // first try: look for unmapped playfield player that is not connected
4050       for (j = 0; j < MAX_PLAYERS; j++)
4051         if (field_player == NULL &&
4052             stored_player[j].present &&
4053             !stored_player[j].mapped &&
4054             !stored_player[j].connected)
4055           field_player = &stored_player[j];
4056
4057       // second try: look for *any* unmapped playfield player
4058       for (j = 0; j < MAX_PLAYERS; j++)
4059         if (field_player == NULL &&
4060             stored_player[j].present &&
4061             !stored_player[j].mapped)
4062           field_player = &stored_player[j];
4063
4064       if (field_player != NULL)
4065       {
4066         int jx = field_player->jx, jy = field_player->jy;
4067
4068 #if DEBUG_INIT_PLAYER
4069         Debug("game:init:player", "- found player %d",
4070               field_player->index_nr + 1);
4071 #endif
4072
4073         player->present = FALSE;
4074         player->active = FALSE;
4075
4076         field_player->present = TRUE;
4077         field_player->active = TRUE;
4078
4079         /*
4080         player->initial_element = field_player->initial_element;
4081         player->artwork_element = field_player->artwork_element;
4082
4083         player->block_last_field       = field_player->block_last_field;
4084         player->block_delay_adjustment = field_player->block_delay_adjustment;
4085         */
4086
4087         StorePlayer[jx][jy] = field_player->element_nr;
4088
4089         field_player->jx = field_player->last_jx = jx;
4090         field_player->jy = field_player->last_jy = jy;
4091
4092         if (local_player == player)
4093           local_player = field_player;
4094
4095         map_player_action[field_player->index_nr] = i;
4096
4097         field_player->mapped = TRUE;
4098
4099 #if DEBUG_INIT_PLAYER
4100         Debug("game:init:player", "- map_player_action[%d] == %d",
4101               field_player->index_nr + 1, i + 1);
4102 #endif
4103       }
4104     }
4105
4106     if (player->connected && player->present)
4107       player->mapped = TRUE;
4108   }
4109
4110 #if DEBUG_INIT_PLAYER
4111   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4112 #endif
4113
4114 #else
4115
4116   // check if any connected player was not found in playfield
4117   for (i = 0; i < MAX_PLAYERS; i++)
4118   {
4119     struct PlayerInfo *player = &stored_player[i];
4120
4121     if (player->connected && !player->present)
4122     {
4123       for (j = 0; j < MAX_PLAYERS; j++)
4124       {
4125         struct PlayerInfo *field_player = &stored_player[j];
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128         // assign first free player found that is present in the playfield
4129         if (field_player->present && !field_player->connected)
4130         {
4131           player->present = TRUE;
4132           player->active = TRUE;
4133
4134           field_player->present = FALSE;
4135           field_player->active = FALSE;
4136
4137           player->initial_element = field_player->initial_element;
4138           player->artwork_element = field_player->artwork_element;
4139
4140           player->block_last_field       = field_player->block_last_field;
4141           player->block_delay_adjustment = field_player->block_delay_adjustment;
4142
4143           StorePlayer[jx][jy] = player->element_nr;
4144
4145           player->jx = player->last_jx = jx;
4146           player->jy = player->last_jy = jy;
4147
4148           break;
4149         }
4150       }
4151     }
4152   }
4153 #endif
4154
4155 #if 0
4156   Debug("game:init:player", "local_player->present == %d",
4157         local_player->present);
4158 #endif
4159
4160   // set focus to local player for network games, else to all players
4161   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4162   game.centered_player_nr_next = game.centered_player_nr;
4163   game.set_centered_player = FALSE;
4164   game.set_centered_player_wrap = FALSE;
4165
4166   if (network_playing && tape.recording)
4167   {
4168     // store client dependent player focus when recording network games
4169     tape.centered_player_nr_next = game.centered_player_nr_next;
4170     tape.set_centered_player = TRUE;
4171   }
4172
4173   if (tape.playing)
4174   {
4175     // when playing a tape, eliminate all players who do not participate
4176
4177 #if USE_NEW_PLAYER_ASSIGNMENTS
4178
4179     if (!game.team_mode)
4180     {
4181       for (i = 0; i < MAX_PLAYERS; i++)
4182       {
4183         if (stored_player[i].active &&
4184             !tape.player_participates[map_player_action[i]])
4185         {
4186           struct PlayerInfo *player = &stored_player[i];
4187           int jx = player->jx, jy = player->jy;
4188
4189 #if DEBUG_INIT_PLAYER
4190           Debug("game:init:player", "Removing player %d at (%d, %d)",
4191                 i + 1, jx, jy);
4192 #endif
4193
4194           player->active = FALSE;
4195           StorePlayer[jx][jy] = 0;
4196           Tile[jx][jy] = EL_EMPTY;
4197         }
4198       }
4199     }
4200
4201 #else
4202
4203     for (i = 0; i < MAX_PLAYERS; i++)
4204     {
4205       if (stored_player[i].active &&
4206           !tape.player_participates[i])
4207       {
4208         struct PlayerInfo *player = &stored_player[i];
4209         int jx = player->jx, jy = player->jy;
4210
4211         player->active = FALSE;
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216 #endif
4217   }
4218   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4219   {
4220     // when in single player mode, eliminate all but the local player
4221
4222     for (i = 0; i < MAX_PLAYERS; i++)
4223     {
4224       struct PlayerInfo *player = &stored_player[i];
4225
4226       if (player->active && player != local_player)
4227       {
4228         int jx = player->jx, jy = player->jy;
4229
4230         player->active = FALSE;
4231         player->present = FALSE;
4232
4233         StorePlayer[jx][jy] = 0;
4234         Tile[jx][jy] = EL_EMPTY;
4235       }
4236     }
4237   }
4238
4239   for (i = 0; i < MAX_PLAYERS; i++)
4240     if (stored_player[i].active)
4241       game.players_still_needed++;
4242
4243   if (level.solved_by_one_player)
4244     game.players_still_needed = 1;
4245
4246   // when recording the game, store which players take part in the game
4247   if (tape.recording)
4248   {
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250     for (i = 0; i < MAX_PLAYERS; i++)
4251       if (stored_player[i].connected)
4252         tape.player_participates[i] = TRUE;
4253 #else
4254     for (i = 0; i < MAX_PLAYERS; i++)
4255       if (stored_player[i].active)
4256         tape.player_participates[i] = TRUE;
4257 #endif
4258   }
4259
4260 #if DEBUG_INIT_PLAYER
4261   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4262 #endif
4263
4264   if (BorderElement == EL_EMPTY)
4265   {
4266     SBX_Left = 0;
4267     SBX_Right = lev_fieldx - SCR_FIELDX;
4268     SBY_Upper = 0;
4269     SBY_Lower = lev_fieldy - SCR_FIELDY;
4270   }
4271   else
4272   {
4273     SBX_Left = -1;
4274     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4275     SBY_Upper = -1;
4276     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4277   }
4278
4279   if (full_lev_fieldx <= SCR_FIELDX)
4280     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4281   if (full_lev_fieldy <= SCR_FIELDY)
4282     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4283
4284   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4285     SBX_Left--;
4286   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4287     SBY_Upper--;
4288
4289   // if local player not found, look for custom element that might create
4290   // the player (make some assumptions about the right custom element)
4291   if (!local_player->present)
4292   {
4293     int start_x = 0, start_y = 0;
4294     int found_rating = 0;
4295     int found_element = EL_UNDEFINED;
4296     int player_nr = local_player->index_nr;
4297
4298     SCAN_PLAYFIELD(x, y)
4299     {
4300       int element = Tile[x][y];
4301       int content;
4302       int xx, yy;
4303       boolean is_player;
4304
4305       if (level.use_start_element[player_nr] &&
4306           level.start_element[player_nr] == element &&
4307           found_rating < 4)
4308       {
4309         start_x = x;
4310         start_y = y;
4311
4312         found_rating = 4;
4313         found_element = element;
4314       }
4315
4316       if (!IS_CUSTOM_ELEMENT(element))
4317         continue;
4318
4319       if (CAN_CHANGE(element))
4320       {
4321         for (i = 0; i < element_info[element].num_change_pages; i++)
4322         {
4323           // check for player created from custom element as single target
4324           content = element_info[element].change_page[i].target_element;
4325           is_player = IS_PLAYER_ELEMENT(content);
4326
4327           if (is_player && (found_rating < 3 ||
4328                             (found_rating == 3 && element < found_element)))
4329           {
4330             start_x = x;
4331             start_y = y;
4332
4333             found_rating = 3;
4334             found_element = element;
4335           }
4336         }
4337       }
4338
4339       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4340       {
4341         // check for player created from custom element as explosion content
4342         content = element_info[element].content.e[xx][yy];
4343         is_player = IS_PLAYER_ELEMENT(content);
4344
4345         if (is_player && (found_rating < 2 ||
4346                           (found_rating == 2 && element < found_element)))
4347         {
4348           start_x = x + xx - 1;
4349           start_y = y + yy - 1;
4350
4351           found_rating = 2;
4352           found_element = element;
4353         }
4354
4355         if (!CAN_CHANGE(element))
4356           continue;
4357
4358         for (i = 0; i < element_info[element].num_change_pages; i++)
4359         {
4360           // check for player created from custom element as extended target
4361           content =
4362             element_info[element].change_page[i].target_content.e[xx][yy];
4363
4364           is_player = IS_PLAYER_ELEMENT(content);
4365
4366           if (is_player && (found_rating < 1 ||
4367                             (found_rating == 1 && element < found_element)))
4368           {
4369             start_x = x + xx - 1;
4370             start_y = y + yy - 1;
4371
4372             found_rating = 1;
4373             found_element = element;
4374           }
4375         }
4376       }
4377     }
4378
4379     scroll_x = SCROLL_POSITION_X(start_x);
4380     scroll_y = SCROLL_POSITION_Y(start_y);
4381   }
4382   else
4383   {
4384     scroll_x = SCROLL_POSITION_X(local_player->jx);
4385     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4386   }
4387
4388   // !!! FIX THIS (START) !!!
4389   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4390   {
4391     InitGameEngine_EM();
4392   }
4393   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4394   {
4395     InitGameEngine_SP();
4396   }
4397   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4398   {
4399     InitGameEngine_MM();
4400   }
4401   else
4402   {
4403     DrawLevel(REDRAW_FIELD);
4404     DrawAllPlayers();
4405
4406     // after drawing the level, correct some elements
4407     if (game.timegate_time_left == 0)
4408       CloseAllOpenTimegates();
4409   }
4410
4411   // blit playfield from scroll buffer to normal back buffer for fading in
4412   BlitScreenToBitmap(backbuffer);
4413   // !!! FIX THIS (END) !!!
4414
4415   DrawMaskedBorder(fade_mask);
4416
4417   FadeIn(fade_mask);
4418
4419 #if 1
4420   // full screen redraw is required at this point in the following cases:
4421   // - special editor door undrawn when game was started from level editor
4422   // - drawing area (playfield) was changed and has to be removed completely
4423   redraw_mask = REDRAW_ALL;
4424   BackToFront();
4425 #endif
4426
4427   if (!game.restart_level)
4428   {
4429     // copy default game door content to main double buffer
4430
4431     // !!! CHECK AGAIN !!!
4432     SetPanelBackground();
4433     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4434     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4435   }
4436
4437   SetPanelBackground();
4438   SetDrawBackgroundMask(REDRAW_DOOR_1);
4439
4440   UpdateAndDisplayGameControlValues();
4441
4442   if (!game.restart_level)
4443   {
4444     UnmapGameButtons();
4445     UnmapTapeButtons();
4446
4447     FreeGameButtons();
4448     CreateGameButtons();
4449
4450     MapGameButtons();
4451     MapTapeButtons();
4452
4453     // copy actual game door content to door double buffer for OpenDoor()
4454     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4455
4456     OpenDoor(DOOR_OPEN_ALL);
4457
4458     KeyboardAutoRepeatOffUnlessAutoplay();
4459
4460 #if DEBUG_INIT_PLAYER
4461     DebugPrintPlayerStatus("Player status (final)");
4462 #endif
4463   }
4464
4465   UnmapAllGadgets();
4466
4467   MapGameButtons();
4468   MapTapeButtons();
4469
4470   if (!game.restart_level && !tape.playing)
4471   {
4472     LevelStats_incPlayed(level_nr);
4473
4474     SaveLevelSetup_SeriesInfo();
4475   }
4476
4477   game.restart_level = FALSE;
4478   game.restart_game_message = NULL;
4479
4480   game.request_active = FALSE;
4481   game.request_active_or_moving = FALSE;
4482
4483   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4484     InitGameActions_MM();
4485
4486   SaveEngineSnapshotToListInitial();
4487
4488   if (!game.restart_level)
4489   {
4490     PlaySound(SND_GAME_STARTING);
4491
4492     if (setup.sound_music)
4493       PlayLevelMusic();
4494   }
4495
4496   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4497 }
4498
4499 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4500                         int actual_player_x, int actual_player_y)
4501 {
4502   // this is used for non-R'n'D game engines to update certain engine values
4503
4504   // needed to determine if sounds are played within the visible screen area
4505   scroll_x = actual_scroll_x;
4506   scroll_y = actual_scroll_y;
4507
4508   // needed to get player position for "follow finger" playing input method
4509   local_player->jx = actual_player_x;
4510   local_player->jy = actual_player_y;
4511 }
4512
4513 void InitMovDir(int x, int y)
4514 {
4515   int i, element = Tile[x][y];
4516   static int xy[4][2] =
4517   {
4518     {  0, +1 },
4519     { +1,  0 },
4520     {  0, -1 },
4521     { -1,  0 }
4522   };
4523   static int direction[3][4] =
4524   {
4525     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4526     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4527     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4528   };
4529
4530   switch (element)
4531   {
4532     case EL_BUG_RIGHT:
4533     case EL_BUG_UP:
4534     case EL_BUG_LEFT:
4535     case EL_BUG_DOWN:
4536       Tile[x][y] = EL_BUG;
4537       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4538       break;
4539
4540     case EL_SPACESHIP_RIGHT:
4541     case EL_SPACESHIP_UP:
4542     case EL_SPACESHIP_LEFT:
4543     case EL_SPACESHIP_DOWN:
4544       Tile[x][y] = EL_SPACESHIP;
4545       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4546       break;
4547
4548     case EL_BD_BUTTERFLY_RIGHT:
4549     case EL_BD_BUTTERFLY_UP:
4550     case EL_BD_BUTTERFLY_LEFT:
4551     case EL_BD_BUTTERFLY_DOWN:
4552       Tile[x][y] = EL_BD_BUTTERFLY;
4553       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4554       break;
4555
4556     case EL_BD_FIREFLY_RIGHT:
4557     case EL_BD_FIREFLY_UP:
4558     case EL_BD_FIREFLY_LEFT:
4559     case EL_BD_FIREFLY_DOWN:
4560       Tile[x][y] = EL_BD_FIREFLY;
4561       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4562       break;
4563
4564     case EL_PACMAN_RIGHT:
4565     case EL_PACMAN_UP:
4566     case EL_PACMAN_LEFT:
4567     case EL_PACMAN_DOWN:
4568       Tile[x][y] = EL_PACMAN;
4569       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4570       break;
4571
4572     case EL_YAMYAM_LEFT:
4573     case EL_YAMYAM_RIGHT:
4574     case EL_YAMYAM_UP:
4575     case EL_YAMYAM_DOWN:
4576       Tile[x][y] = EL_YAMYAM;
4577       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4578       break;
4579
4580     case EL_SP_SNIKSNAK:
4581       MovDir[x][y] = MV_UP;
4582       break;
4583
4584     case EL_SP_ELECTRON:
4585       MovDir[x][y] = MV_LEFT;
4586       break;
4587
4588     case EL_MOLE_LEFT:
4589     case EL_MOLE_RIGHT:
4590     case EL_MOLE_UP:
4591     case EL_MOLE_DOWN:
4592       Tile[x][y] = EL_MOLE;
4593       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4594       break;
4595
4596     case EL_SPRING_LEFT:
4597     case EL_SPRING_RIGHT:
4598       Tile[x][y] = EL_SPRING;
4599       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4600       break;
4601
4602     default:
4603       if (IS_CUSTOM_ELEMENT(element))
4604       {
4605         struct ElementInfo *ei = &element_info[element];
4606         int move_direction_initial = ei->move_direction_initial;
4607         int move_pattern = ei->move_pattern;
4608
4609         if (move_direction_initial == MV_START_PREVIOUS)
4610         {
4611           if (MovDir[x][y] != MV_NONE)
4612             return;
4613
4614           move_direction_initial = MV_START_AUTOMATIC;
4615         }
4616
4617         if (move_direction_initial == MV_START_RANDOM)
4618           MovDir[x][y] = 1 << RND(4);
4619         else if (move_direction_initial & MV_ANY_DIRECTION)
4620           MovDir[x][y] = move_direction_initial;
4621         else if (move_pattern == MV_ALL_DIRECTIONS ||
4622                  move_pattern == MV_TURNING_LEFT ||
4623                  move_pattern == MV_TURNING_RIGHT ||
4624                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4625                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4626                  move_pattern == MV_TURNING_RANDOM)
4627           MovDir[x][y] = 1 << RND(4);
4628         else if (move_pattern == MV_HORIZONTAL)
4629           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4630         else if (move_pattern == MV_VERTICAL)
4631           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4632         else if (move_pattern & MV_ANY_DIRECTION)
4633           MovDir[x][y] = element_info[element].move_pattern;
4634         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4635                  move_pattern == MV_ALONG_RIGHT_SIDE)
4636         {
4637           // use random direction as default start direction
4638           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4639             MovDir[x][y] = 1 << RND(4);
4640
4641           for (i = 0; i < NUM_DIRECTIONS; i++)
4642           {
4643             int x1 = x + xy[i][0];
4644             int y1 = y + xy[i][1];
4645
4646             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4647             {
4648               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4649                 MovDir[x][y] = direction[0][i];
4650               else
4651                 MovDir[x][y] = direction[1][i];
4652
4653               break;
4654             }
4655           }
4656         }                
4657       }
4658       else
4659       {
4660         MovDir[x][y] = 1 << RND(4);
4661
4662         if (element != EL_BUG &&
4663             element != EL_SPACESHIP &&
4664             element != EL_BD_BUTTERFLY &&
4665             element != EL_BD_FIREFLY)
4666           break;
4667
4668         for (i = 0; i < NUM_DIRECTIONS; i++)
4669         {
4670           int x1 = x + xy[i][0];
4671           int y1 = y + xy[i][1];
4672
4673           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4674           {
4675             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4676             {
4677               MovDir[x][y] = direction[0][i];
4678               break;
4679             }
4680             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4681                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4682             {
4683               MovDir[x][y] = direction[1][i];
4684               break;
4685             }
4686           }
4687         }
4688       }
4689       break;
4690   }
4691
4692   GfxDir[x][y] = MovDir[x][y];
4693 }
4694
4695 void InitAmoebaNr(int x, int y)
4696 {
4697   int i;
4698   int group_nr = AmoebaNeighbourNr(x, y);
4699
4700   if (group_nr == 0)
4701   {
4702     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4703     {
4704       if (AmoebaCnt[i] == 0)
4705       {
4706         group_nr = i;
4707         break;
4708       }
4709     }
4710   }
4711
4712   AmoebaNr[x][y] = group_nr;
4713   AmoebaCnt[group_nr]++;
4714   AmoebaCnt2[group_nr]++;
4715 }
4716
4717 static void LevelSolved_SetFinalGameValues(void)
4718 {
4719   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4720   game.score_time_final = (level.use_step_counter ? TimePlayed :
4721                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4722
4723   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4724                       game_em.lev->score :
4725                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4726                       game_mm.score :
4727                       game.score);
4728
4729   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4730                        MM_HEALTH(game_mm.laser_overload_value) :
4731                        game.health);
4732
4733   game.LevelSolved_CountingTime = game.time_final;
4734   game.LevelSolved_CountingScore = game.score_final;
4735   game.LevelSolved_CountingHealth = game.health_final;
4736 }
4737
4738 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4739 {
4740   game.LevelSolved_CountingTime = time;
4741   game.LevelSolved_CountingScore = score;
4742   game.LevelSolved_CountingHealth = health;
4743
4744   game_panel_controls[GAME_PANEL_TIME].value = time;
4745   game_panel_controls[GAME_PANEL_SCORE].value = score;
4746   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4747
4748   DisplayGameControlValues();
4749 }
4750
4751 static void LevelSolved(void)
4752 {
4753   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4754       game.players_still_needed > 0)
4755     return;
4756
4757   game.LevelSolved = TRUE;
4758   game.GameOver = TRUE;
4759
4760   // needed here to display correct panel values while player walks into exit
4761   LevelSolved_SetFinalGameValues();
4762 }
4763
4764 void GameWon(void)
4765 {
4766   static int time_count_steps;
4767   static int time, time_final;
4768   static float score, score_final; // needed for time score < 10 for 10 seconds
4769   static int health, health_final;
4770   static int game_over_delay_1 = 0;
4771   static int game_over_delay_2 = 0;
4772   static int game_over_delay_3 = 0;
4773   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4774   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4775
4776   if (!game.LevelSolved_GameWon)
4777   {
4778     int i;
4779
4780     // do not start end game actions before the player stops moving (to exit)
4781     if (local_player->active && local_player->MovPos)
4782       return;
4783
4784     // calculate final game values after player finished walking into exit
4785     LevelSolved_SetFinalGameValues();
4786
4787     game.LevelSolved_GameWon = TRUE;
4788     game.LevelSolved_SaveTape = tape.recording;
4789     game.LevelSolved_SaveScore = !tape.playing;
4790
4791     if (!tape.playing)
4792     {
4793       LevelStats_incSolved(level_nr);
4794
4795       SaveLevelSetup_SeriesInfo();
4796     }
4797
4798     if (tape.auto_play)         // tape might already be stopped here
4799       tape.auto_play_level_solved = TRUE;
4800
4801     TapeStop();
4802
4803     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4804     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4805     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4806
4807     time = time_final = game.time_final;
4808     score = score_final = game.score_final;
4809     health = health_final = game.health_final;
4810
4811     // update game panel values before (delayed) counting of score (if any)
4812     LevelSolved_DisplayFinalGameValues(time, score, health);
4813
4814     // if level has time score defined, calculate new final game values
4815     if (time_score > 0)
4816     {
4817       int time_final_max = 999;
4818       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4819       int time_frames = 0;
4820       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4821       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4822
4823       if (TimeLeft > 0)
4824       {
4825         time_final = 0;
4826         time_frames = time_frames_left;
4827       }
4828       else if (game.no_time_limit && TimePlayed < time_final_max)
4829       {
4830         time_final = time_final_max;
4831         time_frames = time_frames_final_max - time_frames_played;
4832       }
4833
4834       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4835
4836       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4837
4838       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4839       {
4840         health_final = 0;
4841         score_final += health * time_score;
4842       }
4843
4844       game.score_final = score_final;
4845       game.health_final = health_final;
4846     }
4847
4848     // if not counting score after game, immediately update game panel values
4849     if (level_editor_test_game || !setup.count_score_after_game)
4850     {
4851       time = time_final;
4852       score = score_final;
4853
4854       LevelSolved_DisplayFinalGameValues(time, score, health);
4855     }
4856
4857     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4858     {
4859       // check if last player has left the level
4860       if (game.exit_x >= 0 &&
4861           game.exit_y >= 0)
4862       {
4863         int x = game.exit_x;
4864         int y = game.exit_y;
4865         int element = Tile[x][y];
4866
4867         // close exit door after last player
4868         if ((game.all_players_gone &&
4869              (element == EL_EXIT_OPEN ||
4870               element == EL_SP_EXIT_OPEN ||
4871               element == EL_STEEL_EXIT_OPEN)) ||
4872             element == EL_EM_EXIT_OPEN ||
4873             element == EL_EM_STEEL_EXIT_OPEN)
4874         {
4875
4876           Tile[x][y] =
4877             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4878              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4879              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4880              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4881              EL_EM_STEEL_EXIT_CLOSING);
4882
4883           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4884         }
4885
4886         // player disappears
4887         DrawLevelField(x, y);
4888       }
4889
4890       for (i = 0; i < MAX_PLAYERS; i++)
4891       {
4892         struct PlayerInfo *player = &stored_player[i];
4893
4894         if (player->present)
4895         {
4896           RemovePlayer(player);
4897
4898           // player disappears
4899           DrawLevelField(player->jx, player->jy);
4900         }
4901       }
4902     }
4903
4904     PlaySound(SND_GAME_WINNING);
4905   }
4906
4907   if (setup.count_score_after_game)
4908   {
4909     if (time != time_final)
4910     {
4911       if (game_over_delay_1 > 0)
4912       {
4913         game_over_delay_1--;
4914
4915         return;
4916       }
4917
4918       int time_to_go = ABS(time_final - time);
4919       int time_count_dir = (time < time_final ? +1 : -1);
4920
4921       if (time_to_go < time_count_steps)
4922         time_count_steps = 1;
4923
4924       time  += time_count_steps * time_count_dir;
4925       score += time_count_steps * time_score;
4926
4927       // set final score to correct rounding differences after counting score
4928       if (time == time_final)
4929         score = score_final;
4930
4931       LevelSolved_DisplayFinalGameValues(time, score, health);
4932
4933       if (time == time_final)
4934         StopSound(SND_GAME_LEVELTIME_BONUS);
4935       else if (setup.sound_loops)
4936         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4937       else
4938         PlaySound(SND_GAME_LEVELTIME_BONUS);
4939
4940       return;
4941     }
4942
4943     if (health != health_final)
4944     {
4945       if (game_over_delay_2 > 0)
4946       {
4947         game_over_delay_2--;
4948
4949         return;
4950       }
4951
4952       int health_count_dir = (health < health_final ? +1 : -1);
4953
4954       health += health_count_dir;
4955       score  += time_score;
4956
4957       LevelSolved_DisplayFinalGameValues(time, score, health);
4958
4959       if (health == health_final)
4960         StopSound(SND_GAME_LEVELTIME_BONUS);
4961       else if (setup.sound_loops)
4962         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4963       else
4964         PlaySound(SND_GAME_LEVELTIME_BONUS);
4965
4966       return;
4967     }
4968   }
4969
4970   game.panel.active = FALSE;
4971
4972   if (game_over_delay_3 > 0)
4973   {
4974     game_over_delay_3--;
4975
4976     return;
4977   }
4978
4979   GameEnd();
4980 }
4981
4982 void GameEnd(void)
4983 {
4984   // used instead of "level_nr" (needed for network games)
4985   int last_level_nr = levelset.level_nr;
4986   boolean tape_saved = FALSE;
4987
4988   game.LevelSolved_GameEnd = TRUE;
4989
4990   if (game.LevelSolved_SaveTape)
4991   {
4992     // make sure that request dialog to save tape does not open door again
4993     if (!global.use_envelope_request)
4994       CloseDoor(DOOR_CLOSE_1);
4995
4996     // ask to save tape
4997     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4998
4999     // set unique basename for score tape (also saved in high score table)
5000     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5001   }
5002
5003   // if no tape is to be saved, close both doors simultaneously
5004   CloseDoor(DOOR_CLOSE_ALL);
5005
5006   if (level_editor_test_game)
5007   {
5008     SetGameStatus(GAME_MODE_MAIN);
5009
5010     DrawMainMenu();
5011
5012     return;
5013   }
5014
5015   if (!game.LevelSolved_SaveScore)
5016   {
5017     SetGameStatus(GAME_MODE_MAIN);
5018
5019     DrawMainMenu();
5020
5021     return;
5022   }
5023
5024   if (level_nr == leveldir_current->handicap_level)
5025   {
5026     leveldir_current->handicap_level++;
5027
5028     SaveLevelSetup_SeriesInfo();
5029   }
5030
5031   // save score and score tape before potentially erasing tape below
5032   NewHighScore(last_level_nr, tape_saved);
5033
5034   if (setup.increment_levels &&
5035       level_nr < leveldir_current->last_level &&
5036       !network_playing)
5037   {
5038     level_nr++;         // advance to next level
5039     TapeErase();        // start with empty tape
5040
5041     if (setup.auto_play_next_level)
5042     {
5043       LoadLevel(level_nr);
5044
5045       SaveLevelSetup_SeriesInfo();
5046     }
5047   }
5048
5049   if (scores.last_added >= 0 && setup.show_scores_after_game)
5050   {
5051     SetGameStatus(GAME_MODE_SCORES);
5052
5053     DrawHallOfFame(last_level_nr);
5054   }
5055   else if (setup.auto_play_next_level && setup.increment_levels &&
5056            last_level_nr < leveldir_current->last_level &&
5057            !network_playing)
5058   {
5059     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5060   }
5061   else
5062   {
5063     SetGameStatus(GAME_MODE_MAIN);
5064
5065     DrawMainMenu();
5066   }
5067 }
5068
5069 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5070                          boolean one_score_entry_per_name)
5071 {
5072   int i;
5073
5074   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5075     return -1;
5076
5077   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5078   {
5079     struct ScoreEntry *entry = &list->entry[i];
5080     boolean score_is_better = (new_entry->score >  entry->score);
5081     boolean score_is_equal  = (new_entry->score == entry->score);
5082     boolean time_is_better  = (new_entry->time  <  entry->time);
5083     boolean time_is_equal   = (new_entry->time  == entry->time);
5084     boolean better_by_score = (score_is_better ||
5085                                (score_is_equal && time_is_better));
5086     boolean better_by_time  = (time_is_better ||
5087                                (time_is_equal && score_is_better));
5088     boolean is_better = (level.rate_time_over_score ? better_by_time :
5089                          better_by_score);
5090     boolean entry_is_empty = (entry->score == 0 &&
5091                               entry->time == 0);
5092
5093     // prevent adding server score entries if also existing in local score file
5094     // (special case: historic score entries have an empty tape basename entry)
5095     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5096         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5097     {
5098       // special case: use server score instead of local score value if higher
5099       // (historic scores might have been truncated to 16-bit values locally)
5100       if (score_is_better)
5101         entry->score = new_entry->score;
5102
5103       return -1;
5104     }
5105
5106     if (is_better || entry_is_empty)
5107     {
5108       // player has made it to the hall of fame
5109
5110       if (i < MAX_SCORE_ENTRIES - 1)
5111       {
5112         int m = MAX_SCORE_ENTRIES - 1;
5113         int l;
5114
5115         if (one_score_entry_per_name)
5116         {
5117           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5118             if (strEqual(list->entry[l].name, new_entry->name))
5119               m = l;
5120
5121           if (m == i)   // player's new highscore overwrites his old one
5122             goto put_into_list;
5123         }
5124
5125         for (l = m; l > i; l--)
5126           list->entry[l] = list->entry[l - 1];
5127       }
5128
5129       put_into_list:
5130
5131       *entry = *new_entry;
5132
5133       return i;
5134     }
5135     else if (one_score_entry_per_name &&
5136              strEqual(entry->name, new_entry->name))
5137     {
5138       // player already in high score list with better score or time
5139
5140       return -1;
5141     }
5142   }
5143
5144   return -1;
5145 }
5146
5147 void NewHighScore(int level_nr, boolean tape_saved)
5148 {
5149   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5150   boolean one_per_name = FALSE;
5151
5152   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5153   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5154
5155   new_entry.score = game.score_final;
5156   new_entry.time = game.score_time_final;
5157
5158   LoadScore(level_nr);
5159
5160   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5161
5162   if (scores.last_added < 0)
5163     return;
5164
5165   SaveScore(level_nr);
5166
5167   // store last added local score entry (before merging server scores)
5168   scores.last_added_local = scores.last_added;
5169
5170   if (!game.LevelSolved_SaveTape)
5171     return;
5172
5173   SaveScoreTape(level_nr);
5174
5175   if (setup.ask_for_using_api_server)
5176   {
5177     setup.use_api_server =
5178       Request("Upload your score and tape to the high score server?", REQ_ASK);
5179
5180     if (!setup.use_api_server)
5181       Request("Not using high score server! Use setup menu to enable again!",
5182               REQ_CONFIRM);
5183
5184     runtime.use_api_server = setup.use_api_server;
5185
5186     // after asking for using API server once, do not ask again
5187     setup.ask_for_using_api_server = FALSE;
5188
5189     SaveSetup_ServerSetup();
5190   }
5191
5192   SaveServerScore(level_nr, tape_saved);
5193 }
5194
5195 void MergeServerScore(void)
5196 {
5197   struct ScoreEntry last_added_entry;
5198   boolean one_per_name = FALSE;
5199   int i;
5200
5201   if (scores.last_added >= 0)
5202     last_added_entry = scores.entry[scores.last_added];
5203
5204   for (i = 0; i < server_scores.num_entries; i++)
5205   {
5206     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5207
5208     if (pos >= 0 && pos <= scores.last_added)
5209       scores.last_added++;
5210   }
5211
5212   if (scores.last_added >= MAX_SCORE_ENTRIES)
5213   {
5214     scores.last_added = MAX_SCORE_ENTRIES - 1;
5215     scores.force_last_added = TRUE;
5216
5217     scores.entry[scores.last_added] = last_added_entry;
5218   }
5219 }
5220
5221 static int getElementMoveStepsizeExt(int x, int y, int direction)
5222 {
5223   int element = Tile[x][y];
5224   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5225   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5226   int horiz_move = (dx != 0);
5227   int sign = (horiz_move ? dx : dy);
5228   int step = sign * element_info[element].move_stepsize;
5229
5230   // special values for move stepsize for spring and things on conveyor belt
5231   if (horiz_move)
5232   {
5233     if (CAN_FALL(element) &&
5234         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5235       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5236     else if (element == EL_SPRING)
5237       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5238   }
5239
5240   return step;
5241 }
5242
5243 static int getElementMoveStepsize(int x, int y)
5244 {
5245   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5246 }
5247
5248 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5249 {
5250   if (player->GfxAction != action || player->GfxDir != dir)
5251   {
5252     player->GfxAction = action;
5253     player->GfxDir = dir;
5254     player->Frame = 0;
5255     player->StepFrame = 0;
5256   }
5257 }
5258
5259 static void ResetGfxFrame(int x, int y)
5260 {
5261   // profiling showed that "autotest" spends 10~20% of its time in this function
5262   if (DrawingDeactivatedField())
5263     return;
5264
5265   int element = Tile[x][y];
5266   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5267
5268   if (graphic_info[graphic].anim_global_sync)
5269     GfxFrame[x][y] = FrameCounter;
5270   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5271     GfxFrame[x][y] = CustomValue[x][y];
5272   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5273     GfxFrame[x][y] = element_info[element].collect_score;
5274   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5275     GfxFrame[x][y] = ChangeDelay[x][y];
5276 }
5277
5278 static void ResetGfxAnimation(int x, int y)
5279 {
5280   GfxAction[x][y] = ACTION_DEFAULT;
5281   GfxDir[x][y] = MovDir[x][y];
5282   GfxFrame[x][y] = 0;
5283
5284   ResetGfxFrame(x, y);
5285 }
5286
5287 static void ResetRandomAnimationValue(int x, int y)
5288 {
5289   GfxRandom[x][y] = INIT_GFX_RANDOM();
5290 }
5291
5292 static void InitMovingField(int x, int y, int direction)
5293 {
5294   int element = Tile[x][y];
5295   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5296   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5297   int newx = x + dx;
5298   int newy = y + dy;
5299   boolean is_moving_before, is_moving_after;
5300
5301   // check if element was/is moving or being moved before/after mode change
5302   is_moving_before = (WasJustMoving[x][y] != 0);
5303   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5304
5305   // reset animation only for moving elements which change direction of moving
5306   // or which just started or stopped moving
5307   // (else CEs with property "can move" / "not moving" are reset each frame)
5308   if (is_moving_before != is_moving_after ||
5309       direction != MovDir[x][y])
5310     ResetGfxAnimation(x, y);
5311
5312   MovDir[x][y] = direction;
5313   GfxDir[x][y] = direction;
5314
5315   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5316                      direction == MV_DOWN && CAN_FALL(element) ?
5317                      ACTION_FALLING : ACTION_MOVING);
5318
5319   // this is needed for CEs with property "can move" / "not moving"
5320
5321   if (is_moving_after)
5322   {
5323     if (Tile[newx][newy] == EL_EMPTY)
5324       Tile[newx][newy] = EL_BLOCKED;
5325
5326     MovDir[newx][newy] = MovDir[x][y];
5327
5328     CustomValue[newx][newy] = CustomValue[x][y];
5329
5330     GfxFrame[newx][newy] = GfxFrame[x][y];
5331     GfxRandom[newx][newy] = GfxRandom[x][y];
5332     GfxAction[newx][newy] = GfxAction[x][y];
5333     GfxDir[newx][newy] = GfxDir[x][y];
5334   }
5335 }
5336
5337 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5338 {
5339   int direction = MovDir[x][y];
5340   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5341   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5342
5343   *goes_to_x = newx;
5344   *goes_to_y = newy;
5345 }
5346
5347 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5348 {
5349   int oldx = x, oldy = y;
5350   int direction = MovDir[x][y];
5351
5352   if (direction == MV_LEFT)
5353     oldx++;
5354   else if (direction == MV_RIGHT)
5355     oldx--;
5356   else if (direction == MV_UP)
5357     oldy++;
5358   else if (direction == MV_DOWN)
5359     oldy--;
5360
5361   *comes_from_x = oldx;
5362   *comes_from_y = oldy;
5363 }
5364
5365 static int MovingOrBlocked2Element(int x, int y)
5366 {
5367   int element = Tile[x][y];
5368
5369   if (element == EL_BLOCKED)
5370   {
5371     int oldx, oldy;
5372
5373     Blocked2Moving(x, y, &oldx, &oldy);
5374     return Tile[oldx][oldy];
5375   }
5376   else
5377     return element;
5378 }
5379
5380 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5381 {
5382   // like MovingOrBlocked2Element(), but if element is moving
5383   // and (x,y) is the field the moving element is just leaving,
5384   // return EL_BLOCKED instead of the element value
5385   int element = Tile[x][y];
5386
5387   if (IS_MOVING(x, y))
5388   {
5389     if (element == EL_BLOCKED)
5390     {
5391       int oldx, oldy;
5392
5393       Blocked2Moving(x, y, &oldx, &oldy);
5394       return Tile[oldx][oldy];
5395     }
5396     else
5397       return EL_BLOCKED;
5398   }
5399   else
5400     return element;
5401 }
5402
5403 static void RemoveField(int x, int y)
5404 {
5405   Tile[x][y] = EL_EMPTY;
5406
5407   MovPos[x][y] = 0;
5408   MovDir[x][y] = 0;
5409   MovDelay[x][y] = 0;
5410
5411   CustomValue[x][y] = 0;
5412
5413   AmoebaNr[x][y] = 0;
5414   ChangeDelay[x][y] = 0;
5415   ChangePage[x][y] = -1;
5416   Pushed[x][y] = FALSE;
5417
5418   GfxElement[x][y] = EL_UNDEFINED;
5419   GfxAction[x][y] = ACTION_DEFAULT;
5420   GfxDir[x][y] = MV_NONE;
5421 }
5422
5423 static void RemoveMovingField(int x, int y)
5424 {
5425   int oldx = x, oldy = y, newx = x, newy = y;
5426   int element = Tile[x][y];
5427   int next_element = EL_UNDEFINED;
5428
5429   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5430     return;
5431
5432   if (IS_MOVING(x, y))
5433   {
5434     Moving2Blocked(x, y, &newx, &newy);
5435
5436     if (Tile[newx][newy] != EL_BLOCKED)
5437     {
5438       // element is moving, but target field is not free (blocked), but
5439       // already occupied by something different (example: acid pool);
5440       // in this case, only remove the moving field, but not the target
5441
5442       RemoveField(oldx, oldy);
5443
5444       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5445
5446       TEST_DrawLevelField(oldx, oldy);
5447
5448       return;
5449     }
5450   }
5451   else if (element == EL_BLOCKED)
5452   {
5453     Blocked2Moving(x, y, &oldx, &oldy);
5454     if (!IS_MOVING(oldx, oldy))
5455       return;
5456   }
5457
5458   if (element == EL_BLOCKED &&
5459       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5460        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5461        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5462        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5463        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5464        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5465     next_element = get_next_element(Tile[oldx][oldy]);
5466
5467   RemoveField(oldx, oldy);
5468   RemoveField(newx, newy);
5469
5470   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5471
5472   if (next_element != EL_UNDEFINED)
5473     Tile[oldx][oldy] = next_element;
5474
5475   TEST_DrawLevelField(oldx, oldy);
5476   TEST_DrawLevelField(newx, newy);
5477 }
5478
5479 void DrawDynamite(int x, int y)
5480 {
5481   int sx = SCREENX(x), sy = SCREENY(y);
5482   int graphic = el2img(Tile[x][y]);
5483   int frame;
5484
5485   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5486     return;
5487
5488   if (IS_WALKABLE_INSIDE(Back[x][y]))
5489     return;
5490
5491   if (Back[x][y])
5492     DrawLevelElement(x, y, Back[x][y]);
5493   else if (Store[x][y])
5494     DrawLevelElement(x, y, Store[x][y]);
5495   else if (game.use_masked_elements)
5496     DrawLevelElement(x, y, EL_EMPTY);
5497
5498   frame = getGraphicAnimationFrameXY(graphic, x, y);
5499
5500   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5501     DrawGraphicThruMask(sx, sy, graphic, frame);
5502   else
5503     DrawGraphic(sx, sy, graphic, frame);
5504 }
5505
5506 static void CheckDynamite(int x, int y)
5507 {
5508   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5509   {
5510     MovDelay[x][y]--;
5511
5512     if (MovDelay[x][y] != 0)
5513     {
5514       DrawDynamite(x, y);
5515       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5516
5517       return;
5518     }
5519   }
5520
5521   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5522
5523   Bang(x, y);
5524 }
5525
5526 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5527 {
5528   boolean num_checked_players = 0;
5529   int i;
5530
5531   for (i = 0; i < MAX_PLAYERS; i++)
5532   {
5533     if (stored_player[i].active)
5534     {
5535       int sx = stored_player[i].jx;
5536       int sy = stored_player[i].jy;
5537
5538       if (num_checked_players == 0)
5539       {
5540         *sx1 = *sx2 = sx;
5541         *sy1 = *sy2 = sy;
5542       }
5543       else
5544       {
5545         *sx1 = MIN(*sx1, sx);
5546         *sy1 = MIN(*sy1, sy);
5547         *sx2 = MAX(*sx2, sx);
5548         *sy2 = MAX(*sy2, sy);
5549       }
5550
5551       num_checked_players++;
5552     }
5553   }
5554 }
5555
5556 static boolean checkIfAllPlayersFitToScreen_RND(void)
5557 {
5558   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5559
5560   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5561
5562   return (sx2 - sx1 < SCR_FIELDX &&
5563           sy2 - sy1 < SCR_FIELDY);
5564 }
5565
5566 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5567 {
5568   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5569
5570   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5571
5572   *sx = (sx1 + sx2) / 2;
5573   *sy = (sy1 + sy2) / 2;
5574 }
5575
5576 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5577                                boolean center_screen, boolean quick_relocation)
5578 {
5579   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5580   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5581   boolean no_delay = (tape.warp_forward);
5582   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5583   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5584   int new_scroll_x, new_scroll_y;
5585
5586   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5587   {
5588     // case 1: quick relocation inside visible screen (without scrolling)
5589
5590     RedrawPlayfield();
5591
5592     return;
5593   }
5594
5595   if (!level.shifted_relocation || center_screen)
5596   {
5597     // relocation _with_ centering of screen
5598
5599     new_scroll_x = SCROLL_POSITION_X(x);
5600     new_scroll_y = SCROLL_POSITION_Y(y);
5601   }
5602   else
5603   {
5604     // relocation _without_ centering of screen
5605
5606     int center_scroll_x = SCROLL_POSITION_X(old_x);
5607     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5608     int offset_x = x + (scroll_x - center_scroll_x);
5609     int offset_y = y + (scroll_y - center_scroll_y);
5610
5611     // for new screen position, apply previous offset to center position
5612     new_scroll_x = SCROLL_POSITION_X(offset_x);
5613     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5614   }
5615
5616   if (quick_relocation)
5617   {
5618     // case 2: quick relocation (redraw without visible scrolling)
5619
5620     scroll_x = new_scroll_x;
5621     scroll_y = new_scroll_y;
5622
5623     RedrawPlayfield();
5624
5625     return;
5626   }
5627
5628   // case 3: visible relocation (with scrolling to new position)
5629
5630   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5631
5632   SetVideoFrameDelay(wait_delay_value);
5633
5634   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5635   {
5636     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5637     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5638
5639     if (dx == 0 && dy == 0)             // no scrolling needed at all
5640       break;
5641
5642     scroll_x -= dx;
5643     scroll_y -= dy;
5644
5645     // set values for horizontal/vertical screen scrolling (half tile size)
5646     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5647     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5648     int pos_x = dx * TILEX / 2;
5649     int pos_y = dy * TILEY / 2;
5650     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5651     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5652
5653     ScrollLevel(dx, dy);
5654     DrawAllPlayers();
5655
5656     // scroll in two steps of half tile size to make things smoother
5657     BlitScreenToBitmapExt_RND(window, fx, fy);
5658
5659     // scroll second step to align at full tile size
5660     BlitScreenToBitmap(window);
5661   }
5662
5663   DrawAllPlayers();
5664   BackToFront();
5665
5666   SetVideoFrameDelay(frame_delay_value_old);
5667 }
5668
5669 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5670 {
5671   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5672   int player_nr = GET_PLAYER_NR(el_player);
5673   struct PlayerInfo *player = &stored_player[player_nr];
5674   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5675   boolean no_delay = (tape.warp_forward);
5676   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5677   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5678   int old_jx = player->jx;
5679   int old_jy = player->jy;
5680   int old_element = Tile[old_jx][old_jy];
5681   int element = Tile[jx][jy];
5682   boolean player_relocated = (old_jx != jx || old_jy != jy);
5683
5684   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5685   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5686   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5687   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5688   int leave_side_horiz = move_dir_horiz;
5689   int leave_side_vert  = move_dir_vert;
5690   int enter_side = enter_side_horiz | enter_side_vert;
5691   int leave_side = leave_side_horiz | leave_side_vert;
5692
5693   if (player->buried)           // do not reanimate dead player
5694     return;
5695
5696   if (!player_relocated)        // no need to relocate the player
5697     return;
5698
5699   if (IS_PLAYER(jx, jy))        // player already placed at new position
5700   {
5701     RemoveField(jx, jy);        // temporarily remove newly placed player
5702     DrawLevelField(jx, jy);
5703   }
5704
5705   if (player->present)
5706   {
5707     while (player->MovPos)
5708     {
5709       ScrollPlayer(player, SCROLL_GO_ON);
5710       ScrollScreen(NULL, SCROLL_GO_ON);
5711
5712       AdvanceFrameAndPlayerCounters(player->index_nr);
5713
5714       DrawPlayer(player);
5715
5716       BackToFront_WithFrameDelay(wait_delay_value);
5717     }
5718
5719     DrawPlayer(player);         // needed here only to cleanup last field
5720     DrawLevelField(player->jx, player->jy);     // remove player graphic
5721
5722     player->is_moving = FALSE;
5723   }
5724
5725   if (IS_CUSTOM_ELEMENT(old_element))
5726     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5727                                CE_LEFT_BY_PLAYER,
5728                                player->index_bit, leave_side);
5729
5730   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5731                                       CE_PLAYER_LEAVES_X,
5732                                       player->index_bit, leave_side);
5733
5734   Tile[jx][jy] = el_player;
5735   InitPlayerField(jx, jy, el_player, TRUE);
5736
5737   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5738      possible that the relocation target field did not contain a player element,
5739      but a walkable element, to which the new player was relocated -- in this
5740      case, restore that (already initialized!) element on the player field */
5741   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5742   {
5743     Tile[jx][jy] = element;     // restore previously existing element
5744   }
5745
5746   // only visually relocate centered player
5747   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5748                      FALSE, level.instant_relocation);
5749
5750   TestIfPlayerTouchesBadThing(jx, jy);
5751   TestIfPlayerTouchesCustomElement(jx, jy);
5752
5753   if (IS_CUSTOM_ELEMENT(element))
5754     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5755                                player->index_bit, enter_side);
5756
5757   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5758                                       player->index_bit, enter_side);
5759
5760   if (player->is_switching)
5761   {
5762     /* ensure that relocation while still switching an element does not cause
5763        a new element to be treated as also switched directly after relocation
5764        (this is important for teleporter switches that teleport the player to
5765        a place where another teleporter switch is in the same direction, which
5766        would then incorrectly be treated as immediately switched before the
5767        direction key that caused the switch was released) */
5768
5769     player->switch_x += jx - old_jx;
5770     player->switch_y += jy - old_jy;
5771   }
5772 }
5773
5774 static void Explode(int ex, int ey, int phase, int mode)
5775 {
5776   int x, y;
5777   int last_phase;
5778   int border_element;
5779
5780   // !!! eliminate this variable !!!
5781   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5782
5783   if (game.explosions_delayed)
5784   {
5785     ExplodeField[ex][ey] = mode;
5786     return;
5787   }
5788
5789   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5790   {
5791     int center_element = Tile[ex][ey];
5792     int artwork_element, explosion_element;     // set these values later
5793
5794     // remove things displayed in background while burning dynamite
5795     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5796       Back[ex][ey] = 0;
5797
5798     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5799     {
5800       // put moving element to center field (and let it explode there)
5801       center_element = MovingOrBlocked2Element(ex, ey);
5802       RemoveMovingField(ex, ey);
5803       Tile[ex][ey] = center_element;
5804     }
5805
5806     // now "center_element" is finally determined -- set related values now
5807     artwork_element = center_element;           // for custom player artwork
5808     explosion_element = center_element;         // for custom player artwork
5809
5810     if (IS_PLAYER(ex, ey))
5811     {
5812       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5813
5814       artwork_element = stored_player[player_nr].artwork_element;
5815
5816       if (level.use_explosion_element[player_nr])
5817       {
5818         explosion_element = level.explosion_element[player_nr];
5819         artwork_element = explosion_element;
5820       }
5821     }
5822
5823     if (mode == EX_TYPE_NORMAL ||
5824         mode == EX_TYPE_CENTER ||
5825         mode == EX_TYPE_CROSS)
5826       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5827
5828     last_phase = element_info[explosion_element].explosion_delay + 1;
5829
5830     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5831     {
5832       int xx = x - ex + 1;
5833       int yy = y - ey + 1;
5834       int element;
5835
5836       if (!IN_LEV_FIELD(x, y) ||
5837           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5838           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5839         continue;
5840
5841       element = Tile[x][y];
5842
5843       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5844       {
5845         element = MovingOrBlocked2Element(x, y);
5846
5847         if (!IS_EXPLOSION_PROOF(element))
5848           RemoveMovingField(x, y);
5849       }
5850
5851       // indestructible elements can only explode in center (but not flames)
5852       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5853                                            mode == EX_TYPE_BORDER)) ||
5854           element == EL_FLAMES)
5855         continue;
5856
5857       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5858          behaviour, for example when touching a yamyam that explodes to rocks
5859          with active deadly shield, a rock is created under the player !!! */
5860       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5861 #if 0
5862       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5863           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5864            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5865 #else
5866       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5867 #endif
5868       {
5869         if (IS_ACTIVE_BOMB(element))
5870         {
5871           // re-activate things under the bomb like gate or penguin
5872           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5873           Back[x][y] = 0;
5874         }
5875
5876         continue;
5877       }
5878
5879       // save walkable background elements while explosion on same tile
5880       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5881           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5882         Back[x][y] = element;
5883
5884       // ignite explodable elements reached by other explosion
5885       if (element == EL_EXPLOSION)
5886         element = Store2[x][y];
5887
5888       if (AmoebaNr[x][y] &&
5889           (element == EL_AMOEBA_FULL ||
5890            element == EL_BD_AMOEBA ||
5891            element == EL_AMOEBA_GROWING))
5892       {
5893         AmoebaCnt[AmoebaNr[x][y]]--;
5894         AmoebaCnt2[AmoebaNr[x][y]]--;
5895       }
5896
5897       RemoveField(x, y);
5898
5899       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5900       {
5901         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5902
5903         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5904
5905         if (PLAYERINFO(ex, ey)->use_murphy)
5906           Store[x][y] = EL_EMPTY;
5907       }
5908
5909       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5910       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5911       else if (IS_PLAYER_ELEMENT(center_element))
5912         Store[x][y] = EL_EMPTY;
5913       else if (center_element == EL_YAMYAM)
5914         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5915       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5916         Store[x][y] = element_info[center_element].content.e[xx][yy];
5917 #if 1
5918       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5919       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5920       // otherwise) -- FIX THIS !!!
5921       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5922         Store[x][y] = element_info[element].content.e[1][1];
5923 #else
5924       else if (!CAN_EXPLODE(element))
5925         Store[x][y] = element_info[element].content.e[1][1];
5926 #endif
5927       else
5928         Store[x][y] = EL_EMPTY;
5929
5930       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5931           center_element == EL_AMOEBA_TO_DIAMOND)
5932         Store2[x][y] = element;
5933
5934       Tile[x][y] = EL_EXPLOSION;
5935       GfxElement[x][y] = artwork_element;
5936
5937       ExplodePhase[x][y] = 1;
5938       ExplodeDelay[x][y] = last_phase;
5939
5940       Stop[x][y] = TRUE;
5941     }
5942
5943     if (center_element == EL_YAMYAM)
5944       game.yamyam_content_nr =
5945         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5946
5947     return;
5948   }
5949
5950   if (Stop[ex][ey])
5951     return;
5952
5953   x = ex;
5954   y = ey;
5955
5956   if (phase == 1)
5957     GfxFrame[x][y] = 0;         // restart explosion animation
5958
5959   last_phase = ExplodeDelay[x][y];
5960
5961   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5962
5963   // this can happen if the player leaves an explosion just in time
5964   if (GfxElement[x][y] == EL_UNDEFINED)
5965     GfxElement[x][y] = EL_EMPTY;
5966
5967   border_element = Store2[x][y];
5968   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5969     border_element = StorePlayer[x][y];
5970
5971   if (phase == element_info[border_element].ignition_delay ||
5972       phase == last_phase)
5973   {
5974     boolean border_explosion = FALSE;
5975
5976     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5977         !PLAYER_EXPLOSION_PROTECTED(x, y))
5978     {
5979       KillPlayerUnlessExplosionProtected(x, y);
5980       border_explosion = TRUE;
5981     }
5982     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5983     {
5984       Tile[x][y] = Store2[x][y];
5985       Store2[x][y] = 0;
5986       Bang(x, y);
5987       border_explosion = TRUE;
5988     }
5989     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5990     {
5991       AmoebaToDiamond(x, y);
5992       Store2[x][y] = 0;
5993       border_explosion = TRUE;
5994     }
5995
5996     // if an element just explodes due to another explosion (chain-reaction),
5997     // do not immediately end the new explosion when it was the last frame of
5998     // the explosion (as it would be done in the following "if"-statement!)
5999     if (border_explosion && phase == last_phase)
6000       return;
6001   }
6002
6003   // this can happen if the player was just killed by an explosion
6004   if (GfxElement[x][y] == EL_UNDEFINED)
6005     GfxElement[x][y] = EL_EMPTY;
6006
6007   if (phase == last_phase)
6008   {
6009     int element;
6010
6011     element = Tile[x][y] = Store[x][y];
6012     Store[x][y] = Store2[x][y] = 0;
6013     GfxElement[x][y] = EL_UNDEFINED;
6014
6015     // player can escape from explosions and might therefore be still alive
6016     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6017         element <= EL_PLAYER_IS_EXPLODING_4)
6018     {
6019       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6020       int explosion_element = EL_PLAYER_1 + player_nr;
6021       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6022       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6023
6024       if (level.use_explosion_element[player_nr])
6025         explosion_element = level.explosion_element[player_nr];
6026
6027       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6028                     element_info[explosion_element].content.e[xx][yy]);
6029     }
6030
6031     // restore probably existing indestructible background element
6032     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6033       element = Tile[x][y] = Back[x][y];
6034     Back[x][y] = 0;
6035
6036     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6037     GfxDir[x][y] = MV_NONE;
6038     ChangeDelay[x][y] = 0;
6039     ChangePage[x][y] = -1;
6040
6041     CustomValue[x][y] = 0;
6042
6043     InitField_WithBug2(x, y, FALSE);
6044
6045     TEST_DrawLevelField(x, y);
6046
6047     TestIfElementTouchesCustomElement(x, y);
6048
6049     if (GFX_CRUMBLED(element))
6050       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6051
6052     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6053       StorePlayer[x][y] = 0;
6054
6055     if (IS_PLAYER_ELEMENT(element))
6056       RelocatePlayer(x, y, element);
6057   }
6058   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6059   {
6060     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6061     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6062
6063     if (phase == delay)
6064       TEST_DrawLevelFieldCrumbled(x, y);
6065
6066     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6067     {
6068       DrawLevelElement(x, y, Back[x][y]);
6069       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6070     }
6071     else if (IS_WALKABLE_UNDER(Back[x][y]))
6072     {
6073       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6074       DrawLevelElementThruMask(x, y, Back[x][y]);
6075     }
6076     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6077       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6078   }
6079 }
6080
6081 static void DynaExplode(int ex, int ey)
6082 {
6083   int i, j;
6084   int dynabomb_element = Tile[ex][ey];
6085   int dynabomb_size = 1;
6086   boolean dynabomb_xl = FALSE;
6087   struct PlayerInfo *player;
6088   static int xy[4][2] =
6089   {
6090     { 0, -1 },
6091     { -1, 0 },
6092     { +1, 0 },
6093     { 0, +1 }
6094   };
6095
6096   if (IS_ACTIVE_BOMB(dynabomb_element))
6097   {
6098     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6099     dynabomb_size = player->dynabomb_size;
6100     dynabomb_xl = player->dynabomb_xl;
6101     player->dynabombs_left++;
6102   }
6103
6104   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6105
6106   for (i = 0; i < NUM_DIRECTIONS; i++)
6107   {
6108     for (j = 1; j <= dynabomb_size; j++)
6109     {
6110       int x = ex + j * xy[i][0];
6111       int y = ey + j * xy[i][1];
6112       int element;
6113
6114       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6115         break;
6116
6117       element = Tile[x][y];
6118
6119       // do not restart explosions of fields with active bombs
6120       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6121         continue;
6122
6123       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6124
6125       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6126           !IS_DIGGABLE(element) && !dynabomb_xl)
6127         break;
6128     }
6129   }
6130 }
6131
6132 void Bang(int x, int y)
6133 {
6134   int element = MovingOrBlocked2Element(x, y);
6135   int explosion_type = EX_TYPE_NORMAL;
6136
6137   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6138   {
6139     struct PlayerInfo *player = PLAYERINFO(x, y);
6140
6141     element = Tile[x][y] = player->initial_element;
6142
6143     if (level.use_explosion_element[player->index_nr])
6144     {
6145       int explosion_element = level.explosion_element[player->index_nr];
6146
6147       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6148         explosion_type = EX_TYPE_CROSS;
6149       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6150         explosion_type = EX_TYPE_CENTER;
6151     }
6152   }
6153
6154   switch (element)
6155   {
6156     case EL_BUG:
6157     case EL_SPACESHIP:
6158     case EL_BD_BUTTERFLY:
6159     case EL_BD_FIREFLY:
6160     case EL_YAMYAM:
6161     case EL_DARK_YAMYAM:
6162     case EL_ROBOT:
6163     case EL_PACMAN:
6164     case EL_MOLE:
6165       RaiseScoreElement(element);
6166       break;
6167
6168     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6169     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6170     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6171     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6172     case EL_DYNABOMB_INCREASE_NUMBER:
6173     case EL_DYNABOMB_INCREASE_SIZE:
6174     case EL_DYNABOMB_INCREASE_POWER:
6175       explosion_type = EX_TYPE_DYNA;
6176       break;
6177
6178     case EL_DC_LANDMINE:
6179       explosion_type = EX_TYPE_CENTER;
6180       break;
6181
6182     case EL_PENGUIN:
6183     case EL_LAMP:
6184     case EL_LAMP_ACTIVE:
6185     case EL_AMOEBA_TO_DIAMOND:
6186       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6187         explosion_type = EX_TYPE_CENTER;
6188       break;
6189
6190     default:
6191       if (element_info[element].explosion_type == EXPLODES_CROSS)
6192         explosion_type = EX_TYPE_CROSS;
6193       else if (element_info[element].explosion_type == EXPLODES_1X1)
6194         explosion_type = EX_TYPE_CENTER;
6195       break;
6196   }
6197
6198   if (explosion_type == EX_TYPE_DYNA)
6199     DynaExplode(x, y);
6200   else
6201     Explode(x, y, EX_PHASE_START, explosion_type);
6202
6203   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6204 }
6205
6206 static void SplashAcid(int x, int y)
6207 {
6208   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6209       (!IN_LEV_FIELD(x - 1, y - 2) ||
6210        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6211     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6212
6213   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6214       (!IN_LEV_FIELD(x + 1, y - 2) ||
6215        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6216     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6217
6218   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6219 }
6220
6221 static void InitBeltMovement(void)
6222 {
6223   static int belt_base_element[4] =
6224   {
6225     EL_CONVEYOR_BELT_1_LEFT,
6226     EL_CONVEYOR_BELT_2_LEFT,
6227     EL_CONVEYOR_BELT_3_LEFT,
6228     EL_CONVEYOR_BELT_4_LEFT
6229   };
6230   static int belt_base_active_element[4] =
6231   {
6232     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6233     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6234     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6235     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6236   };
6237
6238   int x, y, i, j;
6239
6240   // set frame order for belt animation graphic according to belt direction
6241   for (i = 0; i < NUM_BELTS; i++)
6242   {
6243     int belt_nr = i;
6244
6245     for (j = 0; j < NUM_BELT_PARTS; j++)
6246     {
6247       int element = belt_base_active_element[belt_nr] + j;
6248       int graphic_1 = el2img(element);
6249       int graphic_2 = el2panelimg(element);
6250
6251       if (game.belt_dir[i] == MV_LEFT)
6252       {
6253         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6254         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6255       }
6256       else
6257       {
6258         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6259         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6260       }
6261     }
6262   }
6263
6264   SCAN_PLAYFIELD(x, y)
6265   {
6266     int element = Tile[x][y];
6267
6268     for (i = 0; i < NUM_BELTS; i++)
6269     {
6270       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6271       {
6272         int e_belt_nr = getBeltNrFromBeltElement(element);
6273         int belt_nr = i;
6274
6275         if (e_belt_nr == belt_nr)
6276         {
6277           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6278
6279           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6280         }
6281       }
6282     }
6283   }
6284 }
6285
6286 static void ToggleBeltSwitch(int x, int y)
6287 {
6288   static int belt_base_element[4] =
6289   {
6290     EL_CONVEYOR_BELT_1_LEFT,
6291     EL_CONVEYOR_BELT_2_LEFT,
6292     EL_CONVEYOR_BELT_3_LEFT,
6293     EL_CONVEYOR_BELT_4_LEFT
6294   };
6295   static int belt_base_active_element[4] =
6296   {
6297     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6298     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6299     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6300     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6301   };
6302   static int belt_base_switch_element[4] =
6303   {
6304     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6305     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6306     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6307     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6308   };
6309   static int belt_move_dir[4] =
6310   {
6311     MV_LEFT,
6312     MV_NONE,
6313     MV_RIGHT,
6314     MV_NONE,
6315   };
6316
6317   int element = Tile[x][y];
6318   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6319   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6320   int belt_dir = belt_move_dir[belt_dir_nr];
6321   int xx, yy, i;
6322
6323   if (!IS_BELT_SWITCH(element))
6324     return;
6325
6326   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6327   game.belt_dir[belt_nr] = belt_dir;
6328
6329   if (belt_dir_nr == 3)
6330     belt_dir_nr = 1;
6331
6332   // set frame order for belt animation graphic according to belt direction
6333   for (i = 0; i < NUM_BELT_PARTS; i++)
6334   {
6335     int element = belt_base_active_element[belt_nr] + i;
6336     int graphic_1 = el2img(element);
6337     int graphic_2 = el2panelimg(element);
6338
6339     if (belt_dir == MV_LEFT)
6340     {
6341       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6342       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6343     }
6344     else
6345     {
6346       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6347       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6348     }
6349   }
6350
6351   SCAN_PLAYFIELD(xx, yy)
6352   {
6353     int element = Tile[xx][yy];
6354
6355     if (IS_BELT_SWITCH(element))
6356     {
6357       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6358
6359       if (e_belt_nr == belt_nr)
6360       {
6361         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6362         TEST_DrawLevelField(xx, yy);
6363       }
6364     }
6365     else if (IS_BELT(element) && belt_dir != MV_NONE)
6366     {
6367       int e_belt_nr = getBeltNrFromBeltElement(element);
6368
6369       if (e_belt_nr == belt_nr)
6370       {
6371         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6372
6373         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6374         TEST_DrawLevelField(xx, yy);
6375       }
6376     }
6377     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6378     {
6379       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6380
6381       if (e_belt_nr == belt_nr)
6382       {
6383         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6384
6385         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6386         TEST_DrawLevelField(xx, yy);
6387       }
6388     }
6389   }
6390 }
6391
6392 static void ToggleSwitchgateSwitch(int x, int y)
6393 {
6394   int xx, yy;
6395
6396   game.switchgate_pos = !game.switchgate_pos;
6397
6398   SCAN_PLAYFIELD(xx, yy)
6399   {
6400     int element = Tile[xx][yy];
6401
6402     if (element == EL_SWITCHGATE_SWITCH_UP)
6403     {
6404       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6405       TEST_DrawLevelField(xx, yy);
6406     }
6407     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6408     {
6409       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6410       TEST_DrawLevelField(xx, yy);
6411     }
6412     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6413     {
6414       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6415       TEST_DrawLevelField(xx, yy);
6416     }
6417     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6418     {
6419       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6420       TEST_DrawLevelField(xx, yy);
6421     }
6422     else if (element == EL_SWITCHGATE_OPEN ||
6423              element == EL_SWITCHGATE_OPENING)
6424     {
6425       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6426
6427       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6428     }
6429     else if (element == EL_SWITCHGATE_CLOSED ||
6430              element == EL_SWITCHGATE_CLOSING)
6431     {
6432       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6433
6434       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6435     }
6436   }
6437 }
6438
6439 static int getInvisibleActiveFromInvisibleElement(int element)
6440 {
6441   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6442           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6443           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6444           element);
6445 }
6446
6447 static int getInvisibleFromInvisibleActiveElement(int element)
6448 {
6449   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6450           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6451           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6452           element);
6453 }
6454
6455 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6456 {
6457   int x, y;
6458
6459   SCAN_PLAYFIELD(x, y)
6460   {
6461     int element = Tile[x][y];
6462
6463     if (element == EL_LIGHT_SWITCH &&
6464         game.light_time_left > 0)
6465     {
6466       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6467       TEST_DrawLevelField(x, y);
6468     }
6469     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6470              game.light_time_left == 0)
6471     {
6472       Tile[x][y] = EL_LIGHT_SWITCH;
6473       TEST_DrawLevelField(x, y);
6474     }
6475     else if (element == EL_EMC_DRIPPER &&
6476              game.light_time_left > 0)
6477     {
6478       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6479       TEST_DrawLevelField(x, y);
6480     }
6481     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6482              game.light_time_left == 0)
6483     {
6484       Tile[x][y] = EL_EMC_DRIPPER;
6485       TEST_DrawLevelField(x, y);
6486     }
6487     else if (element == EL_INVISIBLE_STEELWALL ||
6488              element == EL_INVISIBLE_WALL ||
6489              element == EL_INVISIBLE_SAND)
6490     {
6491       if (game.light_time_left > 0)
6492         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6493
6494       TEST_DrawLevelField(x, y);
6495
6496       // uncrumble neighbour fields, if needed
6497       if (element == EL_INVISIBLE_SAND)
6498         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6499     }
6500     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6501              element == EL_INVISIBLE_WALL_ACTIVE ||
6502              element == EL_INVISIBLE_SAND_ACTIVE)
6503     {
6504       if (game.light_time_left == 0)
6505         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6506
6507       TEST_DrawLevelField(x, y);
6508
6509       // re-crumble neighbour fields, if needed
6510       if (element == EL_INVISIBLE_SAND)
6511         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6512     }
6513   }
6514 }
6515
6516 static void RedrawAllInvisibleElementsForLenses(void)
6517 {
6518   int x, y;
6519
6520   SCAN_PLAYFIELD(x, y)
6521   {
6522     int element = Tile[x][y];
6523
6524     if (element == EL_EMC_DRIPPER &&
6525         game.lenses_time_left > 0)
6526     {
6527       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6528       TEST_DrawLevelField(x, y);
6529     }
6530     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6531              game.lenses_time_left == 0)
6532     {
6533       Tile[x][y] = EL_EMC_DRIPPER;
6534       TEST_DrawLevelField(x, y);
6535     }
6536     else if (element == EL_INVISIBLE_STEELWALL ||
6537              element == EL_INVISIBLE_WALL ||
6538              element == EL_INVISIBLE_SAND)
6539     {
6540       if (game.lenses_time_left > 0)
6541         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6542
6543       TEST_DrawLevelField(x, y);
6544
6545       // uncrumble neighbour fields, if needed
6546       if (element == EL_INVISIBLE_SAND)
6547         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6548     }
6549     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6550              element == EL_INVISIBLE_WALL_ACTIVE ||
6551              element == EL_INVISIBLE_SAND_ACTIVE)
6552     {
6553       if (game.lenses_time_left == 0)
6554         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6555
6556       TEST_DrawLevelField(x, y);
6557
6558       // re-crumble neighbour fields, if needed
6559       if (element == EL_INVISIBLE_SAND)
6560         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6561     }
6562   }
6563 }
6564
6565 static void RedrawAllInvisibleElementsForMagnifier(void)
6566 {
6567   int x, y;
6568
6569   SCAN_PLAYFIELD(x, y)
6570   {
6571     int element = Tile[x][y];
6572
6573     if (element == EL_EMC_FAKE_GRASS &&
6574         game.magnify_time_left > 0)
6575     {
6576       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6577       TEST_DrawLevelField(x, y);
6578     }
6579     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6580              game.magnify_time_left == 0)
6581     {
6582       Tile[x][y] = EL_EMC_FAKE_GRASS;
6583       TEST_DrawLevelField(x, y);
6584     }
6585     else if (IS_GATE_GRAY(element) &&
6586              game.magnify_time_left > 0)
6587     {
6588       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6589                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6590                     IS_EM_GATE_GRAY(element) ?
6591                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6592                     IS_EMC_GATE_GRAY(element) ?
6593                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6594                     IS_DC_GATE_GRAY(element) ?
6595                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6596                     element);
6597       TEST_DrawLevelField(x, y);
6598     }
6599     else if (IS_GATE_GRAY_ACTIVE(element) &&
6600              game.magnify_time_left == 0)
6601     {
6602       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6603                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6604                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6605                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6606                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6607                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6608                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6609                     EL_DC_GATE_WHITE_GRAY :
6610                     element);
6611       TEST_DrawLevelField(x, y);
6612     }
6613   }
6614 }
6615
6616 static void ToggleLightSwitch(int x, int y)
6617 {
6618   int element = Tile[x][y];
6619
6620   game.light_time_left =
6621     (element == EL_LIGHT_SWITCH ?
6622      level.time_light * FRAMES_PER_SECOND : 0);
6623
6624   RedrawAllLightSwitchesAndInvisibleElements();
6625 }
6626
6627 static void ActivateTimegateSwitch(int x, int y)
6628 {
6629   int xx, yy;
6630
6631   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6632
6633   SCAN_PLAYFIELD(xx, yy)
6634   {
6635     int element = Tile[xx][yy];
6636
6637     if (element == EL_TIMEGATE_CLOSED ||
6638         element == EL_TIMEGATE_CLOSING)
6639     {
6640       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6641       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6642     }
6643
6644     /*
6645     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6646     {
6647       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6648       TEST_DrawLevelField(xx, yy);
6649     }
6650     */
6651
6652   }
6653
6654   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6655                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6656 }
6657
6658 static void Impact(int x, int y)
6659 {
6660   boolean last_line = (y == lev_fieldy - 1);
6661   boolean object_hit = FALSE;
6662   boolean impact = (last_line || object_hit);
6663   int element = Tile[x][y];
6664   int smashed = EL_STEELWALL;
6665
6666   if (!last_line)       // check if element below was hit
6667   {
6668     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6669       return;
6670
6671     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6672                                          MovDir[x][y + 1] != MV_DOWN ||
6673                                          MovPos[x][y + 1] <= TILEY / 2));
6674
6675     // do not smash moving elements that left the smashed field in time
6676     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6677         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6678       object_hit = FALSE;
6679
6680 #if USE_QUICKSAND_IMPACT_BUGFIX
6681     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6682     {
6683       RemoveMovingField(x, y + 1);
6684       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6685       Tile[x][y + 2] = EL_ROCK;
6686       TEST_DrawLevelField(x, y + 2);
6687
6688       object_hit = TRUE;
6689     }
6690
6691     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6692     {
6693       RemoveMovingField(x, y + 1);
6694       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6695       Tile[x][y + 2] = EL_ROCK;
6696       TEST_DrawLevelField(x, y + 2);
6697
6698       object_hit = TRUE;
6699     }
6700 #endif
6701
6702     if (object_hit)
6703       smashed = MovingOrBlocked2Element(x, y + 1);
6704
6705     impact = (last_line || object_hit);
6706   }
6707
6708   if (!last_line && smashed == EL_ACID) // element falls into acid
6709   {
6710     SplashAcid(x, y + 1);
6711     return;
6712   }
6713
6714   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6715   // only reset graphic animation if graphic really changes after impact
6716   if (impact &&
6717       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6718   {
6719     ResetGfxAnimation(x, y);
6720     TEST_DrawLevelField(x, y);
6721   }
6722
6723   if (impact && CAN_EXPLODE_IMPACT(element))
6724   {
6725     Bang(x, y);
6726     return;
6727   }
6728   else if (impact && element == EL_PEARL &&
6729            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6730   {
6731     ResetGfxAnimation(x, y);
6732
6733     Tile[x][y] = EL_PEARL_BREAKING;
6734     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6735     return;
6736   }
6737   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6738   {
6739     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6740
6741     return;
6742   }
6743
6744   if (impact && element == EL_AMOEBA_DROP)
6745   {
6746     if (object_hit && IS_PLAYER(x, y + 1))
6747       KillPlayerUnlessEnemyProtected(x, y + 1);
6748     else if (object_hit && smashed == EL_PENGUIN)
6749       Bang(x, y + 1);
6750     else
6751     {
6752       Tile[x][y] = EL_AMOEBA_GROWING;
6753       Store[x][y] = EL_AMOEBA_WET;
6754
6755       ResetRandomAnimationValue(x, y);
6756     }
6757     return;
6758   }
6759
6760   if (object_hit)               // check which object was hit
6761   {
6762     if ((CAN_PASS_MAGIC_WALL(element) && 
6763          (smashed == EL_MAGIC_WALL ||
6764           smashed == EL_BD_MAGIC_WALL)) ||
6765         (CAN_PASS_DC_MAGIC_WALL(element) &&
6766          smashed == EL_DC_MAGIC_WALL))
6767     {
6768       int xx, yy;
6769       int activated_magic_wall =
6770         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6771          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6772          EL_DC_MAGIC_WALL_ACTIVE);
6773
6774       // activate magic wall / mill
6775       SCAN_PLAYFIELD(xx, yy)
6776       {
6777         if (Tile[xx][yy] == smashed)
6778           Tile[xx][yy] = activated_magic_wall;
6779       }
6780
6781       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6782       game.magic_wall_active = TRUE;
6783
6784       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6785                             SND_MAGIC_WALL_ACTIVATING :
6786                             smashed == EL_BD_MAGIC_WALL ?
6787                             SND_BD_MAGIC_WALL_ACTIVATING :
6788                             SND_DC_MAGIC_WALL_ACTIVATING));
6789     }
6790
6791     if (IS_PLAYER(x, y + 1))
6792     {
6793       if (CAN_SMASH_PLAYER(element))
6794       {
6795         KillPlayerUnlessEnemyProtected(x, y + 1);
6796         return;
6797       }
6798     }
6799     else if (smashed == EL_PENGUIN)
6800     {
6801       if (CAN_SMASH_PLAYER(element))
6802       {
6803         Bang(x, y + 1);
6804         return;
6805       }
6806     }
6807     else if (element == EL_BD_DIAMOND)
6808     {
6809       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6810       {
6811         Bang(x, y + 1);
6812         return;
6813       }
6814     }
6815     else if (((element == EL_SP_INFOTRON ||
6816                element == EL_SP_ZONK) &&
6817               (smashed == EL_SP_SNIKSNAK ||
6818                smashed == EL_SP_ELECTRON ||
6819                smashed == EL_SP_DISK_ORANGE)) ||
6820              (element == EL_SP_INFOTRON &&
6821               smashed == EL_SP_DISK_YELLOW))
6822     {
6823       Bang(x, y + 1);
6824       return;
6825     }
6826     else if (CAN_SMASH_EVERYTHING(element))
6827     {
6828       if (IS_CLASSIC_ENEMY(smashed) ||
6829           CAN_EXPLODE_SMASHED(smashed))
6830       {
6831         Bang(x, y + 1);
6832         return;
6833       }
6834       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6835       {
6836         if (smashed == EL_LAMP ||
6837             smashed == EL_LAMP_ACTIVE)
6838         {
6839           Bang(x, y + 1);
6840           return;
6841         }
6842         else if (smashed == EL_NUT)
6843         {
6844           Tile[x][y + 1] = EL_NUT_BREAKING;
6845           PlayLevelSound(x, y, SND_NUT_BREAKING);
6846           RaiseScoreElement(EL_NUT);
6847           return;
6848         }
6849         else if (smashed == EL_PEARL)
6850         {
6851           ResetGfxAnimation(x, y);
6852
6853           Tile[x][y + 1] = EL_PEARL_BREAKING;
6854           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6855           return;
6856         }
6857         else if (smashed == EL_DIAMOND)
6858         {
6859           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6860           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6861           return;
6862         }
6863         else if (IS_BELT_SWITCH(smashed))
6864         {
6865           ToggleBeltSwitch(x, y + 1);
6866         }
6867         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6868                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6869                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6870                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6871         {
6872           ToggleSwitchgateSwitch(x, y + 1);
6873         }
6874         else if (smashed == EL_LIGHT_SWITCH ||
6875                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6876         {
6877           ToggleLightSwitch(x, y + 1);
6878         }
6879         else
6880         {
6881           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6882
6883           CheckElementChangeBySide(x, y + 1, smashed, element,
6884                                    CE_SWITCHED, CH_SIDE_TOP);
6885           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6886                                             CH_SIDE_TOP);
6887         }
6888       }
6889       else
6890       {
6891         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6892       }
6893     }
6894   }
6895
6896   // play sound of magic wall / mill
6897   if (!last_line &&
6898       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6899        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6900        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6901   {
6902     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6903       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6904     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6905       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6906     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6907       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6908
6909     return;
6910   }
6911
6912   // play sound of object that hits the ground
6913   if (last_line || object_hit)
6914     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6915 }
6916
6917 static void TurnRoundExt(int x, int y)
6918 {
6919   static struct
6920   {
6921     int dx, dy;
6922   } move_xy[] =
6923   {
6924     {  0,  0 },
6925     { -1,  0 },
6926     { +1,  0 },
6927     {  0,  0 },
6928     {  0, -1 },
6929     {  0,  0 }, { 0, 0 }, { 0, 0 },
6930     {  0, +1 }
6931   };
6932   static struct
6933   {
6934     int left, right, back;
6935   } turn[] =
6936   {
6937     { 0,        0,              0        },
6938     { MV_DOWN,  MV_UP,          MV_RIGHT },
6939     { MV_UP,    MV_DOWN,        MV_LEFT  },
6940     { 0,        0,              0        },
6941     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6942     { 0,        0,              0        },
6943     { 0,        0,              0        },
6944     { 0,        0,              0        },
6945     { MV_RIGHT, MV_LEFT,        MV_UP    }
6946   };
6947
6948   int element = Tile[x][y];
6949   int move_pattern = element_info[element].move_pattern;
6950
6951   int old_move_dir = MovDir[x][y];
6952   int left_dir  = turn[old_move_dir].left;
6953   int right_dir = turn[old_move_dir].right;
6954   int back_dir  = turn[old_move_dir].back;
6955
6956   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6957   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6958   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6959   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6960
6961   int left_x  = x + left_dx,  left_y  = y + left_dy;
6962   int right_x = x + right_dx, right_y = y + right_dy;
6963   int move_x  = x + move_dx,  move_y  = y + move_dy;
6964
6965   int xx, yy;
6966
6967   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6968   {
6969     TestIfBadThingTouchesOtherBadThing(x, y);
6970
6971     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6972       MovDir[x][y] = right_dir;
6973     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6974       MovDir[x][y] = left_dir;
6975
6976     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6977       MovDelay[x][y] = 9;
6978     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6979       MovDelay[x][y] = 1;
6980   }
6981   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6982   {
6983     TestIfBadThingTouchesOtherBadThing(x, y);
6984
6985     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6986       MovDir[x][y] = left_dir;
6987     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6988       MovDir[x][y] = right_dir;
6989
6990     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6991       MovDelay[x][y] = 9;
6992     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6993       MovDelay[x][y] = 1;
6994   }
6995   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6996   {
6997     TestIfBadThingTouchesOtherBadThing(x, y);
6998
6999     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7000       MovDir[x][y] = left_dir;
7001     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7002       MovDir[x][y] = right_dir;
7003
7004     if (MovDir[x][y] != old_move_dir)
7005       MovDelay[x][y] = 9;
7006   }
7007   else if (element == EL_YAMYAM)
7008   {
7009     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7010     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7011
7012     if (can_turn_left && can_turn_right)
7013       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7014     else if (can_turn_left)
7015       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7016     else if (can_turn_right)
7017       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7018     else
7019       MovDir[x][y] = back_dir;
7020
7021     MovDelay[x][y] = 16 + 16 * RND(3);
7022   }
7023   else if (element == EL_DARK_YAMYAM)
7024   {
7025     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7026                                                          left_x, left_y);
7027     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7028                                                          right_x, right_y);
7029
7030     if (can_turn_left && can_turn_right)
7031       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7032     else if (can_turn_left)
7033       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7034     else if (can_turn_right)
7035       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7036     else
7037       MovDir[x][y] = back_dir;
7038
7039     MovDelay[x][y] = 16 + 16 * RND(3);
7040   }
7041   else if (element == EL_PACMAN)
7042   {
7043     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7044     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7045
7046     if (can_turn_left && can_turn_right)
7047       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7048     else if (can_turn_left)
7049       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7050     else if (can_turn_right)
7051       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7052     else
7053       MovDir[x][y] = back_dir;
7054
7055     MovDelay[x][y] = 6 + RND(40);
7056   }
7057   else if (element == EL_PIG)
7058   {
7059     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7060     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7061     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7062     boolean should_turn_left, should_turn_right, should_move_on;
7063     int rnd_value = 24;
7064     int rnd = RND(rnd_value);
7065
7066     should_turn_left = (can_turn_left &&
7067                         (!can_move_on ||
7068                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7069                                                    y + back_dy + left_dy)));
7070     should_turn_right = (can_turn_right &&
7071                          (!can_move_on ||
7072                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7073                                                     y + back_dy + right_dy)));
7074     should_move_on = (can_move_on &&
7075                       (!can_turn_left ||
7076                        !can_turn_right ||
7077                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7078                                                  y + move_dy + left_dy) ||
7079                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7080                                                  y + move_dy + right_dy)));
7081
7082     if (should_turn_left || should_turn_right || should_move_on)
7083     {
7084       if (should_turn_left && should_turn_right && should_move_on)
7085         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7086                         rnd < 2 * rnd_value / 3 ? right_dir :
7087                         old_move_dir);
7088       else if (should_turn_left && should_turn_right)
7089         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7090       else if (should_turn_left && should_move_on)
7091         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7092       else if (should_turn_right && should_move_on)
7093         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7094       else if (should_turn_left)
7095         MovDir[x][y] = left_dir;
7096       else if (should_turn_right)
7097         MovDir[x][y] = right_dir;
7098       else if (should_move_on)
7099         MovDir[x][y] = old_move_dir;
7100     }
7101     else if (can_move_on && rnd > rnd_value / 8)
7102       MovDir[x][y] = old_move_dir;
7103     else if (can_turn_left && can_turn_right)
7104       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7105     else if (can_turn_left && rnd > rnd_value / 8)
7106       MovDir[x][y] = left_dir;
7107     else if (can_turn_right && rnd > rnd_value/8)
7108       MovDir[x][y] = right_dir;
7109     else
7110       MovDir[x][y] = back_dir;
7111
7112     xx = x + move_xy[MovDir[x][y]].dx;
7113     yy = y + move_xy[MovDir[x][y]].dy;
7114
7115     if (!IN_LEV_FIELD(xx, yy) ||
7116         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7117       MovDir[x][y] = old_move_dir;
7118
7119     MovDelay[x][y] = 0;
7120   }
7121   else if (element == EL_DRAGON)
7122   {
7123     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7124     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7125     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7126     int rnd_value = 24;
7127     int rnd = RND(rnd_value);
7128
7129     if (can_move_on && rnd > rnd_value / 8)
7130       MovDir[x][y] = old_move_dir;
7131     else if (can_turn_left && can_turn_right)
7132       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7133     else if (can_turn_left && rnd > rnd_value / 8)
7134       MovDir[x][y] = left_dir;
7135     else if (can_turn_right && rnd > rnd_value / 8)
7136       MovDir[x][y] = right_dir;
7137     else
7138       MovDir[x][y] = back_dir;
7139
7140     xx = x + move_xy[MovDir[x][y]].dx;
7141     yy = y + move_xy[MovDir[x][y]].dy;
7142
7143     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7144       MovDir[x][y] = old_move_dir;
7145
7146     MovDelay[x][y] = 0;
7147   }
7148   else if (element == EL_MOLE)
7149   {
7150     boolean can_move_on =
7151       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7152                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7153                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7154     if (!can_move_on)
7155     {
7156       boolean can_turn_left =
7157         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7158                               IS_AMOEBOID(Tile[left_x][left_y])));
7159
7160       boolean can_turn_right =
7161         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7162                               IS_AMOEBOID(Tile[right_x][right_y])));
7163
7164       if (can_turn_left && can_turn_right)
7165         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7166       else if (can_turn_left)
7167         MovDir[x][y] = left_dir;
7168       else
7169         MovDir[x][y] = right_dir;
7170     }
7171
7172     if (MovDir[x][y] != old_move_dir)
7173       MovDelay[x][y] = 9;
7174   }
7175   else if (element == EL_BALLOON)
7176   {
7177     MovDir[x][y] = game.wind_direction;
7178     MovDelay[x][y] = 0;
7179   }
7180   else if (element == EL_SPRING)
7181   {
7182     if (MovDir[x][y] & MV_HORIZONTAL)
7183     {
7184       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7185           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7186       {
7187         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7188         ResetGfxAnimation(move_x, move_y);
7189         TEST_DrawLevelField(move_x, move_y);
7190
7191         MovDir[x][y] = back_dir;
7192       }
7193       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7194                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7195         MovDir[x][y] = MV_NONE;
7196     }
7197
7198     MovDelay[x][y] = 0;
7199   }
7200   else if (element == EL_ROBOT ||
7201            element == EL_SATELLITE ||
7202            element == EL_PENGUIN ||
7203            element == EL_EMC_ANDROID)
7204   {
7205     int attr_x = -1, attr_y = -1;
7206
7207     if (game.all_players_gone)
7208     {
7209       attr_x = game.exit_x;
7210       attr_y = game.exit_y;
7211     }
7212     else
7213     {
7214       int i;
7215
7216       for (i = 0; i < MAX_PLAYERS; i++)
7217       {
7218         struct PlayerInfo *player = &stored_player[i];
7219         int jx = player->jx, jy = player->jy;
7220
7221         if (!player->active)
7222           continue;
7223
7224         if (attr_x == -1 ||
7225             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7226         {
7227           attr_x = jx;
7228           attr_y = jy;
7229         }
7230       }
7231     }
7232
7233     if (element == EL_ROBOT &&
7234         game.robot_wheel_x >= 0 &&
7235         game.robot_wheel_y >= 0 &&
7236         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7237          game.engine_version < VERSION_IDENT(3,1,0,0)))
7238     {
7239       attr_x = game.robot_wheel_x;
7240       attr_y = game.robot_wheel_y;
7241     }
7242
7243     if (element == EL_PENGUIN)
7244     {
7245       int i;
7246       static int xy[4][2] =
7247       {
7248         { 0, -1 },
7249         { -1, 0 },
7250         { +1, 0 },
7251         { 0, +1 }
7252       };
7253
7254       for (i = 0; i < NUM_DIRECTIONS; i++)
7255       {
7256         int ex = x + xy[i][0];
7257         int ey = y + xy[i][1];
7258
7259         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7260                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7261                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7262                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7263         {
7264           attr_x = ex;
7265           attr_y = ey;
7266           break;
7267         }
7268       }
7269     }
7270
7271     MovDir[x][y] = MV_NONE;
7272     if (attr_x < x)
7273       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7274     else if (attr_x > x)
7275       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7276     if (attr_y < y)
7277       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7278     else if (attr_y > y)
7279       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7280
7281     if (element == EL_ROBOT)
7282     {
7283       int newx, newy;
7284
7285       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7286         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7287       Moving2Blocked(x, y, &newx, &newy);
7288
7289       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7290         MovDelay[x][y] = 8 + 8 * !RND(3);
7291       else
7292         MovDelay[x][y] = 16;
7293     }
7294     else if (element == EL_PENGUIN)
7295     {
7296       int newx, newy;
7297
7298       MovDelay[x][y] = 1;
7299
7300       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301       {
7302         boolean first_horiz = RND(2);
7303         int new_move_dir = MovDir[x][y];
7304
7305         MovDir[x][y] =
7306           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7307         Moving2Blocked(x, y, &newx, &newy);
7308
7309         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7310           return;
7311
7312         MovDir[x][y] =
7313           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7314         Moving2Blocked(x, y, &newx, &newy);
7315
7316         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7317           return;
7318
7319         MovDir[x][y] = old_move_dir;
7320         return;
7321       }
7322     }
7323     else if (element == EL_SATELLITE)
7324     {
7325       int newx, newy;
7326
7327       MovDelay[x][y] = 1;
7328
7329       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7330       {
7331         boolean first_horiz = RND(2);
7332         int new_move_dir = MovDir[x][y];
7333
7334         MovDir[x][y] =
7335           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7336         Moving2Blocked(x, y, &newx, &newy);
7337
7338         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7339           return;
7340
7341         MovDir[x][y] =
7342           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7343         Moving2Blocked(x, y, &newx, &newy);
7344
7345         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7346           return;
7347
7348         MovDir[x][y] = old_move_dir;
7349         return;
7350       }
7351     }
7352     else if (element == EL_EMC_ANDROID)
7353     {
7354       static int check_pos[16] =
7355       {
7356         -1,             //  0 => (invalid)
7357         7,              //  1 => MV_LEFT
7358         3,              //  2 => MV_RIGHT
7359         -1,             //  3 => (invalid)
7360         1,              //  4 =>            MV_UP
7361         0,              //  5 => MV_LEFT  | MV_UP
7362         2,              //  6 => MV_RIGHT | MV_UP
7363         -1,             //  7 => (invalid)
7364         5,              //  8 =>            MV_DOWN
7365         6,              //  9 => MV_LEFT  | MV_DOWN
7366         4,              // 10 => MV_RIGHT | MV_DOWN
7367         -1,             // 11 => (invalid)
7368         -1,             // 12 => (invalid)
7369         -1,             // 13 => (invalid)
7370         -1,             // 14 => (invalid)
7371         -1,             // 15 => (invalid)
7372       };
7373       static struct
7374       {
7375         int dx, dy;
7376         int dir;
7377       } check_xy[8] =
7378       {
7379         { -1, -1,       MV_LEFT  | MV_UP   },
7380         {  0, -1,                  MV_UP   },
7381         { +1, -1,       MV_RIGHT | MV_UP   },
7382         { +1,  0,       MV_RIGHT           },
7383         { +1, +1,       MV_RIGHT | MV_DOWN },
7384         {  0, +1,                  MV_DOWN },
7385         { -1, +1,       MV_LEFT  | MV_DOWN },
7386         { -1,  0,       MV_LEFT            },
7387       };
7388       int start_pos, check_order;
7389       boolean can_clone = FALSE;
7390       int i;
7391
7392       // check if there is any free field around current position
7393       for (i = 0; i < 8; i++)
7394       {
7395         int newx = x + check_xy[i].dx;
7396         int newy = y + check_xy[i].dy;
7397
7398         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7399         {
7400           can_clone = TRUE;
7401
7402           break;
7403         }
7404       }
7405
7406       if (can_clone)            // randomly find an element to clone
7407       {
7408         can_clone = FALSE;
7409
7410         start_pos = check_pos[RND(8)];
7411         check_order = (RND(2) ? -1 : +1);
7412
7413         for (i = 0; i < 8; i++)
7414         {
7415           int pos_raw = start_pos + i * check_order;
7416           int pos = (pos_raw + 8) % 8;
7417           int newx = x + check_xy[pos].dx;
7418           int newy = y + check_xy[pos].dy;
7419
7420           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7421           {
7422             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7423             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7424
7425             Store[x][y] = Tile[newx][newy];
7426
7427             can_clone = TRUE;
7428
7429             break;
7430           }
7431         }
7432       }
7433
7434       if (can_clone)            // randomly find a direction to move
7435       {
7436         can_clone = FALSE;
7437
7438         start_pos = check_pos[RND(8)];
7439         check_order = (RND(2) ? -1 : +1);
7440
7441         for (i = 0; i < 8; i++)
7442         {
7443           int pos_raw = start_pos + i * check_order;
7444           int pos = (pos_raw + 8) % 8;
7445           int newx = x + check_xy[pos].dx;
7446           int newy = y + check_xy[pos].dy;
7447           int new_move_dir = check_xy[pos].dir;
7448
7449           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7450           {
7451             MovDir[x][y] = new_move_dir;
7452             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7453
7454             can_clone = TRUE;
7455
7456             break;
7457           }
7458         }
7459       }
7460
7461       if (can_clone)            // cloning and moving successful
7462         return;
7463
7464       // cannot clone -- try to move towards player
7465
7466       start_pos = check_pos[MovDir[x][y] & 0x0f];
7467       check_order = (RND(2) ? -1 : +1);
7468
7469       for (i = 0; i < 3; i++)
7470       {
7471         // first check start_pos, then previous/next or (next/previous) pos
7472         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7473         int pos = (pos_raw + 8) % 8;
7474         int newx = x + check_xy[pos].dx;
7475         int newy = y + check_xy[pos].dy;
7476         int new_move_dir = check_xy[pos].dir;
7477
7478         if (IS_PLAYER(newx, newy))
7479           break;
7480
7481         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7482         {
7483           MovDir[x][y] = new_move_dir;
7484           MovDelay[x][y] = level.android_move_time * 8 + 1;
7485
7486           break;
7487         }
7488       }
7489     }
7490   }
7491   else if (move_pattern == MV_TURNING_LEFT ||
7492            move_pattern == MV_TURNING_RIGHT ||
7493            move_pattern == MV_TURNING_LEFT_RIGHT ||
7494            move_pattern == MV_TURNING_RIGHT_LEFT ||
7495            move_pattern == MV_TURNING_RANDOM ||
7496            move_pattern == MV_ALL_DIRECTIONS)
7497   {
7498     boolean can_turn_left =
7499       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7500     boolean can_turn_right =
7501       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7502
7503     if (element_info[element].move_stepsize == 0)       // "not moving"
7504       return;
7505
7506     if (move_pattern == MV_TURNING_LEFT)
7507       MovDir[x][y] = left_dir;
7508     else if (move_pattern == MV_TURNING_RIGHT)
7509       MovDir[x][y] = right_dir;
7510     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7511       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7512     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7513       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7514     else if (move_pattern == MV_TURNING_RANDOM)
7515       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7516                       can_turn_right && !can_turn_left ? right_dir :
7517                       RND(2) ? left_dir : right_dir);
7518     else if (can_turn_left && can_turn_right)
7519       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7520     else if (can_turn_left)
7521       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7522     else if (can_turn_right)
7523       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7524     else
7525       MovDir[x][y] = back_dir;
7526
7527     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7528   }
7529   else if (move_pattern == MV_HORIZONTAL ||
7530            move_pattern == MV_VERTICAL)
7531   {
7532     if (move_pattern & old_move_dir)
7533       MovDir[x][y] = back_dir;
7534     else if (move_pattern == MV_HORIZONTAL)
7535       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7536     else if (move_pattern == MV_VERTICAL)
7537       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7538
7539     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7540   }
7541   else if (move_pattern & MV_ANY_DIRECTION)
7542   {
7543     MovDir[x][y] = move_pattern;
7544     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7545   }
7546   else if (move_pattern & MV_WIND_DIRECTION)
7547   {
7548     MovDir[x][y] = game.wind_direction;
7549     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7550   }
7551   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7552   {
7553     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7554       MovDir[x][y] = left_dir;
7555     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7556       MovDir[x][y] = right_dir;
7557
7558     if (MovDir[x][y] != old_move_dir)
7559       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7562   {
7563     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7564       MovDir[x][y] = right_dir;
7565     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7566       MovDir[x][y] = left_dir;
7567
7568     if (MovDir[x][y] != old_move_dir)
7569       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7570   }
7571   else if (move_pattern == MV_TOWARDS_PLAYER ||
7572            move_pattern == MV_AWAY_FROM_PLAYER)
7573   {
7574     int attr_x = -1, attr_y = -1;
7575     int newx, newy;
7576     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7577
7578     if (game.all_players_gone)
7579     {
7580       attr_x = game.exit_x;
7581       attr_y = game.exit_y;
7582     }
7583     else
7584     {
7585       int i;
7586
7587       for (i = 0; i < MAX_PLAYERS; i++)
7588       {
7589         struct PlayerInfo *player = &stored_player[i];
7590         int jx = player->jx, jy = player->jy;
7591
7592         if (!player->active)
7593           continue;
7594
7595         if (attr_x == -1 ||
7596             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7597         {
7598           attr_x = jx;
7599           attr_y = jy;
7600         }
7601       }
7602     }
7603
7604     MovDir[x][y] = MV_NONE;
7605     if (attr_x < x)
7606       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7607     else if (attr_x > x)
7608       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7609     if (attr_y < y)
7610       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7611     else if (attr_y > y)
7612       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7613
7614     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7615
7616     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7617     {
7618       boolean first_horiz = RND(2);
7619       int new_move_dir = MovDir[x][y];
7620
7621       if (element_info[element].move_stepsize == 0)     // "not moving"
7622       {
7623         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7624         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7625
7626         return;
7627       }
7628
7629       MovDir[x][y] =
7630         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7631       Moving2Blocked(x, y, &newx, &newy);
7632
7633       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7634         return;
7635
7636       MovDir[x][y] =
7637         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7638       Moving2Blocked(x, y, &newx, &newy);
7639
7640       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7641         return;
7642
7643       MovDir[x][y] = old_move_dir;
7644     }
7645   }
7646   else if (move_pattern == MV_WHEN_PUSHED ||
7647            move_pattern == MV_WHEN_DROPPED)
7648   {
7649     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7650       MovDir[x][y] = MV_NONE;
7651
7652     MovDelay[x][y] = 0;
7653   }
7654   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7655   {
7656     static int test_xy[7][2] =
7657     {
7658       { 0, -1 },
7659       { -1, 0 },
7660       { +1, 0 },
7661       { 0, +1 },
7662       { 0, -1 },
7663       { -1, 0 },
7664       { +1, 0 },
7665     };
7666     static int test_dir[7] =
7667     {
7668       MV_UP,
7669       MV_LEFT,
7670       MV_RIGHT,
7671       MV_DOWN,
7672       MV_UP,
7673       MV_LEFT,
7674       MV_RIGHT,
7675     };
7676     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7677     int move_preference = -1000000;     // start with very low preference
7678     int new_move_dir = MV_NONE;
7679     int start_test = RND(4);
7680     int i;
7681
7682     for (i = 0; i < NUM_DIRECTIONS; i++)
7683     {
7684       int move_dir = test_dir[start_test + i];
7685       int move_dir_preference;
7686
7687       xx = x + test_xy[start_test + i][0];
7688       yy = y + test_xy[start_test + i][1];
7689
7690       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7691           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7692       {
7693         new_move_dir = move_dir;
7694
7695         break;
7696       }
7697
7698       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7699         continue;
7700
7701       move_dir_preference = -1 * RunnerVisit[xx][yy];
7702       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7703         move_dir_preference = PlayerVisit[xx][yy];
7704
7705       if (move_dir_preference > move_preference)
7706       {
7707         // prefer field that has not been visited for the longest time
7708         move_preference = move_dir_preference;
7709         new_move_dir = move_dir;
7710       }
7711       else if (move_dir_preference == move_preference &&
7712                move_dir == old_move_dir)
7713       {
7714         // prefer last direction when all directions are preferred equally
7715         move_preference = move_dir_preference;
7716         new_move_dir = move_dir;
7717       }
7718     }
7719
7720     MovDir[x][y] = new_move_dir;
7721     if (old_move_dir != new_move_dir)
7722       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7723   }
7724 }
7725
7726 static void TurnRound(int x, int y)
7727 {
7728   int direction = MovDir[x][y];
7729
7730   TurnRoundExt(x, y);
7731
7732   GfxDir[x][y] = MovDir[x][y];
7733
7734   if (direction != MovDir[x][y])
7735     GfxFrame[x][y] = 0;
7736
7737   if (MovDelay[x][y])
7738     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7739
7740   ResetGfxFrame(x, y);
7741 }
7742
7743 static boolean JustBeingPushed(int x, int y)
7744 {
7745   int i;
7746
7747   for (i = 0; i < MAX_PLAYERS; i++)
7748   {
7749     struct PlayerInfo *player = &stored_player[i];
7750
7751     if (player->active && player->is_pushing && player->MovPos)
7752     {
7753       int next_jx = player->jx + (player->jx - player->last_jx);
7754       int next_jy = player->jy + (player->jy - player->last_jy);
7755
7756       if (x == next_jx && y == next_jy)
7757         return TRUE;
7758     }
7759   }
7760
7761   return FALSE;
7762 }
7763
7764 static void StartMoving(int x, int y)
7765 {
7766   boolean started_moving = FALSE;       // some elements can fall _and_ move
7767   int element = Tile[x][y];
7768
7769   if (Stop[x][y])
7770     return;
7771
7772   if (MovDelay[x][y] == 0)
7773     GfxAction[x][y] = ACTION_DEFAULT;
7774
7775   if (CAN_FALL(element) && y < lev_fieldy - 1)
7776   {
7777     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7778         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7779       if (JustBeingPushed(x, y))
7780         return;
7781
7782     if (element == EL_QUICKSAND_FULL)
7783     {
7784       if (IS_FREE(x, y + 1))
7785       {
7786         InitMovingField(x, y, MV_DOWN);
7787         started_moving = TRUE;
7788
7789         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7790 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7791         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7792           Store[x][y] = EL_ROCK;
7793 #else
7794         Store[x][y] = EL_ROCK;
7795 #endif
7796
7797         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7798       }
7799       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7800       {
7801         if (!MovDelay[x][y])
7802         {
7803           MovDelay[x][y] = TILEY + 1;
7804
7805           ResetGfxAnimation(x, y);
7806           ResetGfxAnimation(x, y + 1);
7807         }
7808
7809         if (MovDelay[x][y])
7810         {
7811           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7812           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7813
7814           MovDelay[x][y]--;
7815           if (MovDelay[x][y])
7816             return;
7817         }
7818
7819         Tile[x][y] = EL_QUICKSAND_EMPTY;
7820         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7821         Store[x][y + 1] = Store[x][y];
7822         Store[x][y] = 0;
7823
7824         PlayLevelSoundAction(x, y, ACTION_FILLING);
7825       }
7826       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7827       {
7828         if (!MovDelay[x][y])
7829         {
7830           MovDelay[x][y] = TILEY + 1;
7831
7832           ResetGfxAnimation(x, y);
7833           ResetGfxAnimation(x, y + 1);
7834         }
7835
7836         if (MovDelay[x][y])
7837         {
7838           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7839           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7840
7841           MovDelay[x][y]--;
7842           if (MovDelay[x][y])
7843             return;
7844         }
7845
7846         Tile[x][y] = EL_QUICKSAND_EMPTY;
7847         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7848         Store[x][y + 1] = Store[x][y];
7849         Store[x][y] = 0;
7850
7851         PlayLevelSoundAction(x, y, ACTION_FILLING);
7852       }
7853     }
7854     else if (element == EL_QUICKSAND_FAST_FULL)
7855     {
7856       if (IS_FREE(x, y + 1))
7857       {
7858         InitMovingField(x, y, MV_DOWN);
7859         started_moving = TRUE;
7860
7861         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7862 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7863         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7864           Store[x][y] = EL_ROCK;
7865 #else
7866         Store[x][y] = EL_ROCK;
7867 #endif
7868
7869         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7870       }
7871       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7872       {
7873         if (!MovDelay[x][y])
7874         {
7875           MovDelay[x][y] = TILEY + 1;
7876
7877           ResetGfxAnimation(x, y);
7878           ResetGfxAnimation(x, y + 1);
7879         }
7880
7881         if (MovDelay[x][y])
7882         {
7883           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7884           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7885
7886           MovDelay[x][y]--;
7887           if (MovDelay[x][y])
7888             return;
7889         }
7890
7891         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7892         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7893         Store[x][y + 1] = Store[x][y];
7894         Store[x][y] = 0;
7895
7896         PlayLevelSoundAction(x, y, ACTION_FILLING);
7897       }
7898       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7899       {
7900         if (!MovDelay[x][y])
7901         {
7902           MovDelay[x][y] = TILEY + 1;
7903
7904           ResetGfxAnimation(x, y);
7905           ResetGfxAnimation(x, y + 1);
7906         }
7907
7908         if (MovDelay[x][y])
7909         {
7910           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7911           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7912
7913           MovDelay[x][y]--;
7914           if (MovDelay[x][y])
7915             return;
7916         }
7917
7918         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7919         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7920         Store[x][y + 1] = Store[x][y];
7921         Store[x][y] = 0;
7922
7923         PlayLevelSoundAction(x, y, ACTION_FILLING);
7924       }
7925     }
7926     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7927              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7928     {
7929       InitMovingField(x, y, MV_DOWN);
7930       started_moving = TRUE;
7931
7932       Tile[x][y] = EL_QUICKSAND_FILLING;
7933       Store[x][y] = element;
7934
7935       PlayLevelSoundAction(x, y, ACTION_FILLING);
7936     }
7937     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7938              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7939     {
7940       InitMovingField(x, y, MV_DOWN);
7941       started_moving = TRUE;
7942
7943       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7944       Store[x][y] = element;
7945
7946       PlayLevelSoundAction(x, y, ACTION_FILLING);
7947     }
7948     else if (element == EL_MAGIC_WALL_FULL)
7949     {
7950       if (IS_FREE(x, y + 1))
7951       {
7952         InitMovingField(x, y, MV_DOWN);
7953         started_moving = TRUE;
7954
7955         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7956         Store[x][y] = EL_CHANGED(Store[x][y]);
7957       }
7958       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7959       {
7960         if (!MovDelay[x][y])
7961           MovDelay[x][y] = TILEY / 4 + 1;
7962
7963         if (MovDelay[x][y])
7964         {
7965           MovDelay[x][y]--;
7966           if (MovDelay[x][y])
7967             return;
7968         }
7969
7970         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7971         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7972         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7973         Store[x][y] = 0;
7974       }
7975     }
7976     else if (element == EL_BD_MAGIC_WALL_FULL)
7977     {
7978       if (IS_FREE(x, y + 1))
7979       {
7980         InitMovingField(x, y, MV_DOWN);
7981         started_moving = TRUE;
7982
7983         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7984         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7985       }
7986       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7987       {
7988         if (!MovDelay[x][y])
7989           MovDelay[x][y] = TILEY / 4 + 1;
7990
7991         if (MovDelay[x][y])
7992         {
7993           MovDelay[x][y]--;
7994           if (MovDelay[x][y])
7995             return;
7996         }
7997
7998         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7999         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8000         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8001         Store[x][y] = 0;
8002       }
8003     }
8004     else if (element == EL_DC_MAGIC_WALL_FULL)
8005     {
8006       if (IS_FREE(x, y + 1))
8007       {
8008         InitMovingField(x, y, MV_DOWN);
8009         started_moving = TRUE;
8010
8011         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8012         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8013       }
8014       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8015       {
8016         if (!MovDelay[x][y])
8017           MovDelay[x][y] = TILEY / 4 + 1;
8018
8019         if (MovDelay[x][y])
8020         {
8021           MovDelay[x][y]--;
8022           if (MovDelay[x][y])
8023             return;
8024         }
8025
8026         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8027         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8028         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8029         Store[x][y] = 0;
8030       }
8031     }
8032     else if ((CAN_PASS_MAGIC_WALL(element) &&
8033               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8034                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8035              (CAN_PASS_DC_MAGIC_WALL(element) &&
8036               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8037
8038     {
8039       InitMovingField(x, y, MV_DOWN);
8040       started_moving = TRUE;
8041
8042       Tile[x][y] =
8043         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8044          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8045          EL_DC_MAGIC_WALL_FILLING);
8046       Store[x][y] = element;
8047     }
8048     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8049     {
8050       SplashAcid(x, y + 1);
8051
8052       InitMovingField(x, y, MV_DOWN);
8053       started_moving = TRUE;
8054
8055       Store[x][y] = EL_ACID;
8056     }
8057     else if (
8058              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8059               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8060              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8061               CAN_FALL(element) && WasJustFalling[x][y] &&
8062               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8063
8064              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8065               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8066               (Tile[x][y + 1] == EL_BLOCKED)))
8067     {
8068       /* this is needed for a special case not covered by calling "Impact()"
8069          from "ContinueMoving()": if an element moves to a tile directly below
8070          another element which was just falling on that tile (which was empty
8071          in the previous frame), the falling element above would just stop
8072          instead of smashing the element below (in previous version, the above
8073          element was just checked for "moving" instead of "falling", resulting
8074          in incorrect smashes caused by horizontal movement of the above
8075          element; also, the case of the player being the element to smash was
8076          simply not covered here... :-/ ) */
8077
8078       CheckCollision[x][y] = 0;
8079       CheckImpact[x][y] = 0;
8080
8081       Impact(x, y);
8082     }
8083     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8084     {
8085       if (MovDir[x][y] == MV_NONE)
8086       {
8087         InitMovingField(x, y, MV_DOWN);
8088         started_moving = TRUE;
8089       }
8090     }
8091     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8092     {
8093       if (WasJustFalling[x][y]) // prevent animation from being restarted
8094         MovDir[x][y] = MV_DOWN;
8095
8096       InitMovingField(x, y, MV_DOWN);
8097       started_moving = TRUE;
8098     }
8099     else if (element == EL_AMOEBA_DROP)
8100     {
8101       Tile[x][y] = EL_AMOEBA_GROWING;
8102       Store[x][y] = EL_AMOEBA_WET;
8103     }
8104     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8105               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8106              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8107              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8108     {
8109       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8110                                 (IS_FREE(x - 1, y + 1) ||
8111                                  Tile[x - 1][y + 1] == EL_ACID));
8112       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8113                                 (IS_FREE(x + 1, y + 1) ||
8114                                  Tile[x + 1][y + 1] == EL_ACID));
8115       boolean can_fall_any  = (can_fall_left || can_fall_right);
8116       boolean can_fall_both = (can_fall_left && can_fall_right);
8117       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8118
8119       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8120       {
8121         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8122           can_fall_right = FALSE;
8123         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8124           can_fall_left = FALSE;
8125         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8126           can_fall_right = FALSE;
8127         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8128           can_fall_left = FALSE;
8129
8130         can_fall_any  = (can_fall_left || can_fall_right);
8131         can_fall_both = FALSE;
8132       }
8133
8134       if (can_fall_both)
8135       {
8136         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8137           can_fall_right = FALSE;       // slip down on left side
8138         else
8139           can_fall_left = !(can_fall_right = RND(2));
8140
8141         can_fall_both = FALSE;
8142       }
8143
8144       if (can_fall_any)
8145       {
8146         // if not determined otherwise, prefer left side for slipping down
8147         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8148         started_moving = TRUE;
8149       }
8150     }
8151     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8152     {
8153       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8154       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8155       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8156       int belt_dir = game.belt_dir[belt_nr];
8157
8158       if ((belt_dir == MV_LEFT  && left_is_free) ||
8159           (belt_dir == MV_RIGHT && right_is_free))
8160       {
8161         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8162
8163         InitMovingField(x, y, belt_dir);
8164         started_moving = TRUE;
8165
8166         Pushed[x][y] = TRUE;
8167         Pushed[nextx][y] = TRUE;
8168
8169         GfxAction[x][y] = ACTION_DEFAULT;
8170       }
8171       else
8172       {
8173         MovDir[x][y] = 0;       // if element was moving, stop it
8174       }
8175     }
8176   }
8177
8178   // not "else if" because of elements that can fall and move (EL_SPRING)
8179   if (CAN_MOVE(element) && !started_moving)
8180   {
8181     int move_pattern = element_info[element].move_pattern;
8182     int newx, newy;
8183
8184     Moving2Blocked(x, y, &newx, &newy);
8185
8186     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8187       return;
8188
8189     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8190         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8191     {
8192       WasJustMoving[x][y] = 0;
8193       CheckCollision[x][y] = 0;
8194
8195       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8196
8197       if (Tile[x][y] != element)        // element has changed
8198         return;
8199     }
8200
8201     if (!MovDelay[x][y])        // start new movement phase
8202     {
8203       // all objects that can change their move direction after each step
8204       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8205
8206       if (element != EL_YAMYAM &&
8207           element != EL_DARK_YAMYAM &&
8208           element != EL_PACMAN &&
8209           !(move_pattern & MV_ANY_DIRECTION) &&
8210           move_pattern != MV_TURNING_LEFT &&
8211           move_pattern != MV_TURNING_RIGHT &&
8212           move_pattern != MV_TURNING_LEFT_RIGHT &&
8213           move_pattern != MV_TURNING_RIGHT_LEFT &&
8214           move_pattern != MV_TURNING_RANDOM)
8215       {
8216         TurnRound(x, y);
8217
8218         if (MovDelay[x][y] && (element == EL_BUG ||
8219                                element == EL_SPACESHIP ||
8220                                element == EL_SP_SNIKSNAK ||
8221                                element == EL_SP_ELECTRON ||
8222                                element == EL_MOLE))
8223           TEST_DrawLevelField(x, y);
8224       }
8225     }
8226
8227     if (MovDelay[x][y])         // wait some time before next movement
8228     {
8229       MovDelay[x][y]--;
8230
8231       if (element == EL_ROBOT ||
8232           element == EL_YAMYAM ||
8233           element == EL_DARK_YAMYAM)
8234       {
8235         DrawLevelElementAnimationIfNeeded(x, y, element);
8236         PlayLevelSoundAction(x, y, ACTION_WAITING);
8237       }
8238       else if (element == EL_SP_ELECTRON)
8239         DrawLevelElementAnimationIfNeeded(x, y, element);
8240       else if (element == EL_DRAGON)
8241       {
8242         int i;
8243         int dir = MovDir[x][y];
8244         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8245         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8246         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8247                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8248                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8249                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8250         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8251
8252         GfxAction[x][y] = ACTION_ATTACKING;
8253
8254         if (IS_PLAYER(x, y))
8255           DrawPlayerField(x, y);
8256         else
8257           TEST_DrawLevelField(x, y);
8258
8259         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8260
8261         for (i = 1; i <= 3; i++)
8262         {
8263           int xx = x + i * dx;
8264           int yy = y + i * dy;
8265           int sx = SCREENX(xx);
8266           int sy = SCREENY(yy);
8267           int flame_graphic = graphic + (i - 1);
8268
8269           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8270             break;
8271
8272           if (MovDelay[x][y])
8273           {
8274             int flamed = MovingOrBlocked2Element(xx, yy);
8275
8276             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8277               Bang(xx, yy);
8278             else
8279               RemoveMovingField(xx, yy);
8280
8281             ChangeDelay[xx][yy] = 0;
8282
8283             Tile[xx][yy] = EL_FLAMES;
8284
8285             if (IN_SCR_FIELD(sx, sy))
8286             {
8287               TEST_DrawLevelFieldCrumbled(xx, yy);
8288               DrawGraphic(sx, sy, flame_graphic, frame);
8289             }
8290           }
8291           else
8292           {
8293             if (Tile[xx][yy] == EL_FLAMES)
8294               Tile[xx][yy] = EL_EMPTY;
8295             TEST_DrawLevelField(xx, yy);
8296           }
8297         }
8298       }
8299
8300       if (MovDelay[x][y])       // element still has to wait some time
8301       {
8302         PlayLevelSoundAction(x, y, ACTION_WAITING);
8303
8304         return;
8305       }
8306     }
8307
8308     // now make next step
8309
8310     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8311
8312     if (DONT_COLLIDE_WITH(element) &&
8313         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8314         !PLAYER_ENEMY_PROTECTED(newx, newy))
8315     {
8316       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8317
8318       return;
8319     }
8320
8321     else if (CAN_MOVE_INTO_ACID(element) &&
8322              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8323              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8324              (MovDir[x][y] == MV_DOWN ||
8325               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8326     {
8327       SplashAcid(newx, newy);
8328       Store[x][y] = EL_ACID;
8329     }
8330     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8331     {
8332       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8333           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8334           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8335           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8336       {
8337         RemoveField(x, y);
8338         TEST_DrawLevelField(x, y);
8339
8340         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8341         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8342           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8343
8344         game.friends_still_needed--;
8345         if (!game.friends_still_needed &&
8346             !game.GameOver &&
8347             game.all_players_gone)
8348           LevelSolved();
8349
8350         return;
8351       }
8352       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8353       {
8354         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8355           TEST_DrawLevelField(newx, newy);
8356         else
8357           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8358       }
8359       else if (!IS_FREE(newx, newy))
8360       {
8361         GfxAction[x][y] = ACTION_WAITING;
8362
8363         if (IS_PLAYER(x, y))
8364           DrawPlayerField(x, y);
8365         else
8366           TEST_DrawLevelField(x, y);
8367
8368         return;
8369       }
8370     }
8371     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8372     {
8373       if (IS_FOOD_PIG(Tile[newx][newy]))
8374       {
8375         if (IS_MOVING(newx, newy))
8376           RemoveMovingField(newx, newy);
8377         else
8378         {
8379           Tile[newx][newy] = EL_EMPTY;
8380           TEST_DrawLevelField(newx, newy);
8381         }
8382
8383         PlayLevelSound(x, y, SND_PIG_DIGGING);
8384       }
8385       else if (!IS_FREE(newx, newy))
8386       {
8387         if (IS_PLAYER(x, y))
8388           DrawPlayerField(x, y);
8389         else
8390           TEST_DrawLevelField(x, y);
8391
8392         return;
8393       }
8394     }
8395     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8396     {
8397       if (Store[x][y] != EL_EMPTY)
8398       {
8399         boolean can_clone = FALSE;
8400         int xx, yy;
8401
8402         // check if element to clone is still there
8403         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8404         {
8405           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8406           {
8407             can_clone = TRUE;
8408
8409             break;
8410           }
8411         }
8412
8413         // cannot clone or target field not free anymore -- do not clone
8414         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8415           Store[x][y] = EL_EMPTY;
8416       }
8417
8418       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8419       {
8420         if (IS_MV_DIAGONAL(MovDir[x][y]))
8421         {
8422           int diagonal_move_dir = MovDir[x][y];
8423           int stored = Store[x][y];
8424           int change_delay = 8;
8425           int graphic;
8426
8427           // android is moving diagonally
8428
8429           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8430
8431           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8432           GfxElement[x][y] = EL_EMC_ANDROID;
8433           GfxAction[x][y] = ACTION_SHRINKING;
8434           GfxDir[x][y] = diagonal_move_dir;
8435           ChangeDelay[x][y] = change_delay;
8436
8437           if (Store[x][y] == EL_EMPTY)
8438             Store[x][y] = GfxElementEmpty[x][y];
8439
8440           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8441                                    GfxDir[x][y]);
8442
8443           DrawLevelGraphicAnimation(x, y, graphic);
8444           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8445
8446           if (Tile[newx][newy] == EL_ACID)
8447           {
8448             SplashAcid(newx, newy);
8449
8450             return;
8451           }
8452
8453           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8454
8455           Store[newx][newy] = EL_EMC_ANDROID;
8456           GfxElement[newx][newy] = EL_EMC_ANDROID;
8457           GfxAction[newx][newy] = ACTION_GROWING;
8458           GfxDir[newx][newy] = diagonal_move_dir;
8459           ChangeDelay[newx][newy] = change_delay;
8460
8461           graphic = el_act_dir2img(GfxElement[newx][newy],
8462                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8463
8464           DrawLevelGraphicAnimation(newx, newy, graphic);
8465           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8466
8467           return;
8468         }
8469         else
8470         {
8471           Tile[newx][newy] = EL_EMPTY;
8472           TEST_DrawLevelField(newx, newy);
8473
8474           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8475         }
8476       }
8477       else if (!IS_FREE(newx, newy))
8478       {
8479         return;
8480       }
8481     }
8482     else if (IS_CUSTOM_ELEMENT(element) &&
8483              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8484     {
8485       if (!DigFieldByCE(newx, newy, element))
8486         return;
8487
8488       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8489       {
8490         RunnerVisit[x][y] = FrameCounter;
8491         PlayerVisit[x][y] /= 8;         // expire player visit path
8492       }
8493     }
8494     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8495     {
8496       if (!IS_FREE(newx, newy))
8497       {
8498         if (IS_PLAYER(x, y))
8499           DrawPlayerField(x, y);
8500         else
8501           TEST_DrawLevelField(x, y);
8502
8503         return;
8504       }
8505       else
8506       {
8507         boolean wanna_flame = !RND(10);
8508         int dx = newx - x, dy = newy - y;
8509         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8510         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8511         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8512                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8513         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8514                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8515
8516         if ((wanna_flame ||
8517              IS_CLASSIC_ENEMY(element1) ||
8518              IS_CLASSIC_ENEMY(element2)) &&
8519             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8520             element1 != EL_FLAMES && element2 != EL_FLAMES)
8521         {
8522           ResetGfxAnimation(x, y);
8523           GfxAction[x][y] = ACTION_ATTACKING;
8524
8525           if (IS_PLAYER(x, y))
8526             DrawPlayerField(x, y);
8527           else
8528             TEST_DrawLevelField(x, y);
8529
8530           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8531
8532           MovDelay[x][y] = 50;
8533
8534           Tile[newx][newy] = EL_FLAMES;
8535           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8536             Tile[newx1][newy1] = EL_FLAMES;
8537           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8538             Tile[newx2][newy2] = EL_FLAMES;
8539
8540           return;
8541         }
8542       }
8543     }
8544     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8545              Tile[newx][newy] == EL_DIAMOND)
8546     {
8547       if (IS_MOVING(newx, newy))
8548         RemoveMovingField(newx, newy);
8549       else
8550       {
8551         Tile[newx][newy] = EL_EMPTY;
8552         TEST_DrawLevelField(newx, newy);
8553       }
8554
8555       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8556     }
8557     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8558              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8559     {
8560       if (AmoebaNr[newx][newy])
8561       {
8562         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8563         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8564             Tile[newx][newy] == EL_BD_AMOEBA)
8565           AmoebaCnt[AmoebaNr[newx][newy]]--;
8566       }
8567
8568       if (IS_MOVING(newx, newy))
8569       {
8570         RemoveMovingField(newx, newy);
8571       }
8572       else
8573       {
8574         Tile[newx][newy] = EL_EMPTY;
8575         TEST_DrawLevelField(newx, newy);
8576       }
8577
8578       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8579     }
8580     else if ((element == EL_PACMAN || element == EL_MOLE)
8581              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8582     {
8583       if (AmoebaNr[newx][newy])
8584       {
8585         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8586         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8587             Tile[newx][newy] == EL_BD_AMOEBA)
8588           AmoebaCnt[AmoebaNr[newx][newy]]--;
8589       }
8590
8591       if (element == EL_MOLE)
8592       {
8593         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8594         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8595
8596         ResetGfxAnimation(x, y);
8597         GfxAction[x][y] = ACTION_DIGGING;
8598         TEST_DrawLevelField(x, y);
8599
8600         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8601
8602         return;                         // wait for shrinking amoeba
8603       }
8604       else      // element == EL_PACMAN
8605       {
8606         Tile[newx][newy] = EL_EMPTY;
8607         TEST_DrawLevelField(newx, newy);
8608         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8609       }
8610     }
8611     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8612              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8613               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8614     {
8615       // wait for shrinking amoeba to completely disappear
8616       return;
8617     }
8618     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8619     {
8620       // object was running against a wall
8621
8622       TurnRound(x, y);
8623
8624       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8625         DrawLevelElementAnimation(x, y, element);
8626
8627       if (DONT_TOUCH(element))
8628         TestIfBadThingTouchesPlayer(x, y);
8629
8630       return;
8631     }
8632
8633     InitMovingField(x, y, MovDir[x][y]);
8634
8635     PlayLevelSoundAction(x, y, ACTION_MOVING);
8636   }
8637
8638   if (MovDir[x][y])
8639     ContinueMoving(x, y);
8640 }
8641
8642 void ContinueMoving(int x, int y)
8643 {
8644   int element = Tile[x][y];
8645   struct ElementInfo *ei = &element_info[element];
8646   int direction = MovDir[x][y];
8647   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8648   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8649   int newx = x + dx, newy = y + dy;
8650   int stored = Store[x][y];
8651   int stored_new = Store[newx][newy];
8652   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8653   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8654   boolean last_line = (newy == lev_fieldy - 1);
8655   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8656
8657   if (pushed_by_player)         // special case: moving object pushed by player
8658   {
8659     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8660   }
8661   else if (use_step_delay)      // special case: moving object has step delay
8662   {
8663     if (!MovDelay[x][y])
8664       MovPos[x][y] += getElementMoveStepsize(x, y);
8665
8666     if (MovDelay[x][y])
8667       MovDelay[x][y]--;
8668     else
8669       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8670
8671     if (MovDelay[x][y])
8672     {
8673       TEST_DrawLevelField(x, y);
8674
8675       return;   // element is still waiting
8676     }
8677   }
8678   else                          // normal case: generically moving object
8679   {
8680     MovPos[x][y] += getElementMoveStepsize(x, y);
8681   }
8682
8683   if (ABS(MovPos[x][y]) < TILEX)
8684   {
8685     TEST_DrawLevelField(x, y);
8686
8687     return;     // element is still moving
8688   }
8689
8690   // element reached destination field
8691
8692   Tile[x][y] = EL_EMPTY;
8693   Tile[newx][newy] = element;
8694   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8695
8696   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8697   {
8698     element = Tile[newx][newy] = EL_ACID;
8699   }
8700   else if (element == EL_MOLE)
8701   {
8702     Tile[x][y] = EL_SAND;
8703
8704     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8705   }
8706   else if (element == EL_QUICKSAND_FILLING)
8707   {
8708     element = Tile[newx][newy] = get_next_element(element);
8709     Store[newx][newy] = Store[x][y];
8710   }
8711   else if (element == EL_QUICKSAND_EMPTYING)
8712   {
8713     Tile[x][y] = get_next_element(element);
8714     element = Tile[newx][newy] = Store[x][y];
8715   }
8716   else if (element == EL_QUICKSAND_FAST_FILLING)
8717   {
8718     element = Tile[newx][newy] = get_next_element(element);
8719     Store[newx][newy] = Store[x][y];
8720   }
8721   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8722   {
8723     Tile[x][y] = get_next_element(element);
8724     element = Tile[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_MAGIC_WALL_FILLING)
8727   {
8728     element = Tile[newx][newy] = get_next_element(element);
8729     if (!game.magic_wall_active)
8730       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8731     Store[newx][newy] = Store[x][y];
8732   }
8733   else if (element == EL_MAGIC_WALL_EMPTYING)
8734   {
8735     Tile[x][y] = get_next_element(element);
8736     if (!game.magic_wall_active)
8737       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8738     element = Tile[newx][newy] = Store[x][y];
8739
8740     InitField(newx, newy, FALSE);
8741   }
8742   else if (element == EL_BD_MAGIC_WALL_FILLING)
8743   {
8744     element = Tile[newx][newy] = get_next_element(element);
8745     if (!game.magic_wall_active)
8746       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8747     Store[newx][newy] = Store[x][y];
8748   }
8749   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8750   {
8751     Tile[x][y] = get_next_element(element);
8752     if (!game.magic_wall_active)
8753       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8754     element = Tile[newx][newy] = Store[x][y];
8755
8756     InitField(newx, newy, FALSE);
8757   }
8758   else if (element == EL_DC_MAGIC_WALL_FILLING)
8759   {
8760     element = Tile[newx][newy] = get_next_element(element);
8761     if (!game.magic_wall_active)
8762       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8763     Store[newx][newy] = Store[x][y];
8764   }
8765   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8766   {
8767     Tile[x][y] = get_next_element(element);
8768     if (!game.magic_wall_active)
8769       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8770     element = Tile[newx][newy] = Store[x][y];
8771
8772     InitField(newx, newy, FALSE);
8773   }
8774   else if (element == EL_AMOEBA_DROPPING)
8775   {
8776     Tile[x][y] = get_next_element(element);
8777     element = Tile[newx][newy] = Store[x][y];
8778   }
8779   else if (element == EL_SOKOBAN_OBJECT)
8780   {
8781     if (Back[x][y])
8782       Tile[x][y] = Back[x][y];
8783
8784     if (Back[newx][newy])
8785       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8786
8787     Back[x][y] = Back[newx][newy] = 0;
8788   }
8789
8790   Store[x][y] = EL_EMPTY;
8791   MovPos[x][y] = 0;
8792   MovDir[x][y] = 0;
8793   MovDelay[x][y] = 0;
8794
8795   MovDelay[newx][newy] = 0;
8796
8797   if (CAN_CHANGE_OR_HAS_ACTION(element))
8798   {
8799     // copy element change control values to new field
8800     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8801     ChangePage[newx][newy]  = ChangePage[x][y];
8802     ChangeCount[newx][newy] = ChangeCount[x][y];
8803     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8804   }
8805
8806   CustomValue[newx][newy] = CustomValue[x][y];
8807
8808   ChangeDelay[x][y] = 0;
8809   ChangePage[x][y] = -1;
8810   ChangeCount[x][y] = 0;
8811   ChangeEvent[x][y] = -1;
8812
8813   CustomValue[x][y] = 0;
8814
8815   // copy animation control values to new field
8816   GfxFrame[newx][newy]  = GfxFrame[x][y];
8817   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8818   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8819   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8820
8821   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8822
8823   // some elements can leave other elements behind after moving
8824   if (ei->move_leave_element != EL_EMPTY &&
8825       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8826       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8827   {
8828     int move_leave_element = ei->move_leave_element;
8829
8830     // this makes it possible to leave the removed element again
8831     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8832       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8833
8834     Tile[x][y] = move_leave_element;
8835
8836     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8837       MovDir[x][y] = direction;
8838
8839     InitField(x, y, FALSE);
8840
8841     if (GFX_CRUMBLED(Tile[x][y]))
8842       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8843
8844     if (IS_PLAYER_ELEMENT(move_leave_element))
8845       RelocatePlayer(x, y, move_leave_element);
8846   }
8847
8848   // do this after checking for left-behind element
8849   ResetGfxAnimation(x, y);      // reset animation values for old field
8850
8851   if (!CAN_MOVE(element) ||
8852       (CAN_FALL(element) && direction == MV_DOWN &&
8853        (element == EL_SPRING ||
8854         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8855         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8856     GfxDir[x][y] = MovDir[newx][newy] = 0;
8857
8858   TEST_DrawLevelField(x, y);
8859   TEST_DrawLevelField(newx, newy);
8860
8861   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8862
8863   // prevent pushed element from moving on in pushed direction
8864   if (pushed_by_player && CAN_MOVE(element) &&
8865       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8866       !(element_info[element].move_pattern & direction))
8867     TurnRound(newx, newy);
8868
8869   // prevent elements on conveyor belt from moving on in last direction
8870   if (pushed_by_conveyor && CAN_FALL(element) &&
8871       direction & MV_HORIZONTAL)
8872     MovDir[newx][newy] = 0;
8873
8874   if (!pushed_by_player)
8875   {
8876     int nextx = newx + dx, nexty = newy + dy;
8877     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8878
8879     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8880
8881     if (CAN_FALL(element) && direction == MV_DOWN)
8882       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8883
8884     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8885       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8886
8887     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8888       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8889   }
8890
8891   if (DONT_TOUCH(element))      // object may be nasty to player or others
8892   {
8893     TestIfBadThingTouchesPlayer(newx, newy);
8894     TestIfBadThingTouchesFriend(newx, newy);
8895
8896     if (!IS_CUSTOM_ELEMENT(element))
8897       TestIfBadThingTouchesOtherBadThing(newx, newy);
8898   }
8899   else if (element == EL_PENGUIN)
8900     TestIfFriendTouchesBadThing(newx, newy);
8901
8902   if (DONT_GET_HIT_BY(element))
8903   {
8904     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8905   }
8906
8907   // give the player one last chance (one more frame) to move away
8908   if (CAN_FALL(element) && direction == MV_DOWN &&
8909       (last_line || (!IS_FREE(x, newy + 1) &&
8910                      (!IS_PLAYER(x, newy + 1) ||
8911                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8912     Impact(x, newy);
8913
8914   if (pushed_by_player && !game.use_change_when_pushing_bug)
8915   {
8916     int push_side = MV_DIR_OPPOSITE(direction);
8917     struct PlayerInfo *player = PLAYERINFO(x, y);
8918
8919     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8920                                player->index_bit, push_side);
8921     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8922                                         player->index_bit, push_side);
8923   }
8924
8925   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8926     MovDelay[newx][newy] = 1;
8927
8928   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8929
8930   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8931   TestIfElementHitsCustomElement(newx, newy, direction);
8932   TestIfPlayerTouchesCustomElement(newx, newy);
8933   TestIfElementTouchesCustomElement(newx, newy);
8934
8935   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8936       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8937     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8938                              MV_DIR_OPPOSITE(direction));
8939 }
8940
8941 int AmoebaNeighbourNr(int ax, int ay)
8942 {
8943   int i;
8944   int element = Tile[ax][ay];
8945   int group_nr = 0;
8946   static int xy[4][2] =
8947   {
8948     { 0, -1 },
8949     { -1, 0 },
8950     { +1, 0 },
8951     { 0, +1 }
8952   };
8953
8954   for (i = 0; i < NUM_DIRECTIONS; i++)
8955   {
8956     int x = ax + xy[i][0];
8957     int y = ay + xy[i][1];
8958
8959     if (!IN_LEV_FIELD(x, y))
8960       continue;
8961
8962     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8963       group_nr = AmoebaNr[x][y];
8964   }
8965
8966   return group_nr;
8967 }
8968
8969 static void AmoebaMerge(int ax, int ay)
8970 {
8971   int i, x, y, xx, yy;
8972   int new_group_nr = AmoebaNr[ax][ay];
8973   static int xy[4][2] =
8974   {
8975     { 0, -1 },
8976     { -1, 0 },
8977     { +1, 0 },
8978     { 0, +1 }
8979   };
8980
8981   if (new_group_nr == 0)
8982     return;
8983
8984   for (i = 0; i < NUM_DIRECTIONS; i++)
8985   {
8986     x = ax + xy[i][0];
8987     y = ay + xy[i][1];
8988
8989     if (!IN_LEV_FIELD(x, y))
8990       continue;
8991
8992     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8993          Tile[x][y] == EL_BD_AMOEBA ||
8994          Tile[x][y] == EL_AMOEBA_DEAD) &&
8995         AmoebaNr[x][y] != new_group_nr)
8996     {
8997       int old_group_nr = AmoebaNr[x][y];
8998
8999       if (old_group_nr == 0)
9000         return;
9001
9002       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9003       AmoebaCnt[old_group_nr] = 0;
9004       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9005       AmoebaCnt2[old_group_nr] = 0;
9006
9007       SCAN_PLAYFIELD(xx, yy)
9008       {
9009         if (AmoebaNr[xx][yy] == old_group_nr)
9010           AmoebaNr[xx][yy] = new_group_nr;
9011       }
9012     }
9013   }
9014 }
9015
9016 void AmoebaToDiamond(int ax, int ay)
9017 {
9018   int i, x, y;
9019
9020   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9021   {
9022     int group_nr = AmoebaNr[ax][ay];
9023
9024 #ifdef DEBUG
9025     if (group_nr == 0)
9026     {
9027       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9028       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9029
9030       return;
9031     }
9032 #endif
9033
9034     SCAN_PLAYFIELD(x, y)
9035     {
9036       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9037       {
9038         AmoebaNr[x][y] = 0;
9039         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9040       }
9041     }
9042
9043     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9044                             SND_AMOEBA_TURNING_TO_GEM :
9045                             SND_AMOEBA_TURNING_TO_ROCK));
9046     Bang(ax, ay);
9047   }
9048   else
9049   {
9050     static int xy[4][2] =
9051     {
9052       { 0, -1 },
9053       { -1, 0 },
9054       { +1, 0 },
9055       { 0, +1 }
9056     };
9057
9058     for (i = 0; i < NUM_DIRECTIONS; i++)
9059     {
9060       x = ax + xy[i][0];
9061       y = ay + xy[i][1];
9062
9063       if (!IN_LEV_FIELD(x, y))
9064         continue;
9065
9066       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9067       {
9068         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9069                               SND_AMOEBA_TURNING_TO_GEM :
9070                               SND_AMOEBA_TURNING_TO_ROCK));
9071         Bang(x, y);
9072       }
9073     }
9074   }
9075 }
9076
9077 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9078 {
9079   int x, y;
9080   int group_nr = AmoebaNr[ax][ay];
9081   boolean done = FALSE;
9082
9083 #ifdef DEBUG
9084   if (group_nr == 0)
9085   {
9086     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9087     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9088
9089     return;
9090   }
9091 #endif
9092
9093   SCAN_PLAYFIELD(x, y)
9094   {
9095     if (AmoebaNr[x][y] == group_nr &&
9096         (Tile[x][y] == EL_AMOEBA_DEAD ||
9097          Tile[x][y] == EL_BD_AMOEBA ||
9098          Tile[x][y] == EL_AMOEBA_GROWING))
9099     {
9100       AmoebaNr[x][y] = 0;
9101       Tile[x][y] = new_element;
9102       InitField(x, y, FALSE);
9103       TEST_DrawLevelField(x, y);
9104       done = TRUE;
9105     }
9106   }
9107
9108   if (done)
9109     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9110                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9111                             SND_BD_AMOEBA_TURNING_TO_GEM));
9112 }
9113
9114 static void AmoebaGrowing(int x, int y)
9115 {
9116   static unsigned int sound_delay = 0;
9117   static unsigned int sound_delay_value = 0;
9118
9119   if (!MovDelay[x][y])          // start new growing cycle
9120   {
9121     MovDelay[x][y] = 7;
9122
9123     if (DelayReached(&sound_delay, sound_delay_value))
9124     {
9125       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9126       sound_delay_value = 30;
9127     }
9128   }
9129
9130   if (MovDelay[x][y])           // wait some time before growing bigger
9131   {
9132     MovDelay[x][y]--;
9133     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9134     {
9135       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9136                                            6 - MovDelay[x][y]);
9137
9138       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9139     }
9140
9141     if (!MovDelay[x][y])
9142     {
9143       Tile[x][y] = Store[x][y];
9144       Store[x][y] = 0;
9145       TEST_DrawLevelField(x, y);
9146     }
9147   }
9148 }
9149
9150 static void AmoebaShrinking(int x, int y)
9151 {
9152   static unsigned int sound_delay = 0;
9153   static unsigned int sound_delay_value = 0;
9154
9155   if (!MovDelay[x][y])          // start new shrinking cycle
9156   {
9157     MovDelay[x][y] = 7;
9158
9159     if (DelayReached(&sound_delay, sound_delay_value))
9160       sound_delay_value = 30;
9161   }
9162
9163   if (MovDelay[x][y])           // wait some time before shrinking
9164   {
9165     MovDelay[x][y]--;
9166     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9167     {
9168       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9169                                            6 - MovDelay[x][y]);
9170
9171       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9172     }
9173
9174     if (!MovDelay[x][y])
9175     {
9176       Tile[x][y] = EL_EMPTY;
9177       TEST_DrawLevelField(x, y);
9178
9179       // don't let mole enter this field in this cycle;
9180       // (give priority to objects falling to this field from above)
9181       Stop[x][y] = TRUE;
9182     }
9183   }
9184 }
9185
9186 static void AmoebaReproduce(int ax, int ay)
9187 {
9188   int i;
9189   int element = Tile[ax][ay];
9190   int graphic = el2img(element);
9191   int newax = ax, neway = ay;
9192   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9193   static int xy[4][2] =
9194   {
9195     { 0, -1 },
9196     { -1, 0 },
9197     { +1, 0 },
9198     { 0, +1 }
9199   };
9200
9201   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9202   {
9203     Tile[ax][ay] = EL_AMOEBA_DEAD;
9204     TEST_DrawLevelField(ax, ay);
9205     return;
9206   }
9207
9208   if (IS_ANIMATED(graphic))
9209     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9210
9211   if (!MovDelay[ax][ay])        // start making new amoeba field
9212     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9213
9214   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9215   {
9216     MovDelay[ax][ay]--;
9217     if (MovDelay[ax][ay])
9218       return;
9219   }
9220
9221   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9222   {
9223     int start = RND(4);
9224     int x = ax + xy[start][0];
9225     int y = ay + xy[start][1];
9226
9227     if (!IN_LEV_FIELD(x, y))
9228       return;
9229
9230     if (IS_FREE(x, y) ||
9231         CAN_GROW_INTO(Tile[x][y]) ||
9232         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9233         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9234     {
9235       newax = x;
9236       neway = y;
9237     }
9238
9239     if (newax == ax && neway == ay)
9240       return;
9241   }
9242   else                          // normal or "filled" (BD style) amoeba
9243   {
9244     int start = RND(4);
9245     boolean waiting_for_player = FALSE;
9246
9247     for (i = 0; i < NUM_DIRECTIONS; i++)
9248     {
9249       int j = (start + i) % 4;
9250       int x = ax + xy[j][0];
9251       int y = ay + xy[j][1];
9252
9253       if (!IN_LEV_FIELD(x, y))
9254         continue;
9255
9256       if (IS_FREE(x, y) ||
9257           CAN_GROW_INTO(Tile[x][y]) ||
9258           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9259           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9260       {
9261         newax = x;
9262         neway = y;
9263         break;
9264       }
9265       else if (IS_PLAYER(x, y))
9266         waiting_for_player = TRUE;
9267     }
9268
9269     if (newax == ax && neway == ay)             // amoeba cannot grow
9270     {
9271       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9272       {
9273         Tile[ax][ay] = EL_AMOEBA_DEAD;
9274         TEST_DrawLevelField(ax, ay);
9275         AmoebaCnt[AmoebaNr[ax][ay]]--;
9276
9277         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9278         {
9279           if (element == EL_AMOEBA_FULL)
9280             AmoebaToDiamond(ax, ay);
9281           else if (element == EL_BD_AMOEBA)
9282             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9283         }
9284       }
9285       return;
9286     }
9287     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9288     {
9289       // amoeba gets larger by growing in some direction
9290
9291       int new_group_nr = AmoebaNr[ax][ay];
9292
9293 #ifdef DEBUG
9294   if (new_group_nr == 0)
9295   {
9296     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9297           newax, neway);
9298     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9299
9300     return;
9301   }
9302 #endif
9303
9304       AmoebaNr[newax][neway] = new_group_nr;
9305       AmoebaCnt[new_group_nr]++;
9306       AmoebaCnt2[new_group_nr]++;
9307
9308       // if amoeba touches other amoeba(s) after growing, unify them
9309       AmoebaMerge(newax, neway);
9310
9311       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9312       {
9313         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9314         return;
9315       }
9316     }
9317   }
9318
9319   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9320       (neway == lev_fieldy - 1 && newax != ax))
9321   {
9322     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9323     Store[newax][neway] = element;
9324   }
9325   else if (neway == ay || element == EL_EMC_DRIPPER)
9326   {
9327     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9328
9329     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9330   }
9331   else
9332   {
9333     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9334     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9335     Store[ax][ay] = EL_AMOEBA_DROP;
9336     ContinueMoving(ax, ay);
9337     return;
9338   }
9339
9340   TEST_DrawLevelField(newax, neway);
9341 }
9342
9343 static void Life(int ax, int ay)
9344 {
9345   int x1, y1, x2, y2;
9346   int life_time = 40;
9347   int element = Tile[ax][ay];
9348   int graphic = el2img(element);
9349   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9350                          level.biomaze);
9351   boolean changed = FALSE;
9352
9353   if (IS_ANIMATED(graphic))
9354     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9355
9356   if (Stop[ax][ay])
9357     return;
9358
9359   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9360     MovDelay[ax][ay] = life_time;
9361
9362   if (MovDelay[ax][ay])         // wait some time before next cycle
9363   {
9364     MovDelay[ax][ay]--;
9365     if (MovDelay[ax][ay])
9366       return;
9367   }
9368
9369   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9370   {
9371     int xx = ax+x1, yy = ay+y1;
9372     int old_element = Tile[xx][yy];
9373     int num_neighbours = 0;
9374
9375     if (!IN_LEV_FIELD(xx, yy))
9376       continue;
9377
9378     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9379     {
9380       int x = xx+x2, y = yy+y2;
9381
9382       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9383         continue;
9384
9385       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9386       boolean is_neighbour = FALSE;
9387
9388       if (level.use_life_bugs)
9389         is_neighbour =
9390           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9391            (IS_FREE(x, y)                             &&  Stop[x][y]));
9392       else
9393         is_neighbour =
9394           (Last[x][y] == element || is_player_cell);
9395
9396       if (is_neighbour)
9397         num_neighbours++;
9398     }
9399
9400     boolean is_free = FALSE;
9401
9402     if (level.use_life_bugs)
9403       is_free = (IS_FREE(xx, yy));
9404     else
9405       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9406
9407     if (xx == ax && yy == ay)           // field in the middle
9408     {
9409       if (num_neighbours < life_parameter[0] ||
9410           num_neighbours > life_parameter[1])
9411       {
9412         Tile[xx][yy] = EL_EMPTY;
9413         if (Tile[xx][yy] != old_element)
9414           TEST_DrawLevelField(xx, yy);
9415         Stop[xx][yy] = TRUE;
9416         changed = TRUE;
9417       }
9418     }
9419     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9420     {                                   // free border field
9421       if (num_neighbours >= life_parameter[2] &&
9422           num_neighbours <= life_parameter[3])
9423       {
9424         Tile[xx][yy] = element;
9425         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9426         if (Tile[xx][yy] != old_element)
9427           TEST_DrawLevelField(xx, yy);
9428         Stop[xx][yy] = TRUE;
9429         changed = TRUE;
9430       }
9431     }
9432   }
9433
9434   if (changed)
9435     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9436                    SND_GAME_OF_LIFE_GROWING);
9437 }
9438
9439 static void InitRobotWheel(int x, int y)
9440 {
9441   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9442 }
9443
9444 static void RunRobotWheel(int x, int y)
9445 {
9446   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9447 }
9448
9449 static void StopRobotWheel(int x, int y)
9450 {
9451   if (game.robot_wheel_x == x &&
9452       game.robot_wheel_y == y)
9453   {
9454     game.robot_wheel_x = -1;
9455     game.robot_wheel_y = -1;
9456     game.robot_wheel_active = FALSE;
9457   }
9458 }
9459
9460 static void InitTimegateWheel(int x, int y)
9461 {
9462   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9463 }
9464
9465 static void RunTimegateWheel(int x, int y)
9466 {
9467   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9468 }
9469
9470 static void InitMagicBallDelay(int x, int y)
9471 {
9472   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9473 }
9474
9475 static void ActivateMagicBall(int bx, int by)
9476 {
9477   int x, y;
9478
9479   if (level.ball_random)
9480   {
9481     int pos_border = RND(8);    // select one of the eight border elements
9482     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9483     int xx = pos_content % 3;
9484     int yy = pos_content / 3;
9485
9486     x = bx - 1 + xx;
9487     y = by - 1 + yy;
9488
9489     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9490       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9491   }
9492   else
9493   {
9494     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9495     {
9496       int xx = x - bx + 1;
9497       int yy = y - by + 1;
9498
9499       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9500         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9501     }
9502   }
9503
9504   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9505 }
9506
9507 static void CheckExit(int x, int y)
9508 {
9509   if (game.gems_still_needed > 0 ||
9510       game.sokoban_fields_still_needed > 0 ||
9511       game.sokoban_objects_still_needed > 0 ||
9512       game.lights_still_needed > 0)
9513   {
9514     int element = Tile[x][y];
9515     int graphic = el2img(element);
9516
9517     if (IS_ANIMATED(graphic))
9518       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9519
9520     return;
9521   }
9522
9523   // do not re-open exit door closed after last player
9524   if (game.all_players_gone)
9525     return;
9526
9527   Tile[x][y] = EL_EXIT_OPENING;
9528
9529   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9530 }
9531
9532 static void CheckExitEM(int x, int y)
9533 {
9534   if (game.gems_still_needed > 0 ||
9535       game.sokoban_fields_still_needed > 0 ||
9536       game.sokoban_objects_still_needed > 0 ||
9537       game.lights_still_needed > 0)
9538   {
9539     int element = Tile[x][y];
9540     int graphic = el2img(element);
9541
9542     if (IS_ANIMATED(graphic))
9543       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9544
9545     return;
9546   }
9547
9548   // do not re-open exit door closed after last player
9549   if (game.all_players_gone)
9550     return;
9551
9552   Tile[x][y] = EL_EM_EXIT_OPENING;
9553
9554   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9555 }
9556
9557 static void CheckExitSteel(int x, int y)
9558 {
9559   if (game.gems_still_needed > 0 ||
9560       game.sokoban_fields_still_needed > 0 ||
9561       game.sokoban_objects_still_needed > 0 ||
9562       game.lights_still_needed > 0)
9563   {
9564     int element = Tile[x][y];
9565     int graphic = el2img(element);
9566
9567     if (IS_ANIMATED(graphic))
9568       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9569
9570     return;
9571   }
9572
9573   // do not re-open exit door closed after last player
9574   if (game.all_players_gone)
9575     return;
9576
9577   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9578
9579   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9580 }
9581
9582 static void CheckExitSteelEM(int x, int y)
9583 {
9584   if (game.gems_still_needed > 0 ||
9585       game.sokoban_fields_still_needed > 0 ||
9586       game.sokoban_objects_still_needed > 0 ||
9587       game.lights_still_needed > 0)
9588   {
9589     int element = Tile[x][y];
9590     int graphic = el2img(element);
9591
9592     if (IS_ANIMATED(graphic))
9593       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594
9595     return;
9596   }
9597
9598   // do not re-open exit door closed after last player
9599   if (game.all_players_gone)
9600     return;
9601
9602   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9603
9604   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9605 }
9606
9607 static void CheckExitSP(int x, int y)
9608 {
9609   if (game.gems_still_needed > 0)
9610   {
9611     int element = Tile[x][y];
9612     int graphic = el2img(element);
9613
9614     if (IS_ANIMATED(graphic))
9615       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9616
9617     return;
9618   }
9619
9620   // do not re-open exit door closed after last player
9621   if (game.all_players_gone)
9622     return;
9623
9624   Tile[x][y] = EL_SP_EXIT_OPENING;
9625
9626   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9627 }
9628
9629 static void CloseAllOpenTimegates(void)
9630 {
9631   int x, y;
9632
9633   SCAN_PLAYFIELD(x, y)
9634   {
9635     int element = Tile[x][y];
9636
9637     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9638     {
9639       Tile[x][y] = EL_TIMEGATE_CLOSING;
9640
9641       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9642     }
9643   }
9644 }
9645
9646 static void DrawTwinkleOnField(int x, int y)
9647 {
9648   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9649     return;
9650
9651   if (Tile[x][y] == EL_BD_DIAMOND)
9652     return;
9653
9654   if (MovDelay[x][y] == 0)      // next animation frame
9655     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9656
9657   if (MovDelay[x][y] != 0)      // wait some time before next frame
9658   {
9659     MovDelay[x][y]--;
9660
9661     DrawLevelElementAnimation(x, y, Tile[x][y]);
9662
9663     if (MovDelay[x][y] != 0)
9664     {
9665       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9666                                            10 - MovDelay[x][y]);
9667
9668       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9669     }
9670   }
9671 }
9672
9673 static void MauerWaechst(int x, int y)
9674 {
9675   int delay = 6;
9676
9677   if (!MovDelay[x][y])          // next animation frame
9678     MovDelay[x][y] = 3 * delay;
9679
9680   if (MovDelay[x][y])           // wait some time before next frame
9681   {
9682     MovDelay[x][y]--;
9683
9684     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9685     {
9686       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9687       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9688
9689       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9690     }
9691
9692     if (!MovDelay[x][y])
9693     {
9694       if (MovDir[x][y] == MV_LEFT)
9695       {
9696         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9697           TEST_DrawLevelField(x - 1, y);
9698       }
9699       else if (MovDir[x][y] == MV_RIGHT)
9700       {
9701         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9702           TEST_DrawLevelField(x + 1, y);
9703       }
9704       else if (MovDir[x][y] == MV_UP)
9705       {
9706         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9707           TEST_DrawLevelField(x, y - 1);
9708       }
9709       else
9710       {
9711         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9712           TEST_DrawLevelField(x, y + 1);
9713       }
9714
9715       Tile[x][y] = Store[x][y];
9716       Store[x][y] = 0;
9717       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9718       TEST_DrawLevelField(x, y);
9719     }
9720   }
9721 }
9722
9723 static void MauerAbleger(int ax, int ay)
9724 {
9725   int element = Tile[ax][ay];
9726   int graphic = el2img(element);
9727   boolean oben_frei = FALSE, unten_frei = FALSE;
9728   boolean links_frei = FALSE, rechts_frei = FALSE;
9729   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9730   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9731   boolean new_wall = FALSE;
9732
9733   if (IS_ANIMATED(graphic))
9734     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9735
9736   if (!MovDelay[ax][ay])        // start building new wall
9737     MovDelay[ax][ay] = 6;
9738
9739   if (MovDelay[ax][ay])         // wait some time before building new wall
9740   {
9741     MovDelay[ax][ay]--;
9742     if (MovDelay[ax][ay])
9743       return;
9744   }
9745
9746   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9747     oben_frei = TRUE;
9748   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9749     unten_frei = TRUE;
9750   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9751     links_frei = TRUE;
9752   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9753     rechts_frei = TRUE;
9754
9755   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9756       element == EL_EXPANDABLE_WALL_ANY)
9757   {
9758     if (oben_frei)
9759     {
9760       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9761       Store[ax][ay-1] = element;
9762       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9763       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9764         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9765                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9766       new_wall = TRUE;
9767     }
9768     if (unten_frei)
9769     {
9770       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9771       Store[ax][ay+1] = element;
9772       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9773       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9774         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9775                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9776       new_wall = TRUE;
9777     }
9778   }
9779
9780   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9781       element == EL_EXPANDABLE_WALL_ANY ||
9782       element == EL_EXPANDABLE_WALL ||
9783       element == EL_BD_EXPANDABLE_WALL)
9784   {
9785     if (links_frei)
9786     {
9787       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9788       Store[ax-1][ay] = element;
9789       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9790       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9791         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9792                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9793       new_wall = TRUE;
9794     }
9795
9796     if (rechts_frei)
9797     {
9798       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9799       Store[ax+1][ay] = element;
9800       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9801       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9802         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9803                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9804       new_wall = TRUE;
9805     }
9806   }
9807
9808   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9809     TEST_DrawLevelField(ax, ay);
9810
9811   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9812     oben_massiv = TRUE;
9813   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9814     unten_massiv = TRUE;
9815   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9816     links_massiv = TRUE;
9817   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9818     rechts_massiv = TRUE;
9819
9820   if (((oben_massiv && unten_massiv) ||
9821        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9822        element == EL_EXPANDABLE_WALL) &&
9823       ((links_massiv && rechts_massiv) ||
9824        element == EL_EXPANDABLE_WALL_VERTICAL))
9825     Tile[ax][ay] = EL_WALL;
9826
9827   if (new_wall)
9828     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9829 }
9830
9831 static void MauerAblegerStahl(int ax, int ay)
9832 {
9833   int element = Tile[ax][ay];
9834   int graphic = el2img(element);
9835   boolean oben_frei = FALSE, unten_frei = FALSE;
9836   boolean links_frei = FALSE, rechts_frei = FALSE;
9837   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9838   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9839   boolean new_wall = FALSE;
9840
9841   if (IS_ANIMATED(graphic))
9842     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9843
9844   if (!MovDelay[ax][ay])        // start building new wall
9845     MovDelay[ax][ay] = 6;
9846
9847   if (MovDelay[ax][ay])         // wait some time before building new wall
9848   {
9849     MovDelay[ax][ay]--;
9850     if (MovDelay[ax][ay])
9851       return;
9852   }
9853
9854   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9855     oben_frei = TRUE;
9856   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9857     unten_frei = TRUE;
9858   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9859     links_frei = TRUE;
9860   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9861     rechts_frei = TRUE;
9862
9863   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9864       element == EL_EXPANDABLE_STEELWALL_ANY)
9865   {
9866     if (oben_frei)
9867     {
9868       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9869       Store[ax][ay-1] = element;
9870       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9871       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9872         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9873                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9874       new_wall = TRUE;
9875     }
9876     if (unten_frei)
9877     {
9878       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9879       Store[ax][ay+1] = element;
9880       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9881       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9882         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9883                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9884       new_wall = TRUE;
9885     }
9886   }
9887
9888   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9889       element == EL_EXPANDABLE_STEELWALL_ANY)
9890   {
9891     if (links_frei)
9892     {
9893       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9894       Store[ax-1][ay] = element;
9895       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9896       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9897         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9898                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9899       new_wall = TRUE;
9900     }
9901
9902     if (rechts_frei)
9903     {
9904       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9905       Store[ax+1][ay] = element;
9906       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9907       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9908         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9909                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9910       new_wall = TRUE;
9911     }
9912   }
9913
9914   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9915     oben_massiv = TRUE;
9916   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9917     unten_massiv = TRUE;
9918   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9919     links_massiv = TRUE;
9920   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9921     rechts_massiv = TRUE;
9922
9923   if (((oben_massiv && unten_massiv) ||
9924        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9925       ((links_massiv && rechts_massiv) ||
9926        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9927     Tile[ax][ay] = EL_STEELWALL;
9928
9929   if (new_wall)
9930     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9931 }
9932
9933 static void CheckForDragon(int x, int y)
9934 {
9935   int i, j;
9936   boolean dragon_found = FALSE;
9937   static int xy[4][2] =
9938   {
9939     { 0, -1 },
9940     { -1, 0 },
9941     { +1, 0 },
9942     { 0, +1 }
9943   };
9944
9945   for (i = 0; i < NUM_DIRECTIONS; i++)
9946   {
9947     for (j = 0; j < 4; j++)
9948     {
9949       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9950
9951       if (IN_LEV_FIELD(xx, yy) &&
9952           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9953       {
9954         if (Tile[xx][yy] == EL_DRAGON)
9955           dragon_found = TRUE;
9956       }
9957       else
9958         break;
9959     }
9960   }
9961
9962   if (!dragon_found)
9963   {
9964     for (i = 0; i < NUM_DIRECTIONS; i++)
9965     {
9966       for (j = 0; j < 3; j++)
9967       {
9968         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9969   
9970         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9971         {
9972           Tile[xx][yy] = EL_EMPTY;
9973           TEST_DrawLevelField(xx, yy);
9974         }
9975         else
9976           break;
9977       }
9978     }
9979   }
9980 }
9981
9982 static void InitBuggyBase(int x, int y)
9983 {
9984   int element = Tile[x][y];
9985   int activating_delay = FRAMES_PER_SECOND / 4;
9986
9987   ChangeDelay[x][y] =
9988     (element == EL_SP_BUGGY_BASE ?
9989      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9990      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9991      activating_delay :
9992      element == EL_SP_BUGGY_BASE_ACTIVE ?
9993      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9994 }
9995
9996 static void WarnBuggyBase(int x, int y)
9997 {
9998   int i;
9999   static int xy[4][2] =
10000   {
10001     { 0, -1 },
10002     { -1, 0 },
10003     { +1, 0 },
10004     { 0, +1 }
10005   };
10006
10007   for (i = 0; i < NUM_DIRECTIONS; i++)
10008   {
10009     int xx = x + xy[i][0];
10010     int yy = y + xy[i][1];
10011
10012     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10013     {
10014       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10015
10016       break;
10017     }
10018   }
10019 }
10020
10021 static void InitTrap(int x, int y)
10022 {
10023   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10024 }
10025
10026 static void ActivateTrap(int x, int y)
10027 {
10028   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10029 }
10030
10031 static void ChangeActiveTrap(int x, int y)
10032 {
10033   int graphic = IMG_TRAP_ACTIVE;
10034
10035   // if new animation frame was drawn, correct crumbled sand border
10036   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10037     TEST_DrawLevelFieldCrumbled(x, y);
10038 }
10039
10040 static int getSpecialActionElement(int element, int number, int base_element)
10041 {
10042   return (element != EL_EMPTY ? element :
10043           number != -1 ? base_element + number - 1 :
10044           EL_EMPTY);
10045 }
10046
10047 static int getModifiedActionNumber(int value_old, int operator, int operand,
10048                                    int value_min, int value_max)
10049 {
10050   int value_new = (operator == CA_MODE_SET      ? operand :
10051                    operator == CA_MODE_ADD      ? value_old + operand :
10052                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10053                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10054                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10055                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10056                    value_old);
10057
10058   return (value_new < value_min ? value_min :
10059           value_new > value_max ? value_max :
10060           value_new);
10061 }
10062
10063 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10064 {
10065   struct ElementInfo *ei = &element_info[element];
10066   struct ElementChangeInfo *change = &ei->change_page[page];
10067   int target_element = change->target_element;
10068   int action_type = change->action_type;
10069   int action_mode = change->action_mode;
10070   int action_arg = change->action_arg;
10071   int action_element = change->action_element;
10072   int i;
10073
10074   if (!change->has_action)
10075     return;
10076
10077   // ---------- determine action paramater values -----------------------------
10078
10079   int level_time_value =
10080     (level.time > 0 ? TimeLeft :
10081      TimePlayed);
10082
10083   int action_arg_element_raw =
10084     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10085      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10086      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10087      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10088      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10089      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10090      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10091      EL_EMPTY);
10092   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10093
10094   int action_arg_direction =
10095     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10096      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10097      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10098      change->actual_trigger_side :
10099      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10100      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10101      MV_NONE);
10102
10103   int action_arg_number_min =
10104     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10105      CA_ARG_MIN);
10106
10107   int action_arg_number_max =
10108     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10109      action_type == CA_SET_LEVEL_GEMS ? 999 :
10110      action_type == CA_SET_LEVEL_TIME ? 9999 :
10111      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10112      action_type == CA_SET_CE_VALUE ? 9999 :
10113      action_type == CA_SET_CE_SCORE ? 9999 :
10114      CA_ARG_MAX);
10115
10116   int action_arg_number_reset =
10117     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10118      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10119      action_type == CA_SET_LEVEL_TIME ? level.time :
10120      action_type == CA_SET_LEVEL_SCORE ? 0 :
10121      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10122      action_type == CA_SET_CE_SCORE ? 0 :
10123      0);
10124
10125   int action_arg_number =
10126     (action_arg <= CA_ARG_MAX ? action_arg :
10127      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10128      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10129      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10130      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10131      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10132      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10133      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10134      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10135      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10136      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10137      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10138      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10139      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10140      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10141      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10142      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10143      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10144      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10145      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10146      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10147      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10148      -1);
10149
10150   int action_arg_number_old =
10151     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10152      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10153      action_type == CA_SET_LEVEL_SCORE ? game.score :
10154      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10155      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10156      0);
10157
10158   int action_arg_number_new =
10159     getModifiedActionNumber(action_arg_number_old,
10160                             action_mode, action_arg_number,
10161                             action_arg_number_min, action_arg_number_max);
10162
10163   int trigger_player_bits =
10164     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10165      change->actual_trigger_player_bits : change->trigger_player);
10166
10167   int action_arg_player_bits =
10168     (action_arg >= CA_ARG_PLAYER_1 &&
10169      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10170      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10171      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10172      PLAYER_BITS_ANY);
10173
10174   // ---------- execute action  -----------------------------------------------
10175
10176   switch (action_type)
10177   {
10178     case CA_NO_ACTION:
10179     {
10180       return;
10181     }
10182
10183     // ---------- level actions  ----------------------------------------------
10184
10185     case CA_RESTART_LEVEL:
10186     {
10187       game.restart_level = TRUE;
10188
10189       break;
10190     }
10191
10192     case CA_SHOW_ENVELOPE:
10193     {
10194       int element = getSpecialActionElement(action_arg_element,
10195                                             action_arg_number, EL_ENVELOPE_1);
10196
10197       if (IS_ENVELOPE(element))
10198         local_player->show_envelope = element;
10199
10200       break;
10201     }
10202
10203     case CA_SET_LEVEL_TIME:
10204     {
10205       if (level.time > 0)       // only modify limited time value
10206       {
10207         TimeLeft = action_arg_number_new;
10208
10209         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10210
10211         DisplayGameControlValues();
10212
10213         if (!TimeLeft && setup.time_limit)
10214           for (i = 0; i < MAX_PLAYERS; i++)
10215             KillPlayer(&stored_player[i]);
10216       }
10217
10218       break;
10219     }
10220
10221     case CA_SET_LEVEL_SCORE:
10222     {
10223       game.score = action_arg_number_new;
10224
10225       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10226
10227       DisplayGameControlValues();
10228
10229       break;
10230     }
10231
10232     case CA_SET_LEVEL_GEMS:
10233     {
10234       game.gems_still_needed = action_arg_number_new;
10235
10236       game.snapshot.collected_item = TRUE;
10237
10238       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10239
10240       DisplayGameControlValues();
10241
10242       break;
10243     }
10244
10245     case CA_SET_LEVEL_WIND:
10246     {
10247       game.wind_direction = action_arg_direction;
10248
10249       break;
10250     }
10251
10252     case CA_SET_LEVEL_RANDOM_SEED:
10253     {
10254       // ensure that setting a new random seed while playing is predictable
10255       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10256
10257       break;
10258     }
10259
10260     // ---------- player actions  ---------------------------------------------
10261
10262     case CA_MOVE_PLAYER:
10263     case CA_MOVE_PLAYER_NEW:
10264     {
10265       // automatically move to the next field in specified direction
10266       for (i = 0; i < MAX_PLAYERS; i++)
10267         if (trigger_player_bits & (1 << i))
10268           if (action_type == CA_MOVE_PLAYER ||
10269               stored_player[i].MovPos == 0)
10270             stored_player[i].programmed_action = action_arg_direction;
10271
10272       break;
10273     }
10274
10275     case CA_EXIT_PLAYER:
10276     {
10277       for (i = 0; i < MAX_PLAYERS; i++)
10278         if (action_arg_player_bits & (1 << i))
10279           ExitPlayer(&stored_player[i]);
10280
10281       if (game.players_still_needed == 0)
10282         LevelSolved();
10283
10284       break;
10285     }
10286
10287     case CA_KILL_PLAYER:
10288     {
10289       for (i = 0; i < MAX_PLAYERS; i++)
10290         if (action_arg_player_bits & (1 << i))
10291           KillPlayer(&stored_player[i]);
10292
10293       break;
10294     }
10295
10296     case CA_SET_PLAYER_KEYS:
10297     {
10298       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10299       int element = getSpecialActionElement(action_arg_element,
10300                                             action_arg_number, EL_KEY_1);
10301
10302       if (IS_KEY(element))
10303       {
10304         for (i = 0; i < MAX_PLAYERS; i++)
10305         {
10306           if (trigger_player_bits & (1 << i))
10307           {
10308             stored_player[i].key[KEY_NR(element)] = key_state;
10309
10310             DrawGameDoorValues();
10311           }
10312         }
10313       }
10314
10315       break;
10316     }
10317
10318     case CA_SET_PLAYER_SPEED:
10319     {
10320       for (i = 0; i < MAX_PLAYERS; i++)
10321       {
10322         if (trigger_player_bits & (1 << i))
10323         {
10324           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10325
10326           if (action_arg == CA_ARG_SPEED_FASTER &&
10327               stored_player[i].cannot_move)
10328           {
10329             action_arg_number = STEPSIZE_VERY_SLOW;
10330           }
10331           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10332                    action_arg == CA_ARG_SPEED_FASTER)
10333           {
10334             action_arg_number = 2;
10335             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10336                            CA_MODE_MULTIPLY);
10337           }
10338           else if (action_arg == CA_ARG_NUMBER_RESET)
10339           {
10340             action_arg_number = level.initial_player_stepsize[i];
10341           }
10342
10343           move_stepsize =
10344             getModifiedActionNumber(move_stepsize,
10345                                     action_mode,
10346                                     action_arg_number,
10347                                     action_arg_number_min,
10348                                     action_arg_number_max);
10349
10350           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10351         }
10352       }
10353
10354       break;
10355     }
10356
10357     case CA_SET_PLAYER_SHIELD:
10358     {
10359       for (i = 0; i < MAX_PLAYERS; i++)
10360       {
10361         if (trigger_player_bits & (1 << i))
10362         {
10363           if (action_arg == CA_ARG_SHIELD_OFF)
10364           {
10365             stored_player[i].shield_normal_time_left = 0;
10366             stored_player[i].shield_deadly_time_left = 0;
10367           }
10368           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10369           {
10370             stored_player[i].shield_normal_time_left = 999999;
10371           }
10372           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10373           {
10374             stored_player[i].shield_normal_time_left = 999999;
10375             stored_player[i].shield_deadly_time_left = 999999;
10376           }
10377         }
10378       }
10379
10380       break;
10381     }
10382
10383     case CA_SET_PLAYER_GRAVITY:
10384     {
10385       for (i = 0; i < MAX_PLAYERS; i++)
10386       {
10387         if (trigger_player_bits & (1 << i))
10388         {
10389           stored_player[i].gravity =
10390             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10391              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10392              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10393              stored_player[i].gravity);
10394         }
10395       }
10396
10397       break;
10398     }
10399
10400     case CA_SET_PLAYER_ARTWORK:
10401     {
10402       for (i = 0; i < MAX_PLAYERS; i++)
10403       {
10404         if (trigger_player_bits & (1 << i))
10405         {
10406           int artwork_element = action_arg_element;
10407
10408           if (action_arg == CA_ARG_ELEMENT_RESET)
10409             artwork_element =
10410               (level.use_artwork_element[i] ? level.artwork_element[i] :
10411                stored_player[i].element_nr);
10412
10413           if (stored_player[i].artwork_element != artwork_element)
10414             stored_player[i].Frame = 0;
10415
10416           stored_player[i].artwork_element = artwork_element;
10417
10418           SetPlayerWaiting(&stored_player[i], FALSE);
10419
10420           // set number of special actions for bored and sleeping animation
10421           stored_player[i].num_special_action_bored =
10422             get_num_special_action(artwork_element,
10423                                    ACTION_BORING_1, ACTION_BORING_LAST);
10424           stored_player[i].num_special_action_sleeping =
10425             get_num_special_action(artwork_element,
10426                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10427         }
10428       }
10429
10430       break;
10431     }
10432
10433     case CA_SET_PLAYER_INVENTORY:
10434     {
10435       for (i = 0; i < MAX_PLAYERS; i++)
10436       {
10437         struct PlayerInfo *player = &stored_player[i];
10438         int j, k;
10439
10440         if (trigger_player_bits & (1 << i))
10441         {
10442           int inventory_element = action_arg_element;
10443
10444           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10445               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10446               action_arg == CA_ARG_ELEMENT_ACTION)
10447           {
10448             int element = inventory_element;
10449             int collect_count = element_info[element].collect_count_initial;
10450
10451             if (!IS_CUSTOM_ELEMENT(element))
10452               collect_count = 1;
10453
10454             if (collect_count == 0)
10455               player->inventory_infinite_element = element;
10456             else
10457               for (k = 0; k < collect_count; k++)
10458                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10459                   player->inventory_element[player->inventory_size++] =
10460                     element;
10461           }
10462           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10463                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10464                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10465           {
10466             if (player->inventory_infinite_element != EL_UNDEFINED &&
10467                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10468                                      action_arg_element_raw))
10469               player->inventory_infinite_element = EL_UNDEFINED;
10470
10471             for (k = 0, j = 0; j < player->inventory_size; j++)
10472             {
10473               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10474                                         action_arg_element_raw))
10475                 player->inventory_element[k++] = player->inventory_element[j];
10476             }
10477
10478             player->inventory_size = k;
10479           }
10480           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10481           {
10482             if (player->inventory_size > 0)
10483             {
10484               for (j = 0; j < player->inventory_size - 1; j++)
10485                 player->inventory_element[j] = player->inventory_element[j + 1];
10486
10487               player->inventory_size--;
10488             }
10489           }
10490           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10491           {
10492             if (player->inventory_size > 0)
10493               player->inventory_size--;
10494           }
10495           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10496           {
10497             player->inventory_infinite_element = EL_UNDEFINED;
10498             player->inventory_size = 0;
10499           }
10500           else if (action_arg == CA_ARG_INVENTORY_RESET)
10501           {
10502             player->inventory_infinite_element = EL_UNDEFINED;
10503             player->inventory_size = 0;
10504
10505             if (level.use_initial_inventory[i])
10506             {
10507               for (j = 0; j < level.initial_inventory_size[i]; j++)
10508               {
10509                 int element = level.initial_inventory_content[i][j];
10510                 int collect_count = element_info[element].collect_count_initial;
10511
10512                 if (!IS_CUSTOM_ELEMENT(element))
10513                   collect_count = 1;
10514
10515                 if (collect_count == 0)
10516                   player->inventory_infinite_element = element;
10517                 else
10518                   for (k = 0; k < collect_count; k++)
10519                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10520                       player->inventory_element[player->inventory_size++] =
10521                         element;
10522               }
10523             }
10524           }
10525         }
10526       }
10527
10528       break;
10529     }
10530
10531     // ---------- CE actions  -------------------------------------------------
10532
10533     case CA_SET_CE_VALUE:
10534     {
10535       int last_ce_value = CustomValue[x][y];
10536
10537       CustomValue[x][y] = action_arg_number_new;
10538
10539       if (CustomValue[x][y] != last_ce_value)
10540       {
10541         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10542         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10543
10544         if (CustomValue[x][y] == 0)
10545         {
10546           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10547           ChangeCount[x][y] = 0;        // allow at least one more change
10548
10549           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10550           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10551         }
10552       }
10553
10554       break;
10555     }
10556
10557     case CA_SET_CE_SCORE:
10558     {
10559       int last_ce_score = ei->collect_score;
10560
10561       ei->collect_score = action_arg_number_new;
10562
10563       if (ei->collect_score != last_ce_score)
10564       {
10565         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10566         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10567
10568         if (ei->collect_score == 0)
10569         {
10570           int xx, yy;
10571
10572           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10573           ChangeCount[x][y] = 0;        // allow at least one more change
10574
10575           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10576           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10577
10578           /*
10579             This is a very special case that seems to be a mixture between
10580             CheckElementChange() and CheckTriggeredElementChange(): while
10581             the first one only affects single elements that are triggered
10582             directly, the second one affects multiple elements in the playfield
10583             that are triggered indirectly by another element. This is a third
10584             case: Changing the CE score always affects multiple identical CEs,
10585             so every affected CE must be checked, not only the single CE for
10586             which the CE score was changed in the first place (as every instance
10587             of that CE shares the same CE score, and therefore also can change)!
10588           */
10589           SCAN_PLAYFIELD(xx, yy)
10590           {
10591             if (Tile[xx][yy] == element)
10592               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10593                                  CE_SCORE_GETS_ZERO);
10594           }
10595         }
10596       }
10597
10598       break;
10599     }
10600
10601     case CA_SET_CE_ARTWORK:
10602     {
10603       int artwork_element = action_arg_element;
10604       boolean reset_frame = FALSE;
10605       int xx, yy;
10606
10607       if (action_arg == CA_ARG_ELEMENT_RESET)
10608         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10609                            element);
10610
10611       if (ei->gfx_element != artwork_element)
10612         reset_frame = TRUE;
10613
10614       ei->gfx_element = artwork_element;
10615
10616       SCAN_PLAYFIELD(xx, yy)
10617       {
10618         if (Tile[xx][yy] == element)
10619         {
10620           if (reset_frame)
10621           {
10622             ResetGfxAnimation(xx, yy);
10623             ResetRandomAnimationValue(xx, yy);
10624           }
10625
10626           TEST_DrawLevelField(xx, yy);
10627         }
10628       }
10629
10630       break;
10631     }
10632
10633     // ---------- engine actions  ---------------------------------------------
10634
10635     case CA_SET_ENGINE_SCAN_MODE:
10636     {
10637       InitPlayfieldScanMode(action_arg);
10638
10639       break;
10640     }
10641
10642     default:
10643       break;
10644   }
10645 }
10646
10647 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10648 {
10649   int old_element = Tile[x][y];
10650   int new_element = GetElementFromGroupElement(element);
10651   int previous_move_direction = MovDir[x][y];
10652   int last_ce_value = CustomValue[x][y];
10653   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10654   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10655   boolean add_player_onto_element = (new_element_is_player &&
10656                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10657                                      IS_WALKABLE(old_element));
10658
10659   if (!add_player_onto_element)
10660   {
10661     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10662       RemoveMovingField(x, y);
10663     else
10664       RemoveField(x, y);
10665
10666     Tile[x][y] = new_element;
10667
10668     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10669       MovDir[x][y] = previous_move_direction;
10670
10671     if (element_info[new_element].use_last_ce_value)
10672       CustomValue[x][y] = last_ce_value;
10673
10674     InitField_WithBug1(x, y, FALSE);
10675
10676     new_element = Tile[x][y];   // element may have changed
10677
10678     ResetGfxAnimation(x, y);
10679     ResetRandomAnimationValue(x, y);
10680
10681     TEST_DrawLevelField(x, y);
10682
10683     if (GFX_CRUMBLED(new_element))
10684       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10685   }
10686
10687   // check if element under the player changes from accessible to unaccessible
10688   // (needed for special case of dropping element which then changes)
10689   // (must be checked after creating new element for walkable group elements)
10690   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10691       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10692   {
10693     Bang(x, y);
10694
10695     return;
10696   }
10697
10698   // "ChangeCount" not set yet to allow "entered by player" change one time
10699   if (new_element_is_player)
10700     RelocatePlayer(x, y, new_element);
10701
10702   if (is_change)
10703     ChangeCount[x][y]++;        // count number of changes in the same frame
10704
10705   TestIfBadThingTouchesPlayer(x, y);
10706   TestIfPlayerTouchesCustomElement(x, y);
10707   TestIfElementTouchesCustomElement(x, y);
10708 }
10709
10710 static void CreateField(int x, int y, int element)
10711 {
10712   CreateFieldExt(x, y, element, FALSE);
10713 }
10714
10715 static void CreateElementFromChange(int x, int y, int element)
10716 {
10717   element = GET_VALID_RUNTIME_ELEMENT(element);
10718
10719   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10720   {
10721     int old_element = Tile[x][y];
10722
10723     // prevent changed element from moving in same engine frame
10724     // unless both old and new element can either fall or move
10725     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10726         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10727       Stop[x][y] = TRUE;
10728   }
10729
10730   CreateFieldExt(x, y, element, TRUE);
10731 }
10732
10733 static boolean ChangeElement(int x, int y, int element, int page)
10734 {
10735   struct ElementInfo *ei = &element_info[element];
10736   struct ElementChangeInfo *change = &ei->change_page[page];
10737   int ce_value = CustomValue[x][y];
10738   int ce_score = ei->collect_score;
10739   int target_element;
10740   int old_element = Tile[x][y];
10741
10742   // always use default change event to prevent running into a loop
10743   if (ChangeEvent[x][y] == -1)
10744     ChangeEvent[x][y] = CE_DELAY;
10745
10746   if (ChangeEvent[x][y] == CE_DELAY)
10747   {
10748     // reset actual trigger element, trigger player and action element
10749     change->actual_trigger_element = EL_EMPTY;
10750     change->actual_trigger_player = EL_EMPTY;
10751     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10752     change->actual_trigger_side = CH_SIDE_NONE;
10753     change->actual_trigger_ce_value = 0;
10754     change->actual_trigger_ce_score = 0;
10755   }
10756
10757   // do not change elements more than a specified maximum number of changes
10758   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10759     return FALSE;
10760
10761   ChangeCount[x][y]++;          // count number of changes in the same frame
10762
10763   if (change->explode)
10764   {
10765     Bang(x, y);
10766
10767     return TRUE;
10768   }
10769
10770   if (change->use_target_content)
10771   {
10772     boolean complete_replace = TRUE;
10773     boolean can_replace[3][3];
10774     int xx, yy;
10775
10776     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10777     {
10778       boolean is_empty;
10779       boolean is_walkable;
10780       boolean is_diggable;
10781       boolean is_collectible;
10782       boolean is_removable;
10783       boolean is_destructible;
10784       int ex = x + xx - 1;
10785       int ey = y + yy - 1;
10786       int content_element = change->target_content.e[xx][yy];
10787       int e;
10788
10789       can_replace[xx][yy] = TRUE;
10790
10791       if (ex == x && ey == y)   // do not check changing element itself
10792         continue;
10793
10794       if (content_element == EL_EMPTY_SPACE)
10795       {
10796         can_replace[xx][yy] = FALSE;    // do not replace border with space
10797
10798         continue;
10799       }
10800
10801       if (!IN_LEV_FIELD(ex, ey))
10802       {
10803         can_replace[xx][yy] = FALSE;
10804         complete_replace = FALSE;
10805
10806         continue;
10807       }
10808
10809       e = Tile[ex][ey];
10810
10811       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10812         e = MovingOrBlocked2Element(ex, ey);
10813
10814       is_empty = (IS_FREE(ex, ey) ||
10815                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10816
10817       is_walkable     = (is_empty || IS_WALKABLE(e));
10818       is_diggable     = (is_empty || IS_DIGGABLE(e));
10819       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10820       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10821       is_removable    = (is_diggable || is_collectible);
10822
10823       can_replace[xx][yy] =
10824         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10825           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10826           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10827           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10828           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10829           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10830          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10831
10832       if (!can_replace[xx][yy])
10833         complete_replace = FALSE;
10834     }
10835
10836     if (!change->only_if_complete || complete_replace)
10837     {
10838       boolean something_has_changed = FALSE;
10839
10840       if (change->only_if_complete && change->use_random_replace &&
10841           RND(100) < change->random_percentage)
10842         return FALSE;
10843
10844       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10845       {
10846         int ex = x + xx - 1;
10847         int ey = y + yy - 1;
10848         int content_element;
10849
10850         if (can_replace[xx][yy] && (!change->use_random_replace ||
10851                                     RND(100) < change->random_percentage))
10852         {
10853           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10854             RemoveMovingField(ex, ey);
10855
10856           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10857
10858           content_element = change->target_content.e[xx][yy];
10859           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10860                                               ce_value, ce_score);
10861
10862           CreateElementFromChange(ex, ey, target_element);
10863
10864           something_has_changed = TRUE;
10865
10866           // for symmetry reasons, freeze newly created border elements
10867           if (ex != x || ey != y)
10868             Stop[ex][ey] = TRUE;        // no more moving in this frame
10869         }
10870       }
10871
10872       if (something_has_changed)
10873       {
10874         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10875         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10876       }
10877     }
10878   }
10879   else
10880   {
10881     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10882                                         ce_value, ce_score);
10883
10884     if (element == EL_DIAGONAL_GROWING ||
10885         element == EL_DIAGONAL_SHRINKING)
10886     {
10887       target_element = Store[x][y];
10888
10889       Store[x][y] = EL_EMPTY;
10890     }
10891
10892     // special case: element changes to player (and may be kept if walkable)
10893     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10894       CreateElementFromChange(x, y, EL_EMPTY);
10895
10896     CreateElementFromChange(x, y, target_element);
10897
10898     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10899     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10900   }
10901
10902   // this uses direct change before indirect change
10903   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10904
10905   return TRUE;
10906 }
10907
10908 static void HandleElementChange(int x, int y, int page)
10909 {
10910   int element = MovingOrBlocked2Element(x, y);
10911   struct ElementInfo *ei = &element_info[element];
10912   struct ElementChangeInfo *change = &ei->change_page[page];
10913   boolean handle_action_before_change = FALSE;
10914
10915 #ifdef DEBUG
10916   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10917       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10918   {
10919     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10920           x, y, element, element_info[element].token_name);
10921     Debug("game:playing:HandleElementChange", "This should never happen!");
10922   }
10923 #endif
10924
10925   // this can happen with classic bombs on walkable, changing elements
10926   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10927   {
10928     return;
10929   }
10930
10931   if (ChangeDelay[x][y] == 0)           // initialize element change
10932   {
10933     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10934
10935     if (change->can_change)
10936     {
10937       // !!! not clear why graphic animation should be reset at all here !!!
10938       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10939       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10940
10941       /*
10942         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10943
10944         When using an animation frame delay of 1 (this only happens with
10945         "sp_zonk.moving.left/right" in the classic graphics), the default
10946         (non-moving) animation shows wrong animation frames (while the
10947         moving animation, like "sp_zonk.moving.left/right", is correct,
10948         so this graphical bug never shows up with the classic graphics).
10949         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10950         be drawn instead of the correct frames 0,1,2,3. This is caused by
10951         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10952         an element change: First when the change delay ("ChangeDelay[][]")
10953         counter has reached zero after decrementing, then a second time in
10954         the next frame (after "GfxFrame[][]" was already incremented) when
10955         "ChangeDelay[][]" is reset to the initial delay value again.
10956
10957         This causes frame 0 to be drawn twice, while the last frame won't
10958         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10959
10960         As some animations may already be cleverly designed around this bug
10961         (at least the "Snake Bite" snake tail animation does this), it cannot
10962         simply be fixed here without breaking such existing animations.
10963         Unfortunately, it cannot easily be detected if a graphics set was
10964         designed "before" or "after" the bug was fixed. As a workaround,
10965         a new graphics set option "game.graphics_engine_version" was added
10966         to be able to specify the game's major release version for which the
10967         graphics set was designed, which can then be used to decide if the
10968         bugfix should be used (version 4 and above) or not (version 3 or
10969         below, or if no version was specified at all, as with old sets).
10970
10971         (The wrong/fixed animation frames can be tested with the test level set
10972         "test_gfxframe" and level "000", which contains a specially prepared
10973         custom element at level position (x/y) == (11/9) which uses the zonk
10974         animation mentioned above. Using "game.graphics_engine_version: 4"
10975         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10976         This can also be seen from the debug output for this test element.)
10977       */
10978
10979       // when a custom element is about to change (for example by change delay),
10980       // do not reset graphic animation when the custom element is moving
10981       if (game.graphics_engine_version < 4 &&
10982           !IS_MOVING(x, y))
10983       {
10984         ResetGfxAnimation(x, y);
10985         ResetRandomAnimationValue(x, y);
10986       }
10987
10988       if (change->pre_change_function)
10989         change->pre_change_function(x, y);
10990     }
10991   }
10992
10993   ChangeDelay[x][y]--;
10994
10995   if (ChangeDelay[x][y] != 0)           // continue element change
10996   {
10997     if (change->can_change)
10998     {
10999       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11000
11001       if (IS_ANIMATED(graphic))
11002         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11003
11004       if (change->change_function)
11005         change->change_function(x, y);
11006     }
11007   }
11008   else                                  // finish element change
11009   {
11010     if (ChangePage[x][y] != -1)         // remember page from delayed change
11011     {
11012       page = ChangePage[x][y];
11013       ChangePage[x][y] = -1;
11014
11015       change = &ei->change_page[page];
11016     }
11017
11018     if (IS_MOVING(x, y))                // never change a running system ;-)
11019     {
11020       ChangeDelay[x][y] = 1;            // try change after next move step
11021       ChangePage[x][y] = page;          // remember page to use for change
11022
11023       return;
11024     }
11025
11026     // special case: set new level random seed before changing element
11027     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11028       handle_action_before_change = TRUE;
11029
11030     if (change->has_action && handle_action_before_change)
11031       ExecuteCustomElementAction(x, y, element, page);
11032
11033     if (change->can_change)
11034     {
11035       if (ChangeElement(x, y, element, page))
11036       {
11037         if (change->post_change_function)
11038           change->post_change_function(x, y);
11039       }
11040     }
11041
11042     if (change->has_action && !handle_action_before_change)
11043       ExecuteCustomElementAction(x, y, element, page);
11044   }
11045 }
11046
11047 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11048                                               int trigger_element,
11049                                               int trigger_event,
11050                                               int trigger_player,
11051                                               int trigger_side,
11052                                               int trigger_page)
11053 {
11054   boolean change_done_any = FALSE;
11055   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11056   int i;
11057
11058   if (!(trigger_events[trigger_element][trigger_event]))
11059     return FALSE;
11060
11061   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11062
11063   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11064   {
11065     int element = EL_CUSTOM_START + i;
11066     boolean change_done = FALSE;
11067     int p;
11068
11069     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11070         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11071       continue;
11072
11073     for (p = 0; p < element_info[element].num_change_pages; p++)
11074     {
11075       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11076
11077       if (change->can_change_or_has_action &&
11078           change->has_event[trigger_event] &&
11079           change->trigger_side & trigger_side &&
11080           change->trigger_player & trigger_player &&
11081           change->trigger_page & trigger_page_bits &&
11082           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11083       {
11084         change->actual_trigger_element = trigger_element;
11085         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11086         change->actual_trigger_player_bits = trigger_player;
11087         change->actual_trigger_side = trigger_side;
11088         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11089         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11090
11091         if ((change->can_change && !change_done) || change->has_action)
11092         {
11093           int x, y;
11094
11095           SCAN_PLAYFIELD(x, y)
11096           {
11097             if (Tile[x][y] == element)
11098             {
11099               if (change->can_change && !change_done)
11100               {
11101                 // if element already changed in this frame, not only prevent
11102                 // another element change (checked in ChangeElement()), but
11103                 // also prevent additional element actions for this element
11104
11105                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11106                     !level.use_action_after_change_bug)
11107                   continue;
11108
11109                 ChangeDelay[x][y] = 1;
11110                 ChangeEvent[x][y] = trigger_event;
11111
11112                 HandleElementChange(x, y, p);
11113               }
11114               else if (change->has_action)
11115               {
11116                 // if element already changed in this frame, not only prevent
11117                 // another element change (checked in ChangeElement()), but
11118                 // also prevent additional element actions for this element
11119
11120                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11121                     !level.use_action_after_change_bug)
11122                   continue;
11123
11124                 ExecuteCustomElementAction(x, y, element, p);
11125                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11126               }
11127             }
11128           }
11129
11130           if (change->can_change)
11131           {
11132             change_done = TRUE;
11133             change_done_any = TRUE;
11134           }
11135         }
11136       }
11137     }
11138   }
11139
11140   RECURSION_LOOP_DETECTION_END();
11141
11142   return change_done_any;
11143 }
11144
11145 static boolean CheckElementChangeExt(int x, int y,
11146                                      int element,
11147                                      int trigger_element,
11148                                      int trigger_event,
11149                                      int trigger_player,
11150                                      int trigger_side)
11151 {
11152   boolean change_done = FALSE;
11153   int p;
11154
11155   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11156       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11157     return FALSE;
11158
11159   if (Tile[x][y] == EL_BLOCKED)
11160   {
11161     Blocked2Moving(x, y, &x, &y);
11162     element = Tile[x][y];
11163   }
11164
11165   // check if element has already changed or is about to change after moving
11166   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11167        Tile[x][y] != element) ||
11168
11169       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11170        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11171         ChangePage[x][y] != -1)))
11172     return FALSE;
11173
11174   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11175
11176   for (p = 0; p < element_info[element].num_change_pages; p++)
11177   {
11178     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11179
11180     /* check trigger element for all events where the element that is checked
11181        for changing interacts with a directly adjacent element -- this is
11182        different to element changes that affect other elements to change on the
11183        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11184     boolean check_trigger_element =
11185       (trigger_event == CE_NEXT_TO_X ||
11186        trigger_event == CE_TOUCHING_X ||
11187        trigger_event == CE_HITTING_X ||
11188        trigger_event == CE_HIT_BY_X ||
11189        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11190
11191     if (change->can_change_or_has_action &&
11192         change->has_event[trigger_event] &&
11193         change->trigger_side & trigger_side &&
11194         change->trigger_player & trigger_player &&
11195         (!check_trigger_element ||
11196          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11197     {
11198       change->actual_trigger_element = trigger_element;
11199       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11200       change->actual_trigger_player_bits = trigger_player;
11201       change->actual_trigger_side = trigger_side;
11202       change->actual_trigger_ce_value = CustomValue[x][y];
11203       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11204
11205       // special case: trigger element not at (x,y) position for some events
11206       if (check_trigger_element)
11207       {
11208         static struct
11209         {
11210           int dx, dy;
11211         } move_xy[] =
11212           {
11213             {  0,  0 },
11214             { -1,  0 },
11215             { +1,  0 },
11216             {  0,  0 },
11217             {  0, -1 },
11218             {  0,  0 }, { 0, 0 }, { 0, 0 },
11219             {  0, +1 }
11220           };
11221
11222         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11223         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11224
11225         change->actual_trigger_ce_value = CustomValue[xx][yy];
11226         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11227       }
11228
11229       if (change->can_change && !change_done)
11230       {
11231         ChangeDelay[x][y] = 1;
11232         ChangeEvent[x][y] = trigger_event;
11233
11234         HandleElementChange(x, y, p);
11235
11236         change_done = TRUE;
11237       }
11238       else if (change->has_action)
11239       {
11240         ExecuteCustomElementAction(x, y, element, p);
11241         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11242       }
11243     }
11244   }
11245
11246   RECURSION_LOOP_DETECTION_END();
11247
11248   return change_done;
11249 }
11250
11251 static void PlayPlayerSound(struct PlayerInfo *player)
11252 {
11253   int jx = player->jx, jy = player->jy;
11254   int sound_element = player->artwork_element;
11255   int last_action = player->last_action_waiting;
11256   int action = player->action_waiting;
11257
11258   if (player->is_waiting)
11259   {
11260     if (action != last_action)
11261       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11262     else
11263       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11264   }
11265   else
11266   {
11267     if (action != last_action)
11268       StopSound(element_info[sound_element].sound[last_action]);
11269
11270     if (last_action == ACTION_SLEEPING)
11271       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11272   }
11273 }
11274
11275 static void PlayAllPlayersSound(void)
11276 {
11277   int i;
11278
11279   for (i = 0; i < MAX_PLAYERS; i++)
11280     if (stored_player[i].active)
11281       PlayPlayerSound(&stored_player[i]);
11282 }
11283
11284 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11285 {
11286   boolean last_waiting = player->is_waiting;
11287   int move_dir = player->MovDir;
11288
11289   player->dir_waiting = move_dir;
11290   player->last_action_waiting = player->action_waiting;
11291
11292   if (is_waiting)
11293   {
11294     if (!last_waiting)          // not waiting -> waiting
11295     {
11296       player->is_waiting = TRUE;
11297
11298       player->frame_counter_bored =
11299         FrameCounter +
11300         game.player_boring_delay_fixed +
11301         GetSimpleRandom(game.player_boring_delay_random);
11302       player->frame_counter_sleeping =
11303         FrameCounter +
11304         game.player_sleeping_delay_fixed +
11305         GetSimpleRandom(game.player_sleeping_delay_random);
11306
11307       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11308     }
11309
11310     if (game.player_sleeping_delay_fixed +
11311         game.player_sleeping_delay_random > 0 &&
11312         player->anim_delay_counter == 0 &&
11313         player->post_delay_counter == 0 &&
11314         FrameCounter >= player->frame_counter_sleeping)
11315       player->is_sleeping = TRUE;
11316     else if (game.player_boring_delay_fixed +
11317              game.player_boring_delay_random > 0 &&
11318              FrameCounter >= player->frame_counter_bored)
11319       player->is_bored = TRUE;
11320
11321     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11322                               player->is_bored ? ACTION_BORING :
11323                               ACTION_WAITING);
11324
11325     if (player->is_sleeping && player->use_murphy)
11326     {
11327       // special case for sleeping Murphy when leaning against non-free tile
11328
11329       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11330           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11331            !IS_MOVING(player->jx - 1, player->jy)))
11332         move_dir = MV_LEFT;
11333       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11334                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11335                 !IS_MOVING(player->jx + 1, player->jy)))
11336         move_dir = MV_RIGHT;
11337       else
11338         player->is_sleeping = FALSE;
11339
11340       player->dir_waiting = move_dir;
11341     }
11342
11343     if (player->is_sleeping)
11344     {
11345       if (player->num_special_action_sleeping > 0)
11346       {
11347         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11348         {
11349           int last_special_action = player->special_action_sleeping;
11350           int num_special_action = player->num_special_action_sleeping;
11351           int special_action =
11352             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11353              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11354              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11355              last_special_action + 1 : ACTION_SLEEPING);
11356           int special_graphic =
11357             el_act_dir2img(player->artwork_element, special_action, move_dir);
11358
11359           player->anim_delay_counter =
11360             graphic_info[special_graphic].anim_delay_fixed +
11361             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11362           player->post_delay_counter =
11363             graphic_info[special_graphic].post_delay_fixed +
11364             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11365
11366           player->special_action_sleeping = special_action;
11367         }
11368
11369         if (player->anim_delay_counter > 0)
11370         {
11371           player->action_waiting = player->special_action_sleeping;
11372           player->anim_delay_counter--;
11373         }
11374         else if (player->post_delay_counter > 0)
11375         {
11376           player->post_delay_counter--;
11377         }
11378       }
11379     }
11380     else if (player->is_bored)
11381     {
11382       if (player->num_special_action_bored > 0)
11383       {
11384         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11385         {
11386           int special_action =
11387             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11388           int special_graphic =
11389             el_act_dir2img(player->artwork_element, special_action, move_dir);
11390
11391           player->anim_delay_counter =
11392             graphic_info[special_graphic].anim_delay_fixed +
11393             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11394           player->post_delay_counter =
11395             graphic_info[special_graphic].post_delay_fixed +
11396             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11397
11398           player->special_action_bored = special_action;
11399         }
11400
11401         if (player->anim_delay_counter > 0)
11402         {
11403           player->action_waiting = player->special_action_bored;
11404           player->anim_delay_counter--;
11405         }
11406         else if (player->post_delay_counter > 0)
11407         {
11408           player->post_delay_counter--;
11409         }
11410       }
11411     }
11412   }
11413   else if (last_waiting)        // waiting -> not waiting
11414   {
11415     player->is_waiting = FALSE;
11416     player->is_bored = FALSE;
11417     player->is_sleeping = FALSE;
11418
11419     player->frame_counter_bored = -1;
11420     player->frame_counter_sleeping = -1;
11421
11422     player->anim_delay_counter = 0;
11423     player->post_delay_counter = 0;
11424
11425     player->dir_waiting = player->MovDir;
11426     player->action_waiting = ACTION_DEFAULT;
11427
11428     player->special_action_bored = ACTION_DEFAULT;
11429     player->special_action_sleeping = ACTION_DEFAULT;
11430   }
11431 }
11432
11433 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11434 {
11435   if ((!player->is_moving  && player->was_moving) ||
11436       (player->MovPos == 0 && player->was_moving) ||
11437       (player->is_snapping && !player->was_snapping) ||
11438       (player->is_dropping && !player->was_dropping))
11439   {
11440     if (!CheckSaveEngineSnapshotToList())
11441       return;
11442
11443     player->was_moving = FALSE;
11444     player->was_snapping = TRUE;
11445     player->was_dropping = TRUE;
11446   }
11447   else
11448   {
11449     if (player->is_moving)
11450       player->was_moving = TRUE;
11451
11452     if (!player->is_snapping)
11453       player->was_snapping = FALSE;
11454
11455     if (!player->is_dropping)
11456       player->was_dropping = FALSE;
11457   }
11458
11459   static struct MouseActionInfo mouse_action_last = { 0 };
11460   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11461   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11462
11463   if (new_released)
11464     CheckSaveEngineSnapshotToList();
11465
11466   mouse_action_last = mouse_action;
11467 }
11468
11469 static void CheckSingleStepMode(struct PlayerInfo *player)
11470 {
11471   if (tape.single_step && tape.recording && !tape.pausing)
11472   {
11473     // as it is called "single step mode", just return to pause mode when the
11474     // player stopped moving after one tile (or never starts moving at all)
11475     // (reverse logic needed here in case single step mode used in team mode)
11476     if (player->is_moving ||
11477         player->is_pushing ||
11478         player->is_dropping_pressed ||
11479         player->effective_mouse_action.button)
11480       game.enter_single_step_mode = FALSE;
11481   }
11482
11483   CheckSaveEngineSnapshot(player);
11484 }
11485
11486 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11487 {
11488   int left      = player_action & JOY_LEFT;
11489   int right     = player_action & JOY_RIGHT;
11490   int up        = player_action & JOY_UP;
11491   int down      = player_action & JOY_DOWN;
11492   int button1   = player_action & JOY_BUTTON_1;
11493   int button2   = player_action & JOY_BUTTON_2;
11494   int dx        = (left ? -1 : right ? 1 : 0);
11495   int dy        = (up   ? -1 : down  ? 1 : 0);
11496
11497   if (!player->active || tape.pausing)
11498     return 0;
11499
11500   if (player_action)
11501   {
11502     if (button1)
11503       SnapField(player, dx, dy);
11504     else
11505     {
11506       if (button2)
11507         DropElement(player);
11508
11509       MovePlayer(player, dx, dy);
11510     }
11511
11512     CheckSingleStepMode(player);
11513
11514     SetPlayerWaiting(player, FALSE);
11515
11516     return player_action;
11517   }
11518   else
11519   {
11520     // no actions for this player (no input at player's configured device)
11521
11522     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11523     SnapField(player, 0, 0);
11524     CheckGravityMovementWhenNotMoving(player);
11525
11526     if (player->MovPos == 0)
11527       SetPlayerWaiting(player, TRUE);
11528
11529     if (player->MovPos == 0)    // needed for tape.playing
11530       player->is_moving = FALSE;
11531
11532     player->is_dropping = FALSE;
11533     player->is_dropping_pressed = FALSE;
11534     player->drop_pressed_delay = 0;
11535
11536     CheckSingleStepMode(player);
11537
11538     return 0;
11539   }
11540 }
11541
11542 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11543                                          byte *tape_action)
11544 {
11545   if (!tape.use_mouse_actions)
11546     return;
11547
11548   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11549   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11550   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11551 }
11552
11553 static void SetTapeActionFromMouseAction(byte *tape_action,
11554                                          struct MouseActionInfo *mouse_action)
11555 {
11556   if (!tape.use_mouse_actions)
11557     return;
11558
11559   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11560   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11561   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11562 }
11563
11564 static void CheckLevelSolved(void)
11565 {
11566   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11567   {
11568     if (game_em.level_solved &&
11569         !game_em.game_over)                             // game won
11570     {
11571       LevelSolved();
11572
11573       game_em.game_over = TRUE;
11574
11575       game.all_players_gone = TRUE;
11576     }
11577
11578     if (game_em.game_over)                              // game lost
11579       game.all_players_gone = TRUE;
11580   }
11581   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11582   {
11583     if (game_sp.level_solved &&
11584         !game_sp.game_over)                             // game won
11585     {
11586       LevelSolved();
11587
11588       game_sp.game_over = TRUE;
11589
11590       game.all_players_gone = TRUE;
11591     }
11592
11593     if (game_sp.game_over)                              // game lost
11594       game.all_players_gone = TRUE;
11595   }
11596   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11597   {
11598     if (game_mm.level_solved &&
11599         !game_mm.game_over)                             // game won
11600     {
11601       LevelSolved();
11602
11603       game_mm.game_over = TRUE;
11604
11605       game.all_players_gone = TRUE;
11606     }
11607
11608     if (game_mm.game_over)                              // game lost
11609       game.all_players_gone = TRUE;
11610   }
11611 }
11612
11613 static void CheckLevelTime_StepCounter(void)
11614 {
11615   int i;
11616
11617   TimePlayed++;
11618
11619   if (TimeLeft > 0)
11620   {
11621     TimeLeft--;
11622
11623     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11624       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11625
11626     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11627
11628     DisplayGameControlValues();
11629
11630     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11631       for (i = 0; i < MAX_PLAYERS; i++)
11632         KillPlayer(&stored_player[i]);
11633   }
11634   else if (game.no_time_limit && !game.all_players_gone)
11635   {
11636     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11637
11638     DisplayGameControlValues();
11639   }
11640 }
11641
11642 static void CheckLevelTime(void)
11643 {
11644   int i;
11645
11646   if (TimeFrames >= FRAMES_PER_SECOND)
11647   {
11648     TimeFrames = 0;
11649     TapeTime++;
11650
11651     for (i = 0; i < MAX_PLAYERS; i++)
11652     {
11653       struct PlayerInfo *player = &stored_player[i];
11654
11655       if (SHIELD_ON(player))
11656       {
11657         player->shield_normal_time_left--;
11658
11659         if (player->shield_deadly_time_left > 0)
11660           player->shield_deadly_time_left--;
11661       }
11662     }
11663
11664     if (!game.LevelSolved && !level.use_step_counter)
11665     {
11666       TimePlayed++;
11667
11668       if (TimeLeft > 0)
11669       {
11670         TimeLeft--;
11671
11672         if (TimeLeft <= 10 && setup.time_limit)
11673           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11674
11675         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11676            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11677
11678         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11679
11680         if (!TimeLeft && setup.time_limit)
11681         {
11682           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11683             game_em.lev->killed_out_of_time = TRUE;
11684           else
11685             for (i = 0; i < MAX_PLAYERS; i++)
11686               KillPlayer(&stored_player[i]);
11687         }
11688       }
11689       else if (game.no_time_limit && !game.all_players_gone)
11690       {
11691         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11692       }
11693
11694       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11695     }
11696
11697     if (tape.recording || tape.playing)
11698       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11699   }
11700
11701   if (tape.recording || tape.playing)
11702     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11703
11704   UpdateAndDisplayGameControlValues();
11705 }
11706
11707 void AdvanceFrameAndPlayerCounters(int player_nr)
11708 {
11709   int i;
11710
11711   // advance frame counters (global frame counter and time frame counter)
11712   FrameCounter++;
11713   TimeFrames++;
11714
11715   // advance player counters (counters for move delay, move animation etc.)
11716   for (i = 0; i < MAX_PLAYERS; i++)
11717   {
11718     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11719     int move_delay_value = stored_player[i].move_delay_value;
11720     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11721
11722     if (!advance_player_counters)       // not all players may be affected
11723       continue;
11724
11725     if (move_frames == 0)       // less than one move per game frame
11726     {
11727       int stepsize = TILEX / move_delay_value;
11728       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11729       int count = (stored_player[i].is_moving ?
11730                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11731
11732       if (count % delay == 0)
11733         move_frames = 1;
11734     }
11735
11736     stored_player[i].Frame += move_frames;
11737
11738     if (stored_player[i].MovPos != 0)
11739       stored_player[i].StepFrame += move_frames;
11740
11741     if (stored_player[i].move_delay > 0)
11742       stored_player[i].move_delay--;
11743
11744     // due to bugs in previous versions, counter must count up, not down
11745     if (stored_player[i].push_delay != -1)
11746       stored_player[i].push_delay++;
11747
11748     if (stored_player[i].drop_delay > 0)
11749       stored_player[i].drop_delay--;
11750
11751     if (stored_player[i].is_dropping_pressed)
11752       stored_player[i].drop_pressed_delay++;
11753   }
11754 }
11755
11756 void StartGameActions(boolean init_network_game, boolean record_tape,
11757                       int random_seed)
11758 {
11759   unsigned int new_random_seed = InitRND(random_seed);
11760
11761   if (record_tape)
11762     TapeStartRecording(new_random_seed);
11763
11764   if (init_network_game)
11765   {
11766     SendToServer_LevelFile();
11767     SendToServer_StartPlaying();
11768
11769     return;
11770   }
11771
11772   InitGame();
11773 }
11774
11775 static void GameActionsExt(void)
11776 {
11777 #if 0
11778   static unsigned int game_frame_delay = 0;
11779 #endif
11780   unsigned int game_frame_delay_value;
11781   byte *recorded_player_action;
11782   byte summarized_player_action = 0;
11783   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11784   int i;
11785
11786   // detect endless loops, caused by custom element programming
11787   if (recursion_loop_detected && recursion_loop_depth == 0)
11788   {
11789     char *message = getStringCat3("Internal Error! Element ",
11790                                   EL_NAME(recursion_loop_element),
11791                                   " caused endless loop! Quit the game?");
11792
11793     Warn("element '%s' caused endless loop in game engine",
11794          EL_NAME(recursion_loop_element));
11795
11796     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11797
11798     recursion_loop_detected = FALSE;    // if game should be continued
11799
11800     free(message);
11801
11802     return;
11803   }
11804
11805   if (game.restart_level)
11806     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11807
11808   CheckLevelSolved();
11809
11810   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11811     GameWon();
11812
11813   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11814     TapeStop();
11815
11816   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11817     return;
11818
11819   game_frame_delay_value =
11820     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11821
11822   if (tape.playing && tape.warp_forward && !tape.pausing)
11823     game_frame_delay_value = 0;
11824
11825   SetVideoFrameDelay(game_frame_delay_value);
11826
11827   // (de)activate virtual buttons depending on current game status
11828   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11829   {
11830     if (game.all_players_gone)  // if no players there to be controlled anymore
11831       SetOverlayActive(FALSE);
11832     else if (!tape.playing)     // if game continues after tape stopped playing
11833       SetOverlayActive(TRUE);
11834   }
11835
11836 #if 0
11837 #if 0
11838   // ---------- main game synchronization point ----------
11839
11840   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11841
11842   Debug("game:playing:skip", "skip == %d", skip);
11843
11844 #else
11845   // ---------- main game synchronization point ----------
11846
11847   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11848 #endif
11849 #endif
11850
11851   if (network_playing && !network_player_action_received)
11852   {
11853     // try to get network player actions in time
11854
11855     // last chance to get network player actions without main loop delay
11856     HandleNetworking();
11857
11858     // game was quit by network peer
11859     if (game_status != GAME_MODE_PLAYING)
11860       return;
11861
11862     // check if network player actions still missing and game still running
11863     if (!network_player_action_received && !checkGameEnded())
11864       return;           // failed to get network player actions in time
11865
11866     // do not yet reset "network_player_action_received" (for tape.pausing)
11867   }
11868
11869   if (tape.pausing)
11870     return;
11871
11872   // at this point we know that we really continue executing the game
11873
11874   network_player_action_received = FALSE;
11875
11876   // when playing tape, read previously recorded player input from tape data
11877   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11878
11879   local_player->effective_mouse_action = local_player->mouse_action;
11880
11881   if (recorded_player_action != NULL)
11882     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11883                                  recorded_player_action);
11884
11885   // TapePlayAction() may return NULL when toggling to "pause before death"
11886   if (tape.pausing)
11887     return;
11888
11889   if (tape.set_centered_player)
11890   {
11891     game.centered_player_nr_next = tape.centered_player_nr_next;
11892     game.set_centered_player = TRUE;
11893   }
11894
11895   for (i = 0; i < MAX_PLAYERS; i++)
11896   {
11897     summarized_player_action |= stored_player[i].action;
11898
11899     if (!network_playing && (game.team_mode || tape.playing))
11900       stored_player[i].effective_action = stored_player[i].action;
11901   }
11902
11903   if (network_playing && !checkGameEnded())
11904     SendToServer_MovePlayer(summarized_player_action);
11905
11906   // summarize all actions at local players mapped input device position
11907   // (this allows using different input devices in single player mode)
11908   if (!network.enabled && !game.team_mode)
11909     stored_player[map_player_action[local_player->index_nr]].effective_action =
11910       summarized_player_action;
11911
11912   // summarize all actions at centered player in local team mode
11913   if (tape.recording &&
11914       setup.team_mode && !network.enabled &&
11915       setup.input_on_focus &&
11916       game.centered_player_nr != -1)
11917   {
11918     for (i = 0; i < MAX_PLAYERS; i++)
11919       stored_player[map_player_action[i]].effective_action =
11920         (i == game.centered_player_nr ? summarized_player_action : 0);
11921   }
11922
11923   if (recorded_player_action != NULL)
11924     for (i = 0; i < MAX_PLAYERS; i++)
11925       stored_player[i].effective_action = recorded_player_action[i];
11926
11927   for (i = 0; i < MAX_PLAYERS; i++)
11928   {
11929     tape_action[i] = stored_player[i].effective_action;
11930
11931     /* (this may happen in the RND game engine if a player was not present on
11932        the playfield on level start, but appeared later from a custom element */
11933     if (setup.team_mode &&
11934         tape.recording &&
11935         tape_action[i] &&
11936         !tape.player_participates[i])
11937       tape.player_participates[i] = TRUE;
11938   }
11939
11940   SetTapeActionFromMouseAction(tape_action,
11941                                &local_player->effective_mouse_action);
11942
11943   // only record actions from input devices, but not programmed actions
11944   if (tape.recording)
11945     TapeRecordAction(tape_action);
11946
11947   // remember if game was played (especially after tape stopped playing)
11948   if (!tape.playing && summarized_player_action)
11949     game.GamePlayed = TRUE;
11950
11951 #if USE_NEW_PLAYER_ASSIGNMENTS
11952   // !!! also map player actions in single player mode !!!
11953   // if (game.team_mode)
11954   if (1)
11955   {
11956     byte mapped_action[MAX_PLAYERS];
11957
11958 #if DEBUG_PLAYER_ACTIONS
11959     for (i = 0; i < MAX_PLAYERS; i++)
11960       DebugContinued("", "%d, ", stored_player[i].effective_action);
11961 #endif
11962
11963     for (i = 0; i < MAX_PLAYERS; i++)
11964       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11965
11966     for (i = 0; i < MAX_PLAYERS; i++)
11967       stored_player[i].effective_action = mapped_action[i];
11968
11969 #if DEBUG_PLAYER_ACTIONS
11970     DebugContinued("", "=> ");
11971     for (i = 0; i < MAX_PLAYERS; i++)
11972       DebugContinued("", "%d, ", stored_player[i].effective_action);
11973     DebugContinued("game:playing:player", "\n");
11974 #endif
11975   }
11976 #if DEBUG_PLAYER_ACTIONS
11977   else
11978   {
11979     for (i = 0; i < MAX_PLAYERS; i++)
11980       DebugContinued("", "%d, ", stored_player[i].effective_action);
11981     DebugContinued("game:playing:player", "\n");
11982   }
11983 #endif
11984 #endif
11985
11986   for (i = 0; i < MAX_PLAYERS; i++)
11987   {
11988     // allow engine snapshot in case of changed movement attempt
11989     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11990         (stored_player[i].effective_action & KEY_MOTION))
11991       game.snapshot.changed_action = TRUE;
11992
11993     // allow engine snapshot in case of snapping/dropping attempt
11994     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11995         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11996       game.snapshot.changed_action = TRUE;
11997
11998     game.snapshot.last_action[i] = stored_player[i].effective_action;
11999   }
12000
12001   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12002   {
12003     GameActions_EM_Main();
12004   }
12005   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12006   {
12007     GameActions_SP_Main();
12008   }
12009   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12010   {
12011     GameActions_MM_Main();
12012   }
12013   else
12014   {
12015     GameActions_RND_Main();
12016   }
12017
12018   BlitScreenToBitmap(backbuffer);
12019
12020   CheckLevelSolved();
12021   CheckLevelTime();
12022
12023   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12024
12025   if (global.show_frames_per_second)
12026   {
12027     static unsigned int fps_counter = 0;
12028     static int fps_frames = 0;
12029     unsigned int fps_delay_ms = Counter() - fps_counter;
12030
12031     fps_frames++;
12032
12033     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12034     {
12035       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12036
12037       fps_frames = 0;
12038       fps_counter = Counter();
12039
12040       // always draw FPS to screen after FPS value was updated
12041       redraw_mask |= REDRAW_FPS;
12042     }
12043
12044     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12045     if (GetDrawDeactivationMask() == REDRAW_NONE)
12046       redraw_mask |= REDRAW_FPS;
12047   }
12048 }
12049
12050 static void GameActions_CheckSaveEngineSnapshot(void)
12051 {
12052   if (!game.snapshot.save_snapshot)
12053     return;
12054
12055   // clear flag for saving snapshot _before_ saving snapshot
12056   game.snapshot.save_snapshot = FALSE;
12057
12058   SaveEngineSnapshotToList();
12059 }
12060
12061 void GameActions(void)
12062 {
12063   GameActionsExt();
12064
12065   GameActions_CheckSaveEngineSnapshot();
12066 }
12067
12068 void GameActions_EM_Main(void)
12069 {
12070   byte effective_action[MAX_PLAYERS];
12071   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12072   int i;
12073
12074   for (i = 0; i < MAX_PLAYERS; i++)
12075     effective_action[i] = stored_player[i].effective_action;
12076
12077   GameActions_EM(effective_action, warp_mode);
12078 }
12079
12080 void GameActions_SP_Main(void)
12081 {
12082   byte effective_action[MAX_PLAYERS];
12083   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12084   int i;
12085
12086   for (i = 0; i < MAX_PLAYERS; i++)
12087     effective_action[i] = stored_player[i].effective_action;
12088
12089   GameActions_SP(effective_action, warp_mode);
12090
12091   for (i = 0; i < MAX_PLAYERS; i++)
12092   {
12093     if (stored_player[i].force_dropping)
12094       stored_player[i].action |= KEY_BUTTON_DROP;
12095
12096     stored_player[i].force_dropping = FALSE;
12097   }
12098 }
12099
12100 void GameActions_MM_Main(void)
12101 {
12102   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12103
12104   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12105 }
12106
12107 void GameActions_RND_Main(void)
12108 {
12109   GameActions_RND();
12110 }
12111
12112 void GameActions_RND(void)
12113 {
12114   static struct MouseActionInfo mouse_action_last = { 0 };
12115   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12116   int magic_wall_x = 0, magic_wall_y = 0;
12117   int i, x, y, element, graphic, last_gfx_frame;
12118
12119   InitPlayfieldScanModeVars();
12120
12121   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12122   {
12123     SCAN_PLAYFIELD(x, y)
12124     {
12125       ChangeCount[x][y] = 0;
12126       ChangeEvent[x][y] = -1;
12127     }
12128   }
12129
12130   if (game.set_centered_player)
12131   {
12132     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12133
12134     // switching to "all players" only possible if all players fit to screen
12135     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12136     {
12137       game.centered_player_nr_next = game.centered_player_nr;
12138       game.set_centered_player = FALSE;
12139     }
12140
12141     // do not switch focus to non-existing (or non-active) player
12142     if (game.centered_player_nr_next >= 0 &&
12143         !stored_player[game.centered_player_nr_next].active)
12144     {
12145       game.centered_player_nr_next = game.centered_player_nr;
12146       game.set_centered_player = FALSE;
12147     }
12148   }
12149
12150   if (game.set_centered_player &&
12151       ScreenMovPos == 0)        // screen currently aligned at tile position
12152   {
12153     int sx, sy;
12154
12155     if (game.centered_player_nr_next == -1)
12156     {
12157       setScreenCenteredToAllPlayers(&sx, &sy);
12158     }
12159     else
12160     {
12161       sx = stored_player[game.centered_player_nr_next].jx;
12162       sy = stored_player[game.centered_player_nr_next].jy;
12163     }
12164
12165     game.centered_player_nr = game.centered_player_nr_next;
12166     game.set_centered_player = FALSE;
12167
12168     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12169     DrawGameDoorValues();
12170   }
12171
12172   // check single step mode (set flag and clear again if any player is active)
12173   game.enter_single_step_mode =
12174     (tape.single_step && tape.recording && !tape.pausing);
12175
12176   for (i = 0; i < MAX_PLAYERS; i++)
12177   {
12178     int actual_player_action = stored_player[i].effective_action;
12179
12180 #if 1
12181     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12182        - rnd_equinox_tetrachloride 048
12183        - rnd_equinox_tetrachloride_ii 096
12184        - rnd_emanuel_schmieg 002
12185        - doctor_sloan_ww 001, 020
12186     */
12187     if (stored_player[i].MovPos == 0)
12188       CheckGravityMovement(&stored_player[i]);
12189 #endif
12190
12191     // overwrite programmed action with tape action
12192     if (stored_player[i].programmed_action)
12193       actual_player_action = stored_player[i].programmed_action;
12194
12195     PlayerActions(&stored_player[i], actual_player_action);
12196
12197     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12198   }
12199
12200   // single step pause mode may already have been toggled by "ScrollPlayer()"
12201   if (game.enter_single_step_mode && !tape.pausing)
12202     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12203
12204   ScrollScreen(NULL, SCROLL_GO_ON);
12205
12206   /* for backwards compatibility, the following code emulates a fixed bug that
12207      occured when pushing elements (causing elements that just made their last
12208      pushing step to already (if possible) make their first falling step in the
12209      same game frame, which is bad); this code is also needed to use the famous
12210      "spring push bug" which is used in older levels and might be wanted to be
12211      used also in newer levels, but in this case the buggy pushing code is only
12212      affecting the "spring" element and no other elements */
12213
12214   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12215   {
12216     for (i = 0; i < MAX_PLAYERS; i++)
12217     {
12218       struct PlayerInfo *player = &stored_player[i];
12219       int x = player->jx;
12220       int y = player->jy;
12221
12222       if (player->active && player->is_pushing && player->is_moving &&
12223           IS_MOVING(x, y) &&
12224           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12225            Tile[x][y] == EL_SPRING))
12226       {
12227         ContinueMoving(x, y);
12228
12229         // continue moving after pushing (this is actually a bug)
12230         if (!IS_MOVING(x, y))
12231           Stop[x][y] = FALSE;
12232       }
12233     }
12234   }
12235
12236   SCAN_PLAYFIELD(x, y)
12237   {
12238     Last[x][y] = Tile[x][y];
12239
12240     ChangeCount[x][y] = 0;
12241     ChangeEvent[x][y] = -1;
12242
12243     // this must be handled before main playfield loop
12244     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12245     {
12246       MovDelay[x][y]--;
12247       if (MovDelay[x][y] <= 0)
12248         RemoveField(x, y);
12249     }
12250
12251     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12252     {
12253       MovDelay[x][y]--;
12254       if (MovDelay[x][y] <= 0)
12255       {
12256         int element = Store[x][y];
12257         int move_direction = MovDir[x][y];
12258         int player_index_bit = Store2[x][y];
12259
12260         Store[x][y] = 0;
12261         Store2[x][y] = 0;
12262
12263         RemoveField(x, y);
12264         TEST_DrawLevelField(x, y);
12265
12266         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12267
12268         if (IS_ENVELOPE(element))
12269           local_player->show_envelope = element;
12270       }
12271     }
12272
12273 #if DEBUG
12274     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12275     {
12276       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12277             x, y);
12278       Debug("game:playing:GameActions_RND", "This should never happen!");
12279
12280       ChangePage[x][y] = -1;
12281     }
12282 #endif
12283
12284     Stop[x][y] = FALSE;
12285     if (WasJustMoving[x][y] > 0)
12286       WasJustMoving[x][y]--;
12287     if (WasJustFalling[x][y] > 0)
12288       WasJustFalling[x][y]--;
12289     if (CheckCollision[x][y] > 0)
12290       CheckCollision[x][y]--;
12291     if (CheckImpact[x][y] > 0)
12292       CheckImpact[x][y]--;
12293
12294     GfxFrame[x][y]++;
12295
12296     /* reset finished pushing action (not done in ContinueMoving() to allow
12297        continuous pushing animation for elements with zero push delay) */
12298     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12299     {
12300       ResetGfxAnimation(x, y);
12301       TEST_DrawLevelField(x, y);
12302     }
12303
12304 #if DEBUG
12305     if (IS_BLOCKED(x, y))
12306     {
12307       int oldx, oldy;
12308
12309       Blocked2Moving(x, y, &oldx, &oldy);
12310       if (!IS_MOVING(oldx, oldy))
12311       {
12312         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12313         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12314         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12315         Debug("game:playing:GameActions_RND", "This should never happen!");
12316       }
12317     }
12318 #endif
12319   }
12320
12321   if (mouse_action.button)
12322   {
12323     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12324     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12325
12326     x = mouse_action.lx;
12327     y = mouse_action.ly;
12328     element = Tile[x][y];
12329
12330     if (new_button)
12331     {
12332       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12333       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12334                                          ch_button);
12335     }
12336
12337     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12338     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12339                                        ch_button);
12340
12341     if (level.use_step_counter)
12342     {
12343       boolean counted_click = FALSE;
12344
12345       // element clicked that can change when clicked/pressed
12346       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12347           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12348            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12349         counted_click = TRUE;
12350
12351       // element clicked that can trigger change when clicked/pressed
12352       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12353           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12354         counted_click = TRUE;
12355
12356       if (new_button && counted_click)
12357         CheckLevelTime_StepCounter();
12358     }
12359   }
12360
12361   SCAN_PLAYFIELD(x, y)
12362   {
12363     element = Tile[x][y];
12364     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12365     last_gfx_frame = GfxFrame[x][y];
12366
12367     if (element == EL_EMPTY)
12368       graphic = el2img(GfxElementEmpty[x][y]);
12369
12370     ResetGfxFrame(x, y);
12371
12372     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12373       DrawLevelGraphicAnimation(x, y, graphic);
12374
12375     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12376         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12377       ResetRandomAnimationValue(x, y);
12378
12379     SetRandomAnimationValue(x, y);
12380
12381     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12382
12383     if (IS_INACTIVE(element))
12384     {
12385       if (IS_ANIMATED(graphic))
12386         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12387
12388       continue;
12389     }
12390
12391     // this may take place after moving, so 'element' may have changed
12392     if (IS_CHANGING(x, y) &&
12393         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12394     {
12395       int page = element_info[element].event_page_nr[CE_DELAY];
12396
12397       HandleElementChange(x, y, page);
12398
12399       element = Tile[x][y];
12400       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12401     }
12402
12403     CheckNextToConditions(x, y);
12404
12405     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12406     {
12407       StartMoving(x, y);
12408
12409       element = Tile[x][y];
12410       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12411
12412       if (IS_ANIMATED(graphic) &&
12413           !IS_MOVING(x, y) &&
12414           !Stop[x][y])
12415         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12416
12417       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12418         TEST_DrawTwinkleOnField(x, y);
12419     }
12420     else if (element == EL_ACID)
12421     {
12422       if (!Stop[x][y])
12423         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12424     }
12425     else if ((element == EL_EXIT_OPEN ||
12426               element == EL_EM_EXIT_OPEN ||
12427               element == EL_SP_EXIT_OPEN ||
12428               element == EL_STEEL_EXIT_OPEN ||
12429               element == EL_EM_STEEL_EXIT_OPEN ||
12430               element == EL_SP_TERMINAL ||
12431               element == EL_SP_TERMINAL_ACTIVE ||
12432               element == EL_EXTRA_TIME ||
12433               element == EL_SHIELD_NORMAL ||
12434               element == EL_SHIELD_DEADLY) &&
12435              IS_ANIMATED(graphic))
12436       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12437     else if (IS_MOVING(x, y))
12438       ContinueMoving(x, y);
12439     else if (IS_ACTIVE_BOMB(element))
12440       CheckDynamite(x, y);
12441     else if (element == EL_AMOEBA_GROWING)
12442       AmoebaGrowing(x, y);
12443     else if (element == EL_AMOEBA_SHRINKING)
12444       AmoebaShrinking(x, y);
12445
12446 #if !USE_NEW_AMOEBA_CODE
12447     else if (IS_AMOEBALIVE(element))
12448       AmoebaReproduce(x, y);
12449 #endif
12450
12451     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12452       Life(x, y);
12453     else if (element == EL_EXIT_CLOSED)
12454       CheckExit(x, y);
12455     else if (element == EL_EM_EXIT_CLOSED)
12456       CheckExitEM(x, y);
12457     else if (element == EL_STEEL_EXIT_CLOSED)
12458       CheckExitSteel(x, y);
12459     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12460       CheckExitSteelEM(x, y);
12461     else if (element == EL_SP_EXIT_CLOSED)
12462       CheckExitSP(x, y);
12463     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12464              element == EL_EXPANDABLE_STEELWALL_GROWING)
12465       MauerWaechst(x, y);
12466     else if (element == EL_EXPANDABLE_WALL ||
12467              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12468              element == EL_EXPANDABLE_WALL_VERTICAL ||
12469              element == EL_EXPANDABLE_WALL_ANY ||
12470              element == EL_BD_EXPANDABLE_WALL)
12471       MauerAbleger(x, y);
12472     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12473              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12474              element == EL_EXPANDABLE_STEELWALL_ANY)
12475       MauerAblegerStahl(x, y);
12476     else if (element == EL_FLAMES)
12477       CheckForDragon(x, y);
12478     else if (element == EL_EXPLOSION)
12479       ; // drawing of correct explosion animation is handled separately
12480     else if (element == EL_ELEMENT_SNAPPING ||
12481              element == EL_DIAGONAL_SHRINKING ||
12482              element == EL_DIAGONAL_GROWING)
12483     {
12484       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12485
12486       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12487     }
12488     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12489       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12490
12491     if (IS_BELT_ACTIVE(element))
12492       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12493
12494     if (game.magic_wall_active)
12495     {
12496       int jx = local_player->jx, jy = local_player->jy;
12497
12498       // play the element sound at the position nearest to the player
12499       if ((element == EL_MAGIC_WALL_FULL ||
12500            element == EL_MAGIC_WALL_ACTIVE ||
12501            element == EL_MAGIC_WALL_EMPTYING ||
12502            element == EL_BD_MAGIC_WALL_FULL ||
12503            element == EL_BD_MAGIC_WALL_ACTIVE ||
12504            element == EL_BD_MAGIC_WALL_EMPTYING ||
12505            element == EL_DC_MAGIC_WALL_FULL ||
12506            element == EL_DC_MAGIC_WALL_ACTIVE ||
12507            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12508           ABS(x - jx) + ABS(y - jy) <
12509           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12510       {
12511         magic_wall_x = x;
12512         magic_wall_y = y;
12513       }
12514     }
12515   }
12516
12517 #if USE_NEW_AMOEBA_CODE
12518   // new experimental amoeba growth stuff
12519   if (!(FrameCounter % 8))
12520   {
12521     static unsigned int random = 1684108901;
12522
12523     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12524     {
12525       x = RND(lev_fieldx);
12526       y = RND(lev_fieldy);
12527       element = Tile[x][y];
12528
12529       if (!IS_PLAYER(x,y) &&
12530           (element == EL_EMPTY ||
12531            CAN_GROW_INTO(element) ||
12532            element == EL_QUICKSAND_EMPTY ||
12533            element == EL_QUICKSAND_FAST_EMPTY ||
12534            element == EL_ACID_SPLASH_LEFT ||
12535            element == EL_ACID_SPLASH_RIGHT))
12536       {
12537         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12538             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12539             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12540             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12541           Tile[x][y] = EL_AMOEBA_DROP;
12542       }
12543
12544       random = random * 129 + 1;
12545     }
12546   }
12547 #endif
12548
12549   game.explosions_delayed = FALSE;
12550
12551   SCAN_PLAYFIELD(x, y)
12552   {
12553     element = Tile[x][y];
12554
12555     if (ExplodeField[x][y])
12556       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12557     else if (element == EL_EXPLOSION)
12558       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12559
12560     ExplodeField[x][y] = EX_TYPE_NONE;
12561   }
12562
12563   game.explosions_delayed = TRUE;
12564
12565   if (game.magic_wall_active)
12566   {
12567     if (!(game.magic_wall_time_left % 4))
12568     {
12569       int element = Tile[magic_wall_x][magic_wall_y];
12570
12571       if (element == EL_BD_MAGIC_WALL_FULL ||
12572           element == EL_BD_MAGIC_WALL_ACTIVE ||
12573           element == EL_BD_MAGIC_WALL_EMPTYING)
12574         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12575       else if (element == EL_DC_MAGIC_WALL_FULL ||
12576                element == EL_DC_MAGIC_WALL_ACTIVE ||
12577                element == EL_DC_MAGIC_WALL_EMPTYING)
12578         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12579       else
12580         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12581     }
12582
12583     if (game.magic_wall_time_left > 0)
12584     {
12585       game.magic_wall_time_left--;
12586
12587       if (!game.magic_wall_time_left)
12588       {
12589         SCAN_PLAYFIELD(x, y)
12590         {
12591           element = Tile[x][y];
12592
12593           if (element == EL_MAGIC_WALL_ACTIVE ||
12594               element == EL_MAGIC_WALL_FULL)
12595           {
12596             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12597             TEST_DrawLevelField(x, y);
12598           }
12599           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12600                    element == EL_BD_MAGIC_WALL_FULL)
12601           {
12602             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12603             TEST_DrawLevelField(x, y);
12604           }
12605           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12606                    element == EL_DC_MAGIC_WALL_FULL)
12607           {
12608             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12609             TEST_DrawLevelField(x, y);
12610           }
12611         }
12612
12613         game.magic_wall_active = FALSE;
12614       }
12615     }
12616   }
12617
12618   if (game.light_time_left > 0)
12619   {
12620     game.light_time_left--;
12621
12622     if (game.light_time_left == 0)
12623       RedrawAllLightSwitchesAndInvisibleElements();
12624   }
12625
12626   if (game.timegate_time_left > 0)
12627   {
12628     game.timegate_time_left--;
12629
12630     if (game.timegate_time_left == 0)
12631       CloseAllOpenTimegates();
12632   }
12633
12634   if (game.lenses_time_left > 0)
12635   {
12636     game.lenses_time_left--;
12637
12638     if (game.lenses_time_left == 0)
12639       RedrawAllInvisibleElementsForLenses();
12640   }
12641
12642   if (game.magnify_time_left > 0)
12643   {
12644     game.magnify_time_left--;
12645
12646     if (game.magnify_time_left == 0)
12647       RedrawAllInvisibleElementsForMagnifier();
12648   }
12649
12650   for (i = 0; i < MAX_PLAYERS; i++)
12651   {
12652     struct PlayerInfo *player = &stored_player[i];
12653
12654     if (SHIELD_ON(player))
12655     {
12656       if (player->shield_deadly_time_left)
12657         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12658       else if (player->shield_normal_time_left)
12659         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12660     }
12661   }
12662
12663 #if USE_DELAYED_GFX_REDRAW
12664   SCAN_PLAYFIELD(x, y)
12665   {
12666     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12667     {
12668       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12669          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12670
12671       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12672         DrawLevelField(x, y);
12673
12674       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12675         DrawLevelFieldCrumbled(x, y);
12676
12677       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12678         DrawLevelFieldCrumbledNeighbours(x, y);
12679
12680       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12681         DrawTwinkleOnField(x, y);
12682     }
12683
12684     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12685   }
12686 #endif
12687
12688   DrawAllPlayers();
12689   PlayAllPlayersSound();
12690
12691   for (i = 0; i < MAX_PLAYERS; i++)
12692   {
12693     struct PlayerInfo *player = &stored_player[i];
12694
12695     if (player->show_envelope != 0 && (!player->active ||
12696                                        player->MovPos == 0))
12697     {
12698       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12699
12700       player->show_envelope = 0;
12701     }
12702   }
12703
12704   // use random number generator in every frame to make it less predictable
12705   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12706     RND(1);
12707
12708   mouse_action_last = mouse_action;
12709 }
12710
12711 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12712 {
12713   int min_x = x, min_y = y, max_x = x, max_y = y;
12714   int scr_fieldx = getScreenFieldSizeX();
12715   int scr_fieldy = getScreenFieldSizeY();
12716   int i;
12717
12718   for (i = 0; i < MAX_PLAYERS; i++)
12719   {
12720     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12721
12722     if (!stored_player[i].active || &stored_player[i] == player)
12723       continue;
12724
12725     min_x = MIN(min_x, jx);
12726     min_y = MIN(min_y, jy);
12727     max_x = MAX(max_x, jx);
12728     max_y = MAX(max_y, jy);
12729   }
12730
12731   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12732 }
12733
12734 static boolean AllPlayersInVisibleScreen(void)
12735 {
12736   int i;
12737
12738   for (i = 0; i < MAX_PLAYERS; i++)
12739   {
12740     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12741
12742     if (!stored_player[i].active)
12743       continue;
12744
12745     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12746       return FALSE;
12747   }
12748
12749   return TRUE;
12750 }
12751
12752 void ScrollLevel(int dx, int dy)
12753 {
12754   int scroll_offset = 2 * TILEX_VAR;
12755   int x, y;
12756
12757   BlitBitmap(drawto_field, drawto_field,
12758              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12759              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12760              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12761              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12762              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12763              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12764
12765   if (dx != 0)
12766   {
12767     x = (dx == 1 ? BX1 : BX2);
12768     for (y = BY1; y <= BY2; y++)
12769       DrawScreenField(x, y);
12770   }
12771
12772   if (dy != 0)
12773   {
12774     y = (dy == 1 ? BY1 : BY2);
12775     for (x = BX1; x <= BX2; x++)
12776       DrawScreenField(x, y);
12777   }
12778
12779   redraw_mask |= REDRAW_FIELD;
12780 }
12781
12782 static boolean canFallDown(struct PlayerInfo *player)
12783 {
12784   int jx = player->jx, jy = player->jy;
12785
12786   return (IN_LEV_FIELD(jx, jy + 1) &&
12787           (IS_FREE(jx, jy + 1) ||
12788            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12789           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12790           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12791 }
12792
12793 static boolean canPassField(int x, int y, int move_dir)
12794 {
12795   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12796   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12797   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12798   int nextx = x + dx;
12799   int nexty = y + dy;
12800   int element = Tile[x][y];
12801
12802   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12803           !CAN_MOVE(element) &&
12804           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12805           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12806           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12807 }
12808
12809 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12810 {
12811   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12812   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12813   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12814   int newx = x + dx;
12815   int newy = y + dy;
12816
12817   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12818           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12819           (IS_DIGGABLE(Tile[newx][newy]) ||
12820            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12821            canPassField(newx, newy, move_dir)));
12822 }
12823
12824 static void CheckGravityMovement(struct PlayerInfo *player)
12825 {
12826   if (player->gravity && !player->programmed_action)
12827   {
12828     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12829     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12830     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12831     int jx = player->jx, jy = player->jy;
12832     boolean player_is_moving_to_valid_field =
12833       (!player_is_snapping &&
12834        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12835         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12836     boolean player_can_fall_down = canFallDown(player);
12837
12838     if (player_can_fall_down &&
12839         !player_is_moving_to_valid_field)
12840       player->programmed_action = MV_DOWN;
12841   }
12842 }
12843
12844 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12845 {
12846   return CheckGravityMovement(player);
12847
12848   if (player->gravity && !player->programmed_action)
12849   {
12850     int jx = player->jx, jy = player->jy;
12851     boolean field_under_player_is_free =
12852       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12853     boolean player_is_standing_on_valid_field =
12854       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12855        (IS_WALKABLE(Tile[jx][jy]) &&
12856         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12857
12858     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12859       player->programmed_action = MV_DOWN;
12860   }
12861 }
12862
12863 /*
12864   MovePlayerOneStep()
12865   -----------------------------------------------------------------------------
12866   dx, dy:               direction (non-diagonal) to try to move the player to
12867   real_dx, real_dy:     direction as read from input device (can be diagonal)
12868 */
12869
12870 boolean MovePlayerOneStep(struct PlayerInfo *player,
12871                           int dx, int dy, int real_dx, int real_dy)
12872 {
12873   int jx = player->jx, jy = player->jy;
12874   int new_jx = jx + dx, new_jy = jy + dy;
12875   int can_move;
12876   boolean player_can_move = !player->cannot_move;
12877
12878   if (!player->active || (!dx && !dy))
12879     return MP_NO_ACTION;
12880
12881   player->MovDir = (dx < 0 ? MV_LEFT :
12882                     dx > 0 ? MV_RIGHT :
12883                     dy < 0 ? MV_UP :
12884                     dy > 0 ? MV_DOWN :  MV_NONE);
12885
12886   if (!IN_LEV_FIELD(new_jx, new_jy))
12887     return MP_NO_ACTION;
12888
12889   if (!player_can_move)
12890   {
12891     if (player->MovPos == 0)
12892     {
12893       player->is_moving = FALSE;
12894       player->is_digging = FALSE;
12895       player->is_collecting = FALSE;
12896       player->is_snapping = FALSE;
12897       player->is_pushing = FALSE;
12898     }
12899   }
12900
12901   if (!network.enabled && game.centered_player_nr == -1 &&
12902       !AllPlayersInSight(player, new_jx, new_jy))
12903     return MP_NO_ACTION;
12904
12905   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12906   if (can_move != MP_MOVING)
12907     return can_move;
12908
12909   // check if DigField() has caused relocation of the player
12910   if (player->jx != jx || player->jy != jy)
12911     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12912
12913   StorePlayer[jx][jy] = 0;
12914   player->last_jx = jx;
12915   player->last_jy = jy;
12916   player->jx = new_jx;
12917   player->jy = new_jy;
12918   StorePlayer[new_jx][new_jy] = player->element_nr;
12919
12920   if (player->move_delay_value_next != -1)
12921   {
12922     player->move_delay_value = player->move_delay_value_next;
12923     player->move_delay_value_next = -1;
12924   }
12925
12926   player->MovPos =
12927     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12928
12929   player->step_counter++;
12930
12931   PlayerVisit[jx][jy] = FrameCounter;
12932
12933   player->is_moving = TRUE;
12934
12935 #if 1
12936   // should better be called in MovePlayer(), but this breaks some tapes
12937   ScrollPlayer(player, SCROLL_INIT);
12938 #endif
12939
12940   return MP_MOVING;
12941 }
12942
12943 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12944 {
12945   int jx = player->jx, jy = player->jy;
12946   int old_jx = jx, old_jy = jy;
12947   int moved = MP_NO_ACTION;
12948
12949   if (!player->active)
12950     return FALSE;
12951
12952   if (!dx && !dy)
12953   {
12954     if (player->MovPos == 0)
12955     {
12956       player->is_moving = FALSE;
12957       player->is_digging = FALSE;
12958       player->is_collecting = FALSE;
12959       player->is_snapping = FALSE;
12960       player->is_pushing = FALSE;
12961     }
12962
12963     return FALSE;
12964   }
12965
12966   if (player->move_delay > 0)
12967     return FALSE;
12968
12969   player->move_delay = -1;              // set to "uninitialized" value
12970
12971   // store if player is automatically moved to next field
12972   player->is_auto_moving = (player->programmed_action != MV_NONE);
12973
12974   // remove the last programmed player action
12975   player->programmed_action = 0;
12976
12977   if (player->MovPos)
12978   {
12979     // should only happen if pre-1.2 tape recordings are played
12980     // this is only for backward compatibility
12981
12982     int original_move_delay_value = player->move_delay_value;
12983
12984 #if DEBUG
12985     Debug("game:playing:MovePlayer",
12986           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12987           tape.counter);
12988 #endif
12989
12990     // scroll remaining steps with finest movement resolution
12991     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12992
12993     while (player->MovPos)
12994     {
12995       ScrollPlayer(player, SCROLL_GO_ON);
12996       ScrollScreen(NULL, SCROLL_GO_ON);
12997
12998       AdvanceFrameAndPlayerCounters(player->index_nr);
12999
13000       DrawAllPlayers();
13001       BackToFront_WithFrameDelay(0);
13002     }
13003
13004     player->move_delay_value = original_move_delay_value;
13005   }
13006
13007   player->is_active = FALSE;
13008
13009   if (player->last_move_dir & MV_HORIZONTAL)
13010   {
13011     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13012       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13013   }
13014   else
13015   {
13016     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13017       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13018   }
13019
13020   if (!moved && !player->is_active)
13021   {
13022     player->is_moving = FALSE;
13023     player->is_digging = FALSE;
13024     player->is_collecting = FALSE;
13025     player->is_snapping = FALSE;
13026     player->is_pushing = FALSE;
13027   }
13028
13029   jx = player->jx;
13030   jy = player->jy;
13031
13032   if (moved & MP_MOVING && !ScreenMovPos &&
13033       (player->index_nr == game.centered_player_nr ||
13034        game.centered_player_nr == -1))
13035   {
13036     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13037
13038     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13039     {
13040       // actual player has left the screen -- scroll in that direction
13041       if (jx != old_jx)         // player has moved horizontally
13042         scroll_x += (jx - old_jx);
13043       else                      // player has moved vertically
13044         scroll_y += (jy - old_jy);
13045     }
13046     else
13047     {
13048       int offset_raw = game.scroll_delay_value;
13049
13050       if (jx != old_jx)         // player has moved horizontally
13051       {
13052         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13053         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13054         int new_scroll_x = jx - MIDPOSX + offset_x;
13055
13056         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13057             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13058           scroll_x = new_scroll_x;
13059
13060         // don't scroll over playfield boundaries
13061         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13062
13063         // don't scroll more than one field at a time
13064         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13065
13066         // don't scroll against the player's moving direction
13067         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13068             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13069           scroll_x = old_scroll_x;
13070       }
13071       else                      // player has moved vertically
13072       {
13073         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13074         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13075         int new_scroll_y = jy - MIDPOSY + offset_y;
13076
13077         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13078             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13079           scroll_y = new_scroll_y;
13080
13081         // don't scroll over playfield boundaries
13082         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13083
13084         // don't scroll more than one field at a time
13085         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13086
13087         // don't scroll against the player's moving direction
13088         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13089             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13090           scroll_y = old_scroll_y;
13091       }
13092     }
13093
13094     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13095     {
13096       if (!network.enabled && game.centered_player_nr == -1 &&
13097           !AllPlayersInVisibleScreen())
13098       {
13099         scroll_x = old_scroll_x;
13100         scroll_y = old_scroll_y;
13101       }
13102       else
13103       {
13104         ScrollScreen(player, SCROLL_INIT);
13105         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13106       }
13107     }
13108   }
13109
13110   player->StepFrame = 0;
13111
13112   if (moved & MP_MOVING)
13113   {
13114     if (old_jx != jx && old_jy == jy)
13115       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13116     else if (old_jx == jx && old_jy != jy)
13117       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13118
13119     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13120
13121     player->last_move_dir = player->MovDir;
13122     player->is_moving = TRUE;
13123     player->is_snapping = FALSE;
13124     player->is_switching = FALSE;
13125     player->is_dropping = FALSE;
13126     player->is_dropping_pressed = FALSE;
13127     player->drop_pressed_delay = 0;
13128
13129 #if 0
13130     // should better be called here than above, but this breaks some tapes
13131     ScrollPlayer(player, SCROLL_INIT);
13132 #endif
13133   }
13134   else
13135   {
13136     CheckGravityMovementWhenNotMoving(player);
13137
13138     player->is_moving = FALSE;
13139
13140     /* at this point, the player is allowed to move, but cannot move right now
13141        (e.g. because of something blocking the way) -- ensure that the player
13142        is also allowed to move in the next frame (in old versions before 3.1.1,
13143        the player was forced to wait again for eight frames before next try) */
13144
13145     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13146       player->move_delay = 0;   // allow direct movement in the next frame
13147   }
13148
13149   if (player->move_delay == -1)         // not yet initialized by DigField()
13150     player->move_delay = player->move_delay_value;
13151
13152   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13153   {
13154     TestIfPlayerTouchesBadThing(jx, jy);
13155     TestIfPlayerTouchesCustomElement(jx, jy);
13156   }
13157
13158   if (!player->active)
13159     RemovePlayer(player);
13160
13161   return moved;
13162 }
13163
13164 void ScrollPlayer(struct PlayerInfo *player, int mode)
13165 {
13166   int jx = player->jx, jy = player->jy;
13167   int last_jx = player->last_jx, last_jy = player->last_jy;
13168   int move_stepsize = TILEX / player->move_delay_value;
13169
13170   if (!player->active)
13171     return;
13172
13173   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13174     return;
13175
13176   if (mode == SCROLL_INIT)
13177   {
13178     player->actual_frame_counter = FrameCounter;
13179     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13180
13181     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13182         Tile[last_jx][last_jy] == EL_EMPTY)
13183     {
13184       int last_field_block_delay = 0;   // start with no blocking at all
13185       int block_delay_adjustment = player->block_delay_adjustment;
13186
13187       // if player blocks last field, add delay for exactly one move
13188       if (player->block_last_field)
13189       {
13190         last_field_block_delay += player->move_delay_value;
13191
13192         // when blocking enabled, prevent moving up despite gravity
13193         if (player->gravity && player->MovDir == MV_UP)
13194           block_delay_adjustment = -1;
13195       }
13196
13197       // add block delay adjustment (also possible when not blocking)
13198       last_field_block_delay += block_delay_adjustment;
13199
13200       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13201       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13202     }
13203
13204     if (player->MovPos != 0)    // player has not yet reached destination
13205       return;
13206   }
13207   else if (!FrameReached(&player->actual_frame_counter, 1))
13208     return;
13209
13210   if (player->MovPos != 0)
13211   {
13212     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13213     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13214
13215     // before DrawPlayer() to draw correct player graphic for this case
13216     if (player->MovPos == 0)
13217       CheckGravityMovement(player);
13218   }
13219
13220   if (player->MovPos == 0)      // player reached destination field
13221   {
13222     if (player->move_delay_reset_counter > 0)
13223     {
13224       player->move_delay_reset_counter--;
13225
13226       if (player->move_delay_reset_counter == 0)
13227       {
13228         // continue with normal speed after quickly moving through gate
13229         HALVE_PLAYER_SPEED(player);
13230
13231         // be able to make the next move without delay
13232         player->move_delay = 0;
13233       }
13234     }
13235
13236     player->last_jx = jx;
13237     player->last_jy = jy;
13238
13239     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13240         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13241         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13242         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13243         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13244         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13245         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13246         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13247     {
13248       ExitPlayer(player);
13249
13250       if (game.players_still_needed == 0 &&
13251           (game.friends_still_needed == 0 ||
13252            IS_SP_ELEMENT(Tile[jx][jy])))
13253         LevelSolved();
13254     }
13255
13256     // this breaks one level: "machine", level 000
13257     {
13258       int move_direction = player->MovDir;
13259       int enter_side = MV_DIR_OPPOSITE(move_direction);
13260       int leave_side = move_direction;
13261       int old_jx = last_jx;
13262       int old_jy = last_jy;
13263       int old_element = Tile[old_jx][old_jy];
13264       int new_element = Tile[jx][jy];
13265
13266       if (IS_CUSTOM_ELEMENT(old_element))
13267         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13268                                    CE_LEFT_BY_PLAYER,
13269                                    player->index_bit, leave_side);
13270
13271       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13272                                           CE_PLAYER_LEAVES_X,
13273                                           player->index_bit, leave_side);
13274
13275       if (IS_CUSTOM_ELEMENT(new_element))
13276         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13277                                    player->index_bit, enter_side);
13278
13279       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13280                                           CE_PLAYER_ENTERS_X,
13281                                           player->index_bit, enter_side);
13282
13283       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13284                                         CE_MOVE_OF_X, move_direction);
13285     }
13286
13287     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13288     {
13289       TestIfPlayerTouchesBadThing(jx, jy);
13290       TestIfPlayerTouchesCustomElement(jx, jy);
13291
13292       /* needed because pushed element has not yet reached its destination,
13293          so it would trigger a change event at its previous field location */
13294       if (!player->is_pushing)
13295         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13296
13297       if (level.finish_dig_collect &&
13298           (player->is_digging || player->is_collecting))
13299       {
13300         int last_element = player->last_removed_element;
13301         int move_direction = player->MovDir;
13302         int enter_side = MV_DIR_OPPOSITE(move_direction);
13303         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13304                             CE_PLAYER_COLLECTS_X);
13305
13306         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13307                                             player->index_bit, enter_side);
13308
13309         player->last_removed_element = EL_UNDEFINED;
13310       }
13311
13312       if (!player->active)
13313         RemovePlayer(player);
13314     }
13315
13316     if (level.use_step_counter)
13317       CheckLevelTime_StepCounter();
13318
13319     if (tape.single_step && tape.recording && !tape.pausing &&
13320         !player->programmed_action)
13321       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13322
13323     if (!player->programmed_action)
13324       CheckSaveEngineSnapshot(player);
13325   }
13326 }
13327
13328 void ScrollScreen(struct PlayerInfo *player, int mode)
13329 {
13330   static unsigned int screen_frame_counter = 0;
13331
13332   if (mode == SCROLL_INIT)
13333   {
13334     // set scrolling step size according to actual player's moving speed
13335     ScrollStepSize = TILEX / player->move_delay_value;
13336
13337     screen_frame_counter = FrameCounter;
13338     ScreenMovDir = player->MovDir;
13339     ScreenMovPos = player->MovPos;
13340     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13341     return;
13342   }
13343   else if (!FrameReached(&screen_frame_counter, 1))
13344     return;
13345
13346   if (ScreenMovPos)
13347   {
13348     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13349     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13350     redraw_mask |= REDRAW_FIELD;
13351   }
13352   else
13353     ScreenMovDir = MV_NONE;
13354 }
13355
13356 void CheckNextToConditions(int x, int y)
13357 {
13358   int element = Tile[x][y];
13359
13360   if (IS_PLAYER(x, y))
13361     TestIfPlayerNextToCustomElement(x, y);
13362
13363   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13364       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13365     TestIfElementNextToCustomElement(x, y);
13366 }
13367
13368 void TestIfPlayerNextToCustomElement(int x, int y)
13369 {
13370   static int xy[4][2] =
13371   {
13372     { 0, -1 },
13373     { -1, 0 },
13374     { +1, 0 },
13375     { 0, +1 }
13376   };
13377   static int trigger_sides[4][2] =
13378   {
13379     // center side       border side
13380     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13381     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13382     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13383     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13384   };
13385   int i;
13386
13387   if (!IS_PLAYER(x, y))
13388     return;
13389
13390   struct PlayerInfo *player = PLAYERINFO(x, y);
13391
13392   if (player->is_moving)
13393     return;
13394
13395   for (i = 0; i < NUM_DIRECTIONS; i++)
13396   {
13397     int xx = x + xy[i][0];
13398     int yy = y + xy[i][1];
13399     int border_side = trigger_sides[i][1];
13400     int border_element;
13401
13402     if (!IN_LEV_FIELD(xx, yy))
13403       continue;
13404
13405     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13406       continue;         // center and border element not connected
13407
13408     border_element = Tile[xx][yy];
13409
13410     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13411                                player->index_bit, border_side);
13412     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13413                                         CE_PLAYER_NEXT_TO_X,
13414                                         player->index_bit, border_side);
13415
13416     /* use player element that is initially defined in the level playfield,
13417        not the player element that corresponds to the runtime player number
13418        (example: a level that contains EL_PLAYER_3 as the only player would
13419        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13420
13421     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13422                              CE_NEXT_TO_X, border_side);
13423   }
13424 }
13425
13426 void TestIfPlayerTouchesCustomElement(int x, int y)
13427 {
13428   static int xy[4][2] =
13429   {
13430     { 0, -1 },
13431     { -1, 0 },
13432     { +1, 0 },
13433     { 0, +1 }
13434   };
13435   static int trigger_sides[4][2] =
13436   {
13437     // center side       border side
13438     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13439     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13440     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13441     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13442   };
13443   static int touch_dir[4] =
13444   {
13445     MV_LEFT | MV_RIGHT,
13446     MV_UP   | MV_DOWN,
13447     MV_UP   | MV_DOWN,
13448     MV_LEFT | MV_RIGHT
13449   };
13450   int center_element = Tile[x][y];      // should always be non-moving!
13451   int i;
13452
13453   for (i = 0; i < NUM_DIRECTIONS; i++)
13454   {
13455     int xx = x + xy[i][0];
13456     int yy = y + xy[i][1];
13457     int center_side = trigger_sides[i][0];
13458     int border_side = trigger_sides[i][1];
13459     int border_element;
13460
13461     if (!IN_LEV_FIELD(xx, yy))
13462       continue;
13463
13464     if (IS_PLAYER(x, y))                // player found at center element
13465     {
13466       struct PlayerInfo *player = PLAYERINFO(x, y);
13467
13468       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13469         border_element = Tile[xx][yy];          // may be moving!
13470       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13471         border_element = Tile[xx][yy];
13472       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13473         border_element = MovingOrBlocked2Element(xx, yy);
13474       else
13475         continue;               // center and border element do not touch
13476
13477       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13478                                  player->index_bit, border_side);
13479       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13480                                           CE_PLAYER_TOUCHES_X,
13481                                           player->index_bit, border_side);
13482
13483       {
13484         /* use player element that is initially defined in the level playfield,
13485            not the player element that corresponds to the runtime player number
13486            (example: a level that contains EL_PLAYER_3 as the only player would
13487            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13488         int player_element = PLAYERINFO(x, y)->initial_element;
13489
13490         CheckElementChangeBySide(xx, yy, border_element, player_element,
13491                                  CE_TOUCHING_X, border_side);
13492       }
13493     }
13494     else if (IS_PLAYER(xx, yy))         // player found at border element
13495     {
13496       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13497
13498       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13499       {
13500         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13501           continue;             // center and border element do not touch
13502       }
13503
13504       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13505                                  player->index_bit, center_side);
13506       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13507                                           CE_PLAYER_TOUCHES_X,
13508                                           player->index_bit, center_side);
13509
13510       {
13511         /* use player element that is initially defined in the level playfield,
13512            not the player element that corresponds to the runtime player number
13513            (example: a level that contains EL_PLAYER_3 as the only player would
13514            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13515         int player_element = PLAYERINFO(xx, yy)->initial_element;
13516
13517         CheckElementChangeBySide(x, y, center_element, player_element,
13518                                  CE_TOUCHING_X, center_side);
13519       }
13520
13521       break;
13522     }
13523   }
13524 }
13525
13526 void TestIfElementNextToCustomElement(int x, int y)
13527 {
13528   static int xy[4][2] =
13529   {
13530     { 0, -1 },
13531     { -1, 0 },
13532     { +1, 0 },
13533     { 0, +1 }
13534   };
13535   static int trigger_sides[4][2] =
13536   {
13537     // center side      border side
13538     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13539     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13540     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13541     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13542   };
13543   int center_element = Tile[x][y];      // should always be non-moving!
13544   int i;
13545
13546   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13547     return;
13548
13549   for (i = 0; i < NUM_DIRECTIONS; i++)
13550   {
13551     int xx = x + xy[i][0];
13552     int yy = y + xy[i][1];
13553     int border_side = trigger_sides[i][1];
13554     int border_element;
13555
13556     if (!IN_LEV_FIELD(xx, yy))
13557       continue;
13558
13559     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13560       continue;                 // center and border element not connected
13561
13562     border_element = Tile[xx][yy];
13563
13564     // check for change of center element (but change it only once)
13565     if (CheckElementChangeBySide(x, y, center_element, border_element,
13566                                  CE_NEXT_TO_X, border_side))
13567       break;
13568   }
13569 }
13570
13571 void TestIfElementTouchesCustomElement(int x, int y)
13572 {
13573   static int xy[4][2] =
13574   {
13575     { 0, -1 },
13576     { -1, 0 },
13577     { +1, 0 },
13578     { 0, +1 }
13579   };
13580   static int trigger_sides[4][2] =
13581   {
13582     // center side      border side
13583     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13584     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13585     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13586     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13587   };
13588   static int touch_dir[4] =
13589   {
13590     MV_LEFT | MV_RIGHT,
13591     MV_UP   | MV_DOWN,
13592     MV_UP   | MV_DOWN,
13593     MV_LEFT | MV_RIGHT
13594   };
13595   boolean change_center_element = FALSE;
13596   int center_element = Tile[x][y];      // should always be non-moving!
13597   int border_element_old[NUM_DIRECTIONS];
13598   int i;
13599
13600   for (i = 0; i < NUM_DIRECTIONS; i++)
13601   {
13602     int xx = x + xy[i][0];
13603     int yy = y + xy[i][1];
13604     int border_element;
13605
13606     border_element_old[i] = -1;
13607
13608     if (!IN_LEV_FIELD(xx, yy))
13609       continue;
13610
13611     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13612       border_element = Tile[xx][yy];    // may be moving!
13613     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13614       border_element = Tile[xx][yy];
13615     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13616       border_element = MovingOrBlocked2Element(xx, yy);
13617     else
13618       continue;                 // center and border element do not touch
13619
13620     border_element_old[i] = border_element;
13621   }
13622
13623   for (i = 0; i < NUM_DIRECTIONS; i++)
13624   {
13625     int xx = x + xy[i][0];
13626     int yy = y + xy[i][1];
13627     int center_side = trigger_sides[i][0];
13628     int border_element = border_element_old[i];
13629
13630     if (border_element == -1)
13631       continue;
13632
13633     // check for change of border element
13634     CheckElementChangeBySide(xx, yy, border_element, center_element,
13635                              CE_TOUCHING_X, center_side);
13636
13637     // (center element cannot be player, so we dont have to check this here)
13638   }
13639
13640   for (i = 0; i < NUM_DIRECTIONS; i++)
13641   {
13642     int xx = x + xy[i][0];
13643     int yy = y + xy[i][1];
13644     int border_side = trigger_sides[i][1];
13645     int border_element = border_element_old[i];
13646
13647     if (border_element == -1)
13648       continue;
13649
13650     // check for change of center element (but change it only once)
13651     if (!change_center_element)
13652       change_center_element =
13653         CheckElementChangeBySide(x, y, center_element, border_element,
13654                                  CE_TOUCHING_X, border_side);
13655
13656     if (IS_PLAYER(xx, yy))
13657     {
13658       /* use player element that is initially defined in the level playfield,
13659          not the player element that corresponds to the runtime player number
13660          (example: a level that contains EL_PLAYER_3 as the only player would
13661          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13662       int player_element = PLAYERINFO(xx, yy)->initial_element;
13663
13664       CheckElementChangeBySide(x, y, center_element, player_element,
13665                                CE_TOUCHING_X, border_side);
13666     }
13667   }
13668 }
13669
13670 void TestIfElementHitsCustomElement(int x, int y, int direction)
13671 {
13672   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13673   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13674   int hitx = x + dx, hity = y + dy;
13675   int hitting_element = Tile[x][y];
13676   int touched_element;
13677
13678   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13679     return;
13680
13681   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13682                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13683
13684   if (IN_LEV_FIELD(hitx, hity))
13685   {
13686     int opposite_direction = MV_DIR_OPPOSITE(direction);
13687     int hitting_side = direction;
13688     int touched_side = opposite_direction;
13689     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13690                           MovDir[hitx][hity] != direction ||
13691                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13692
13693     object_hit = TRUE;
13694
13695     if (object_hit)
13696     {
13697       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13698                                CE_HITTING_X, touched_side);
13699
13700       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13701                                CE_HIT_BY_X, hitting_side);
13702
13703       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13704                                CE_HIT_BY_SOMETHING, opposite_direction);
13705
13706       if (IS_PLAYER(hitx, hity))
13707       {
13708         /* use player element that is initially defined in the level playfield,
13709            not the player element that corresponds to the runtime player number
13710            (example: a level that contains EL_PLAYER_3 as the only player would
13711            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13712         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13713
13714         CheckElementChangeBySide(x, y, hitting_element, player_element,
13715                                  CE_HITTING_X, touched_side);
13716       }
13717     }
13718   }
13719
13720   // "hitting something" is also true when hitting the playfield border
13721   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13722                            CE_HITTING_SOMETHING, direction);
13723 }
13724
13725 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13726 {
13727   int i, kill_x = -1, kill_y = -1;
13728
13729   int bad_element = -1;
13730   static int test_xy[4][2] =
13731   {
13732     { 0, -1 },
13733     { -1, 0 },
13734     { +1, 0 },
13735     { 0, +1 }
13736   };
13737   static int test_dir[4] =
13738   {
13739     MV_UP,
13740     MV_LEFT,
13741     MV_RIGHT,
13742     MV_DOWN
13743   };
13744
13745   for (i = 0; i < NUM_DIRECTIONS; i++)
13746   {
13747     int test_x, test_y, test_move_dir, test_element;
13748
13749     test_x = good_x + test_xy[i][0];
13750     test_y = good_y + test_xy[i][1];
13751
13752     if (!IN_LEV_FIELD(test_x, test_y))
13753       continue;
13754
13755     test_move_dir =
13756       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13757
13758     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13759
13760     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13761        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13762     */
13763     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13764         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13765     {
13766       kill_x = test_x;
13767       kill_y = test_y;
13768       bad_element = test_element;
13769
13770       break;
13771     }
13772   }
13773
13774   if (kill_x != -1 || kill_y != -1)
13775   {
13776     if (IS_PLAYER(good_x, good_y))
13777     {
13778       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13779
13780       if (player->shield_deadly_time_left > 0 &&
13781           !IS_INDESTRUCTIBLE(bad_element))
13782         Bang(kill_x, kill_y);
13783       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13784         KillPlayer(player);
13785     }
13786     else
13787       Bang(good_x, good_y);
13788   }
13789 }
13790
13791 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13792 {
13793   int i, kill_x = -1, kill_y = -1;
13794   int bad_element = Tile[bad_x][bad_y];
13795   static int test_xy[4][2] =
13796   {
13797     { 0, -1 },
13798     { -1, 0 },
13799     { +1, 0 },
13800     { 0, +1 }
13801   };
13802   static int touch_dir[4] =
13803   {
13804     MV_LEFT | MV_RIGHT,
13805     MV_UP   | MV_DOWN,
13806     MV_UP   | MV_DOWN,
13807     MV_LEFT | MV_RIGHT
13808   };
13809   static int test_dir[4] =
13810   {
13811     MV_UP,
13812     MV_LEFT,
13813     MV_RIGHT,
13814     MV_DOWN
13815   };
13816
13817   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13818     return;
13819
13820   for (i = 0; i < NUM_DIRECTIONS; i++)
13821   {
13822     int test_x, test_y, test_move_dir, test_element;
13823
13824     test_x = bad_x + test_xy[i][0];
13825     test_y = bad_y + test_xy[i][1];
13826
13827     if (!IN_LEV_FIELD(test_x, test_y))
13828       continue;
13829
13830     test_move_dir =
13831       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13832
13833     test_element = Tile[test_x][test_y];
13834
13835     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13836        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13837     */
13838     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13839         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13840     {
13841       // good thing is player or penguin that does not move away
13842       if (IS_PLAYER(test_x, test_y))
13843       {
13844         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13845
13846         if (bad_element == EL_ROBOT && player->is_moving)
13847           continue;     // robot does not kill player if he is moving
13848
13849         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13850         {
13851           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13852             continue;           // center and border element do not touch
13853         }
13854
13855         kill_x = test_x;
13856         kill_y = test_y;
13857
13858         break;
13859       }
13860       else if (test_element == EL_PENGUIN)
13861       {
13862         kill_x = test_x;
13863         kill_y = test_y;
13864
13865         break;
13866       }
13867     }
13868   }
13869
13870   if (kill_x != -1 || kill_y != -1)
13871   {
13872     if (IS_PLAYER(kill_x, kill_y))
13873     {
13874       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13875
13876       if (player->shield_deadly_time_left > 0 &&
13877           !IS_INDESTRUCTIBLE(bad_element))
13878         Bang(bad_x, bad_y);
13879       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13880         KillPlayer(player);
13881     }
13882     else
13883       Bang(kill_x, kill_y);
13884   }
13885 }
13886
13887 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13888 {
13889   int bad_element = Tile[bad_x][bad_y];
13890   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13891   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13892   int test_x = bad_x + dx, test_y = bad_y + dy;
13893   int test_move_dir, test_element;
13894   int kill_x = -1, kill_y = -1;
13895
13896   if (!IN_LEV_FIELD(test_x, test_y))
13897     return;
13898
13899   test_move_dir =
13900     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13901
13902   test_element = Tile[test_x][test_y];
13903
13904   if (test_move_dir != bad_move_dir)
13905   {
13906     // good thing can be player or penguin that does not move away
13907     if (IS_PLAYER(test_x, test_y))
13908     {
13909       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13910
13911       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13912          player as being hit when he is moving towards the bad thing, because
13913          the "get hit by" condition would be lost after the player stops) */
13914       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13915         return;         // player moves away from bad thing
13916
13917       kill_x = test_x;
13918       kill_y = test_y;
13919     }
13920     else if (test_element == EL_PENGUIN)
13921     {
13922       kill_x = test_x;
13923       kill_y = test_y;
13924     }
13925   }
13926
13927   if (kill_x != -1 || kill_y != -1)
13928   {
13929     if (IS_PLAYER(kill_x, kill_y))
13930     {
13931       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13932
13933       if (player->shield_deadly_time_left > 0 &&
13934           !IS_INDESTRUCTIBLE(bad_element))
13935         Bang(bad_x, bad_y);
13936       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13937         KillPlayer(player);
13938     }
13939     else
13940       Bang(kill_x, kill_y);
13941   }
13942 }
13943
13944 void TestIfPlayerTouchesBadThing(int x, int y)
13945 {
13946   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13947 }
13948
13949 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13950 {
13951   TestIfGoodThingHitsBadThing(x, y, move_dir);
13952 }
13953
13954 void TestIfBadThingTouchesPlayer(int x, int y)
13955 {
13956   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13957 }
13958
13959 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13960 {
13961   TestIfBadThingHitsGoodThing(x, y, move_dir);
13962 }
13963
13964 void TestIfFriendTouchesBadThing(int x, int y)
13965 {
13966   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13967 }
13968
13969 void TestIfBadThingTouchesFriend(int x, int y)
13970 {
13971   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13972 }
13973
13974 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13975 {
13976   int i, kill_x = bad_x, kill_y = bad_y;
13977   static int xy[4][2] =
13978   {
13979     { 0, -1 },
13980     { -1, 0 },
13981     { +1, 0 },
13982     { 0, +1 }
13983   };
13984
13985   for (i = 0; i < NUM_DIRECTIONS; i++)
13986   {
13987     int x, y, element;
13988
13989     x = bad_x + xy[i][0];
13990     y = bad_y + xy[i][1];
13991     if (!IN_LEV_FIELD(x, y))
13992       continue;
13993
13994     element = Tile[x][y];
13995     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13996         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13997     {
13998       kill_x = x;
13999       kill_y = y;
14000       break;
14001     }
14002   }
14003
14004   if (kill_x != bad_x || kill_y != bad_y)
14005     Bang(bad_x, bad_y);
14006 }
14007
14008 void KillPlayer(struct PlayerInfo *player)
14009 {
14010   int jx = player->jx, jy = player->jy;
14011
14012   if (!player->active)
14013     return;
14014
14015 #if 0
14016   Debug("game:playing:KillPlayer",
14017         "0: killed == %d, active == %d, reanimated == %d",
14018         player->killed, player->active, player->reanimated);
14019 #endif
14020
14021   /* the following code was introduced to prevent an infinite loop when calling
14022      -> Bang()
14023      -> CheckTriggeredElementChangeExt()
14024      -> ExecuteCustomElementAction()
14025      -> KillPlayer()
14026      -> (infinitely repeating the above sequence of function calls)
14027      which occurs when killing the player while having a CE with the setting
14028      "kill player X when explosion of <player X>"; the solution using a new
14029      field "player->killed" was chosen for backwards compatibility, although
14030      clever use of the fields "player->active" etc. would probably also work */
14031 #if 1
14032   if (player->killed)
14033     return;
14034 #endif
14035
14036   player->killed = TRUE;
14037
14038   // remove accessible field at the player's position
14039   Tile[jx][jy] = EL_EMPTY;
14040
14041   // deactivate shield (else Bang()/Explode() would not work right)
14042   player->shield_normal_time_left = 0;
14043   player->shield_deadly_time_left = 0;
14044
14045 #if 0
14046   Debug("game:playing:KillPlayer",
14047         "1: killed == %d, active == %d, reanimated == %d",
14048         player->killed, player->active, player->reanimated);
14049 #endif
14050
14051   Bang(jx, jy);
14052
14053 #if 0
14054   Debug("game:playing:KillPlayer",
14055         "2: killed == %d, active == %d, reanimated == %d",
14056         player->killed, player->active, player->reanimated);
14057 #endif
14058
14059   if (player->reanimated)       // killed player may have been reanimated
14060     player->killed = player->reanimated = FALSE;
14061   else
14062     BuryPlayer(player);
14063 }
14064
14065 static void KillPlayerUnlessEnemyProtected(int x, int y)
14066 {
14067   if (!PLAYER_ENEMY_PROTECTED(x, y))
14068     KillPlayer(PLAYERINFO(x, y));
14069 }
14070
14071 static void KillPlayerUnlessExplosionProtected(int x, int y)
14072 {
14073   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14074     KillPlayer(PLAYERINFO(x, y));
14075 }
14076
14077 void BuryPlayer(struct PlayerInfo *player)
14078 {
14079   int jx = player->jx, jy = player->jy;
14080
14081   if (!player->active)
14082     return;
14083
14084   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14085   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14086
14087   RemovePlayer(player);
14088
14089   player->buried = TRUE;
14090
14091   if (game.all_players_gone)
14092     game.GameOver = TRUE;
14093 }
14094
14095 void RemovePlayer(struct PlayerInfo *player)
14096 {
14097   int jx = player->jx, jy = player->jy;
14098   int i, found = FALSE;
14099
14100   player->present = FALSE;
14101   player->active = FALSE;
14102
14103   // required for some CE actions (even if the player is not active anymore)
14104   player->MovPos = 0;
14105
14106   if (!ExplodeField[jx][jy])
14107     StorePlayer[jx][jy] = 0;
14108
14109   if (player->is_moving)
14110     TEST_DrawLevelField(player->last_jx, player->last_jy);
14111
14112   for (i = 0; i < MAX_PLAYERS; i++)
14113     if (stored_player[i].active)
14114       found = TRUE;
14115
14116   if (!found)
14117   {
14118     game.all_players_gone = TRUE;
14119     game.GameOver = TRUE;
14120   }
14121
14122   game.exit_x = game.robot_wheel_x = jx;
14123   game.exit_y = game.robot_wheel_y = jy;
14124 }
14125
14126 void ExitPlayer(struct PlayerInfo *player)
14127 {
14128   DrawPlayer(player);   // needed here only to cleanup last field
14129   RemovePlayer(player);
14130
14131   if (game.players_still_needed > 0)
14132     game.players_still_needed--;
14133 }
14134
14135 static void SetFieldForSnapping(int x, int y, int element, int direction,
14136                                 int player_index_bit)
14137 {
14138   struct ElementInfo *ei = &element_info[element];
14139   int direction_bit = MV_DIR_TO_BIT(direction);
14140   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14141   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14142                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14143
14144   Tile[x][y] = EL_ELEMENT_SNAPPING;
14145   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14146   MovDir[x][y] = direction;
14147   Store[x][y] = element;
14148   Store2[x][y] = player_index_bit;
14149
14150   ResetGfxAnimation(x, y);
14151
14152   GfxElement[x][y] = element;
14153   GfxAction[x][y] = action;
14154   GfxDir[x][y] = direction;
14155   GfxFrame[x][y] = -1;
14156 }
14157
14158 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14159                                    int player_index_bit)
14160 {
14161   TestIfElementTouchesCustomElement(x, y);      // for empty space
14162
14163   if (level.finish_dig_collect)
14164   {
14165     int dig_side = MV_DIR_OPPOSITE(direction);
14166     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14167                         CE_PLAYER_COLLECTS_X);
14168
14169     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14170                                         player_index_bit, dig_side);
14171     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14172                                         player_index_bit, dig_side);
14173   }
14174 }
14175
14176 /*
14177   =============================================================================
14178   checkDiagonalPushing()
14179   -----------------------------------------------------------------------------
14180   check if diagonal input device direction results in pushing of object
14181   (by checking if the alternative direction is walkable, diggable, ...)
14182   =============================================================================
14183 */
14184
14185 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14186                                     int x, int y, int real_dx, int real_dy)
14187 {
14188   int jx, jy, dx, dy, xx, yy;
14189
14190   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14191     return TRUE;
14192
14193   // diagonal direction: check alternative direction
14194   jx = player->jx;
14195   jy = player->jy;
14196   dx = x - jx;
14197   dy = y - jy;
14198   xx = jx + (dx == 0 ? real_dx : 0);
14199   yy = jy + (dy == 0 ? real_dy : 0);
14200
14201   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14202 }
14203
14204 /*
14205   =============================================================================
14206   DigField()
14207   -----------------------------------------------------------------------------
14208   x, y:                 field next to player (non-diagonal) to try to dig to
14209   real_dx, real_dy:     direction as read from input device (can be diagonal)
14210   =============================================================================
14211 */
14212
14213 static int DigField(struct PlayerInfo *player,
14214                     int oldx, int oldy, int x, int y,
14215                     int real_dx, int real_dy, int mode)
14216 {
14217   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14218   boolean player_was_pushing = player->is_pushing;
14219   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14220   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14221   int jx = oldx, jy = oldy;
14222   int dx = x - jx, dy = y - jy;
14223   int nextx = x + dx, nexty = y + dy;
14224   int move_direction = (dx == -1 ? MV_LEFT  :
14225                         dx == +1 ? MV_RIGHT :
14226                         dy == -1 ? MV_UP    :
14227                         dy == +1 ? MV_DOWN  : MV_NONE);
14228   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14229   int dig_side = MV_DIR_OPPOSITE(move_direction);
14230   int old_element = Tile[jx][jy];
14231   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14232   int collect_count;
14233
14234   if (is_player)                // function can also be called by EL_PENGUIN
14235   {
14236     if (player->MovPos == 0)
14237     {
14238       player->is_digging = FALSE;
14239       player->is_collecting = FALSE;
14240     }
14241
14242     if (player->MovPos == 0)    // last pushing move finished
14243       player->is_pushing = FALSE;
14244
14245     if (mode == DF_NO_PUSH)     // player just stopped pushing
14246     {
14247       player->is_switching = FALSE;
14248       player->push_delay = -1;
14249
14250       return MP_NO_ACTION;
14251     }
14252   }
14253   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14254     old_element = Back[jx][jy];
14255
14256   // in case of element dropped at player position, check background
14257   else if (Back[jx][jy] != EL_EMPTY &&
14258            game.engine_version >= VERSION_IDENT(2,2,0,0))
14259     old_element = Back[jx][jy];
14260
14261   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14262     return MP_NO_ACTION;        // field has no opening in this direction
14263
14264   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14265     return MP_NO_ACTION;        // field has no opening in this direction
14266
14267   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14268   {
14269     SplashAcid(x, y);
14270
14271     Tile[jx][jy] = player->artwork_element;
14272     InitMovingField(jx, jy, MV_DOWN);
14273     Store[jx][jy] = EL_ACID;
14274     ContinueMoving(jx, jy);
14275     BuryPlayer(player);
14276
14277     return MP_DONT_RUN_INTO;
14278   }
14279
14280   if (player_can_move && DONT_RUN_INTO(element))
14281   {
14282     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14283
14284     return MP_DONT_RUN_INTO;
14285   }
14286
14287   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14288     return MP_NO_ACTION;
14289
14290   collect_count = element_info[element].collect_count_initial;
14291
14292   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14293     return MP_NO_ACTION;
14294
14295   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14296     player_can_move = player_can_move_or_snap;
14297
14298   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14299       game.engine_version >= VERSION_IDENT(2,2,0,0))
14300   {
14301     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14302                                player->index_bit, dig_side);
14303     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14304                                         player->index_bit, dig_side);
14305
14306     if (element == EL_DC_LANDMINE)
14307       Bang(x, y);
14308
14309     if (Tile[x][y] != element)          // field changed by snapping
14310       return MP_ACTION;
14311
14312     return MP_NO_ACTION;
14313   }
14314
14315   if (player->gravity && is_player && !player->is_auto_moving &&
14316       canFallDown(player) && move_direction != MV_DOWN &&
14317       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14318     return MP_NO_ACTION;        // player cannot walk here due to gravity
14319
14320   if (player_can_move &&
14321       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14322   {
14323     int sound_element = SND_ELEMENT(element);
14324     int sound_action = ACTION_WALKING;
14325
14326     if (IS_RND_GATE(element))
14327     {
14328       if (!player->key[RND_GATE_NR(element)])
14329         return MP_NO_ACTION;
14330     }
14331     else if (IS_RND_GATE_GRAY(element))
14332     {
14333       if (!player->key[RND_GATE_GRAY_NR(element)])
14334         return MP_NO_ACTION;
14335     }
14336     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14337     {
14338       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (element == EL_EXIT_OPEN ||
14342              element == EL_EM_EXIT_OPEN ||
14343              element == EL_EM_EXIT_OPENING ||
14344              element == EL_STEEL_EXIT_OPEN ||
14345              element == EL_EM_STEEL_EXIT_OPEN ||
14346              element == EL_EM_STEEL_EXIT_OPENING ||
14347              element == EL_SP_EXIT_OPEN ||
14348              element == EL_SP_EXIT_OPENING)
14349     {
14350       sound_action = ACTION_PASSING;    // player is passing exit
14351     }
14352     else if (element == EL_EMPTY)
14353     {
14354       sound_action = ACTION_MOVING;             // nothing to walk on
14355     }
14356
14357     // play sound from background or player, whatever is available
14358     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14359       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14360     else
14361       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14362   }
14363   else if (player_can_move &&
14364            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14365   {
14366     if (!ACCESS_FROM(element, opposite_direction))
14367       return MP_NO_ACTION;      // field not accessible from this direction
14368
14369     if (CAN_MOVE(element))      // only fixed elements can be passed!
14370       return MP_NO_ACTION;
14371
14372     if (IS_EM_GATE(element))
14373     {
14374       if (!player->key[EM_GATE_NR(element)])
14375         return MP_NO_ACTION;
14376     }
14377     else if (IS_EM_GATE_GRAY(element))
14378     {
14379       if (!player->key[EM_GATE_GRAY_NR(element)])
14380         return MP_NO_ACTION;
14381     }
14382     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14383     {
14384       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14385         return MP_NO_ACTION;
14386     }
14387     else if (IS_EMC_GATE(element))
14388     {
14389       if (!player->key[EMC_GATE_NR(element)])
14390         return MP_NO_ACTION;
14391     }
14392     else if (IS_EMC_GATE_GRAY(element))
14393     {
14394       if (!player->key[EMC_GATE_GRAY_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14398     {
14399       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (element == EL_DC_GATE_WHITE ||
14403              element == EL_DC_GATE_WHITE_GRAY ||
14404              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14405     {
14406       if (player->num_white_keys == 0)
14407         return MP_NO_ACTION;
14408
14409       player->num_white_keys--;
14410     }
14411     else if (IS_SP_PORT(element))
14412     {
14413       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14414           element == EL_SP_GRAVITY_PORT_RIGHT ||
14415           element == EL_SP_GRAVITY_PORT_UP ||
14416           element == EL_SP_GRAVITY_PORT_DOWN)
14417         player->gravity = !player->gravity;
14418       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14419                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14420                element == EL_SP_GRAVITY_ON_PORT_UP ||
14421                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14422         player->gravity = TRUE;
14423       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14424                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14425                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14426                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14427         player->gravity = FALSE;
14428     }
14429
14430     // automatically move to the next field with double speed
14431     player->programmed_action = move_direction;
14432
14433     if (player->move_delay_reset_counter == 0)
14434     {
14435       player->move_delay_reset_counter = 2;     // two double speed steps
14436
14437       DOUBLE_PLAYER_SPEED(player);
14438     }
14439
14440     PlayLevelSoundAction(x, y, ACTION_PASSING);
14441   }
14442   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14443   {
14444     RemoveField(x, y);
14445
14446     if (mode != DF_SNAP)
14447     {
14448       GfxElement[x][y] = GFX_ELEMENT(element);
14449       player->is_digging = TRUE;
14450     }
14451
14452     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14453
14454     // use old behaviour for old levels (digging)
14455     if (!level.finish_dig_collect)
14456     {
14457       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14458                                           player->index_bit, dig_side);
14459
14460       // if digging triggered player relocation, finish digging tile
14461       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14462         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14463     }
14464
14465     if (mode == DF_SNAP)
14466     {
14467       if (level.block_snap_field)
14468         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14469       else
14470         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14471
14472       // use old behaviour for old levels (snapping)
14473       if (!level.finish_dig_collect)
14474         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14475                                             player->index_bit, dig_side);
14476     }
14477   }
14478   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14479   {
14480     RemoveField(x, y);
14481
14482     if (is_player && mode != DF_SNAP)
14483     {
14484       GfxElement[x][y] = element;
14485       player->is_collecting = TRUE;
14486     }
14487
14488     if (element == EL_SPEED_PILL)
14489     {
14490       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14491     }
14492     else if (element == EL_EXTRA_TIME && level.time > 0)
14493     {
14494       TimeLeft += level.extra_time;
14495
14496       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14497
14498       DisplayGameControlValues();
14499     }
14500     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14501     {
14502       player->shield_normal_time_left += level.shield_normal_time;
14503       if (element == EL_SHIELD_DEADLY)
14504         player->shield_deadly_time_left += level.shield_deadly_time;
14505     }
14506     else if (element == EL_DYNAMITE ||
14507              element == EL_EM_DYNAMITE ||
14508              element == EL_SP_DISK_RED)
14509     {
14510       if (player->inventory_size < MAX_INVENTORY_SIZE)
14511         player->inventory_element[player->inventory_size++] = element;
14512
14513       DrawGameDoorValues();
14514     }
14515     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14516     {
14517       player->dynabomb_count++;
14518       player->dynabombs_left++;
14519     }
14520     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14521     {
14522       player->dynabomb_size++;
14523     }
14524     else if (element == EL_DYNABOMB_INCREASE_POWER)
14525     {
14526       player->dynabomb_xl = TRUE;
14527     }
14528     else if (IS_KEY(element))
14529     {
14530       player->key[KEY_NR(element)] = TRUE;
14531
14532       DrawGameDoorValues();
14533     }
14534     else if (element == EL_DC_KEY_WHITE)
14535     {
14536       player->num_white_keys++;
14537
14538       // display white keys?
14539       // DrawGameDoorValues();
14540     }
14541     else if (IS_ENVELOPE(element))
14542     {
14543       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14544
14545       if (!wait_for_snapping)
14546         player->show_envelope = element;
14547     }
14548     else if (element == EL_EMC_LENSES)
14549     {
14550       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14551
14552       RedrawAllInvisibleElementsForLenses();
14553     }
14554     else if (element == EL_EMC_MAGNIFIER)
14555     {
14556       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14557
14558       RedrawAllInvisibleElementsForMagnifier();
14559     }
14560     else if (IS_DROPPABLE(element) ||
14561              IS_THROWABLE(element))     // can be collected and dropped
14562     {
14563       int i;
14564
14565       if (collect_count == 0)
14566         player->inventory_infinite_element = element;
14567       else
14568         for (i = 0; i < collect_count; i++)
14569           if (player->inventory_size < MAX_INVENTORY_SIZE)
14570             player->inventory_element[player->inventory_size++] = element;
14571
14572       DrawGameDoorValues();
14573     }
14574     else if (collect_count > 0)
14575     {
14576       game.gems_still_needed -= collect_count;
14577       if (game.gems_still_needed < 0)
14578         game.gems_still_needed = 0;
14579
14580       game.snapshot.collected_item = TRUE;
14581
14582       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14583
14584       DisplayGameControlValues();
14585     }
14586
14587     RaiseScoreElement(element);
14588     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14589
14590     // use old behaviour for old levels (collecting)
14591     if (!level.finish_dig_collect && is_player)
14592     {
14593       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14594                                           player->index_bit, dig_side);
14595
14596       // if collecting triggered player relocation, finish collecting tile
14597       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14598         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14599     }
14600
14601     if (mode == DF_SNAP)
14602     {
14603       if (level.block_snap_field)
14604         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14605       else
14606         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14607
14608       // use old behaviour for old levels (snapping)
14609       if (!level.finish_dig_collect)
14610         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14611                                             player->index_bit, dig_side);
14612     }
14613   }
14614   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14615   {
14616     if (mode == DF_SNAP && element != EL_BD_ROCK)
14617       return MP_NO_ACTION;
14618
14619     if (CAN_FALL(element) && dy)
14620       return MP_NO_ACTION;
14621
14622     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14623         !(element == EL_SPRING && level.use_spring_bug))
14624       return MP_NO_ACTION;
14625
14626     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14627         ((move_direction & MV_VERTICAL &&
14628           ((element_info[element].move_pattern & MV_LEFT &&
14629             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14630            (element_info[element].move_pattern & MV_RIGHT &&
14631             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14632          (move_direction & MV_HORIZONTAL &&
14633           ((element_info[element].move_pattern & MV_UP &&
14634             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14635            (element_info[element].move_pattern & MV_DOWN &&
14636             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14637       return MP_NO_ACTION;
14638
14639     // do not push elements already moving away faster than player
14640     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14641         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14642       return MP_NO_ACTION;
14643
14644     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14645     {
14646       if (player->push_delay_value == -1 || !player_was_pushing)
14647         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14648     }
14649     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14650     {
14651       if (player->push_delay_value == -1)
14652         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14653     }
14654     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14655     {
14656       if (!player->is_pushing)
14657         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14658     }
14659
14660     player->is_pushing = TRUE;
14661     player->is_active = TRUE;
14662
14663     if (!(IN_LEV_FIELD(nextx, nexty) &&
14664           (IS_FREE(nextx, nexty) ||
14665            (IS_SB_ELEMENT(element) &&
14666             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14667            (IS_CUSTOM_ELEMENT(element) &&
14668             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14669       return MP_NO_ACTION;
14670
14671     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14672       return MP_NO_ACTION;
14673
14674     if (player->push_delay == -1)       // new pushing; restart delay
14675       player->push_delay = 0;
14676
14677     if (player->push_delay < player->push_delay_value &&
14678         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14679         element != EL_SPRING && element != EL_BALLOON)
14680     {
14681       // make sure that there is no move delay before next try to push
14682       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14683         player->move_delay = 0;
14684
14685       return MP_NO_ACTION;
14686     }
14687
14688     if (IS_CUSTOM_ELEMENT(element) &&
14689         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14690     {
14691       if (!DigFieldByCE(nextx, nexty, element))
14692         return MP_NO_ACTION;
14693     }
14694
14695     if (IS_SB_ELEMENT(element))
14696     {
14697       boolean sokoban_task_solved = FALSE;
14698
14699       if (element == EL_SOKOBAN_FIELD_FULL)
14700       {
14701         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14702
14703         IncrementSokobanFieldsNeeded();
14704         IncrementSokobanObjectsNeeded();
14705       }
14706
14707       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14708       {
14709         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14710
14711         DecrementSokobanFieldsNeeded();
14712         DecrementSokobanObjectsNeeded();
14713
14714         // sokoban object was pushed from empty field to sokoban field
14715         if (Back[x][y] == EL_EMPTY)
14716           sokoban_task_solved = TRUE;
14717       }
14718
14719       Tile[x][y] = EL_SOKOBAN_OBJECT;
14720
14721       if (Back[x][y] == Back[nextx][nexty])
14722         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14723       else if (Back[x][y] != 0)
14724         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14725                                     ACTION_EMPTYING);
14726       else
14727         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14728                                     ACTION_FILLING);
14729
14730       if (sokoban_task_solved &&
14731           game.sokoban_fields_still_needed == 0 &&
14732           game.sokoban_objects_still_needed == 0 &&
14733           level.auto_exit_sokoban)
14734       {
14735         game.players_still_needed = 0;
14736
14737         LevelSolved();
14738
14739         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14740       }
14741     }
14742     else
14743       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14744
14745     InitMovingField(x, y, move_direction);
14746     GfxAction[x][y] = ACTION_PUSHING;
14747
14748     if (mode == DF_SNAP)
14749       ContinueMoving(x, y);
14750     else
14751       MovPos[x][y] = (dx != 0 ? dx : dy);
14752
14753     Pushed[x][y] = TRUE;
14754     Pushed[nextx][nexty] = TRUE;
14755
14756     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14757       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14758     else
14759       player->push_delay_value = -1;    // get new value later
14760
14761     // check for element change _after_ element has been pushed
14762     if (game.use_change_when_pushing_bug)
14763     {
14764       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14765                                  player->index_bit, dig_side);
14766       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14767                                           player->index_bit, dig_side);
14768     }
14769   }
14770   else if (IS_SWITCHABLE(element))
14771   {
14772     if (PLAYER_SWITCHING(player, x, y))
14773     {
14774       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14775                                           player->index_bit, dig_side);
14776
14777       return MP_ACTION;
14778     }
14779
14780     player->is_switching = TRUE;
14781     player->switch_x = x;
14782     player->switch_y = y;
14783
14784     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14785
14786     if (element == EL_ROBOT_WHEEL)
14787     {
14788       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14789
14790       game.robot_wheel_x = x;
14791       game.robot_wheel_y = y;
14792       game.robot_wheel_active = TRUE;
14793
14794       TEST_DrawLevelField(x, y);
14795     }
14796     else if (element == EL_SP_TERMINAL)
14797     {
14798       int xx, yy;
14799
14800       SCAN_PLAYFIELD(xx, yy)
14801       {
14802         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14803         {
14804           Bang(xx, yy);
14805         }
14806         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14807         {
14808           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14809
14810           ResetGfxAnimation(xx, yy);
14811           TEST_DrawLevelField(xx, yy);
14812         }
14813       }
14814     }
14815     else if (IS_BELT_SWITCH(element))
14816     {
14817       ToggleBeltSwitch(x, y);
14818     }
14819     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14820              element == EL_SWITCHGATE_SWITCH_DOWN ||
14821              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14822              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14823     {
14824       ToggleSwitchgateSwitch(x, y);
14825     }
14826     else if (element == EL_LIGHT_SWITCH ||
14827              element == EL_LIGHT_SWITCH_ACTIVE)
14828     {
14829       ToggleLightSwitch(x, y);
14830     }
14831     else if (element == EL_TIMEGATE_SWITCH ||
14832              element == EL_DC_TIMEGATE_SWITCH)
14833     {
14834       ActivateTimegateSwitch(x, y);
14835     }
14836     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14837              element == EL_BALLOON_SWITCH_RIGHT ||
14838              element == EL_BALLOON_SWITCH_UP    ||
14839              element == EL_BALLOON_SWITCH_DOWN  ||
14840              element == EL_BALLOON_SWITCH_NONE  ||
14841              element == EL_BALLOON_SWITCH_ANY)
14842     {
14843       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14844                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14845                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14846                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14847                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14848                              move_direction);
14849     }
14850     else if (element == EL_LAMP)
14851     {
14852       Tile[x][y] = EL_LAMP_ACTIVE;
14853       game.lights_still_needed--;
14854
14855       ResetGfxAnimation(x, y);
14856       TEST_DrawLevelField(x, y);
14857     }
14858     else if (element == EL_TIME_ORB_FULL)
14859     {
14860       Tile[x][y] = EL_TIME_ORB_EMPTY;
14861
14862       if (level.time > 0 || level.use_time_orb_bug)
14863       {
14864         TimeLeft += level.time_orb_time;
14865         game.no_time_limit = FALSE;
14866
14867         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14868
14869         DisplayGameControlValues();
14870       }
14871
14872       ResetGfxAnimation(x, y);
14873       TEST_DrawLevelField(x, y);
14874     }
14875     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14876              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14877     {
14878       int xx, yy;
14879
14880       game.ball_active = !game.ball_active;
14881
14882       SCAN_PLAYFIELD(xx, yy)
14883       {
14884         int e = Tile[xx][yy];
14885
14886         if (game.ball_active)
14887         {
14888           if (e == EL_EMC_MAGIC_BALL)
14889             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14890           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14891             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14892         }
14893         else
14894         {
14895           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14896             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14897           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14898             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14899         }
14900       }
14901     }
14902
14903     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14904                                         player->index_bit, dig_side);
14905
14906     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14907                                         player->index_bit, dig_side);
14908
14909     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14910                                         player->index_bit, dig_side);
14911
14912     return MP_ACTION;
14913   }
14914   else
14915   {
14916     if (!PLAYER_SWITCHING(player, x, y))
14917     {
14918       player->is_switching = TRUE;
14919       player->switch_x = x;
14920       player->switch_y = y;
14921
14922       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14923                                  player->index_bit, dig_side);
14924       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14925                                           player->index_bit, dig_side);
14926
14927       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14928                                  player->index_bit, dig_side);
14929       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14930                                           player->index_bit, dig_side);
14931     }
14932
14933     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14934                                player->index_bit, dig_side);
14935     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14936                                         player->index_bit, dig_side);
14937
14938     return MP_NO_ACTION;
14939   }
14940
14941   player->push_delay = -1;
14942
14943   if (is_player)                // function can also be called by EL_PENGUIN
14944   {
14945     if (Tile[x][y] != element)          // really digged/collected something
14946     {
14947       player->is_collecting = !player->is_digging;
14948       player->is_active = TRUE;
14949
14950       player->last_removed_element = element;
14951     }
14952   }
14953
14954   return MP_MOVING;
14955 }
14956
14957 static boolean DigFieldByCE(int x, int y, int digging_element)
14958 {
14959   int element = Tile[x][y];
14960
14961   if (!IS_FREE(x, y))
14962   {
14963     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14964                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14965                   ACTION_BREAKING);
14966
14967     // no element can dig solid indestructible elements
14968     if (IS_INDESTRUCTIBLE(element) &&
14969         !IS_DIGGABLE(element) &&
14970         !IS_COLLECTIBLE(element))
14971       return FALSE;
14972
14973     if (AmoebaNr[x][y] &&
14974         (element == EL_AMOEBA_FULL ||
14975          element == EL_BD_AMOEBA ||
14976          element == EL_AMOEBA_GROWING))
14977     {
14978       AmoebaCnt[AmoebaNr[x][y]]--;
14979       AmoebaCnt2[AmoebaNr[x][y]]--;
14980     }
14981
14982     if (IS_MOVING(x, y))
14983       RemoveMovingField(x, y);
14984     else
14985     {
14986       RemoveField(x, y);
14987       TEST_DrawLevelField(x, y);
14988     }
14989
14990     // if digged element was about to explode, prevent the explosion
14991     ExplodeField[x][y] = EX_TYPE_NONE;
14992
14993     PlayLevelSoundAction(x, y, action);
14994   }
14995
14996   Store[x][y] = EL_EMPTY;
14997
14998   // this makes it possible to leave the removed element again
14999   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15000     Store[x][y] = element;
15001
15002   return TRUE;
15003 }
15004
15005 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15006 {
15007   int jx = player->jx, jy = player->jy;
15008   int x = jx + dx, y = jy + dy;
15009   int snap_direction = (dx == -1 ? MV_LEFT  :
15010                         dx == +1 ? MV_RIGHT :
15011                         dy == -1 ? MV_UP    :
15012                         dy == +1 ? MV_DOWN  : MV_NONE);
15013   boolean can_continue_snapping = (level.continuous_snapping &&
15014                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15015
15016   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15017     return FALSE;
15018
15019   if (!player->active || !IN_LEV_FIELD(x, y))
15020     return FALSE;
15021
15022   if (dx && dy)
15023     return FALSE;
15024
15025   if (!dx && !dy)
15026   {
15027     if (player->MovPos == 0)
15028       player->is_pushing = FALSE;
15029
15030     player->is_snapping = FALSE;
15031
15032     if (player->MovPos == 0)
15033     {
15034       player->is_moving = FALSE;
15035       player->is_digging = FALSE;
15036       player->is_collecting = FALSE;
15037     }
15038
15039     return FALSE;
15040   }
15041
15042   // prevent snapping with already pressed snap key when not allowed
15043   if (player->is_snapping && !can_continue_snapping)
15044     return FALSE;
15045
15046   player->MovDir = snap_direction;
15047
15048   if (player->MovPos == 0)
15049   {
15050     player->is_moving = FALSE;
15051     player->is_digging = FALSE;
15052     player->is_collecting = FALSE;
15053   }
15054
15055   player->is_dropping = FALSE;
15056   player->is_dropping_pressed = FALSE;
15057   player->drop_pressed_delay = 0;
15058
15059   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15060     return FALSE;
15061
15062   player->is_snapping = TRUE;
15063   player->is_active = TRUE;
15064
15065   if (player->MovPos == 0)
15066   {
15067     player->is_moving = FALSE;
15068     player->is_digging = FALSE;
15069     player->is_collecting = FALSE;
15070   }
15071
15072   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15073     TEST_DrawLevelField(player->last_jx, player->last_jy);
15074
15075   TEST_DrawLevelField(x, y);
15076
15077   return TRUE;
15078 }
15079
15080 static boolean DropElement(struct PlayerInfo *player)
15081 {
15082   int old_element, new_element;
15083   int dropx = player->jx, dropy = player->jy;
15084   int drop_direction = player->MovDir;
15085   int drop_side = drop_direction;
15086   int drop_element = get_next_dropped_element(player);
15087
15088   /* do not drop an element on top of another element; when holding drop key
15089      pressed without moving, dropped element must move away before the next
15090      element can be dropped (this is especially important if the next element
15091      is dynamite, which can be placed on background for historical reasons) */
15092   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15093     return MP_ACTION;
15094
15095   if (IS_THROWABLE(drop_element))
15096   {
15097     dropx += GET_DX_FROM_DIR(drop_direction);
15098     dropy += GET_DY_FROM_DIR(drop_direction);
15099
15100     if (!IN_LEV_FIELD(dropx, dropy))
15101       return FALSE;
15102   }
15103
15104   old_element = Tile[dropx][dropy];     // old element at dropping position
15105   new_element = drop_element;           // default: no change when dropping
15106
15107   // check if player is active, not moving and ready to drop
15108   if (!player->active || player->MovPos || player->drop_delay > 0)
15109     return FALSE;
15110
15111   // check if player has anything that can be dropped
15112   if (new_element == EL_UNDEFINED)
15113     return FALSE;
15114
15115   // only set if player has anything that can be dropped
15116   player->is_dropping_pressed = TRUE;
15117
15118   // check if drop key was pressed long enough for EM style dynamite
15119   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15120     return FALSE;
15121
15122   // check if anything can be dropped at the current position
15123   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15124     return FALSE;
15125
15126   // collected custom elements can only be dropped on empty fields
15127   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15128     return FALSE;
15129
15130   if (old_element != EL_EMPTY)
15131     Back[dropx][dropy] = old_element;   // store old element on this field
15132
15133   ResetGfxAnimation(dropx, dropy);
15134   ResetRandomAnimationValue(dropx, dropy);
15135
15136   if (player->inventory_size > 0 ||
15137       player->inventory_infinite_element != EL_UNDEFINED)
15138   {
15139     if (player->inventory_size > 0)
15140     {
15141       player->inventory_size--;
15142
15143       DrawGameDoorValues();
15144
15145       if (new_element == EL_DYNAMITE)
15146         new_element = EL_DYNAMITE_ACTIVE;
15147       else if (new_element == EL_EM_DYNAMITE)
15148         new_element = EL_EM_DYNAMITE_ACTIVE;
15149       else if (new_element == EL_SP_DISK_RED)
15150         new_element = EL_SP_DISK_RED_ACTIVE;
15151     }
15152
15153     Tile[dropx][dropy] = new_element;
15154
15155     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15156       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15157                           el2img(Tile[dropx][dropy]), 0);
15158
15159     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15160
15161     // needed if previous element just changed to "empty" in the last frame
15162     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15163
15164     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15165                                player->index_bit, drop_side);
15166     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15167                                         CE_PLAYER_DROPS_X,
15168                                         player->index_bit, drop_side);
15169
15170     TestIfElementTouchesCustomElement(dropx, dropy);
15171   }
15172   else          // player is dropping a dyna bomb
15173   {
15174     player->dynabombs_left--;
15175
15176     Tile[dropx][dropy] = new_element;
15177
15178     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15179       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15180                           el2img(Tile[dropx][dropy]), 0);
15181
15182     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15183   }
15184
15185   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15186     InitField_WithBug1(dropx, dropy, FALSE);
15187
15188   new_element = Tile[dropx][dropy];     // element might have changed
15189
15190   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15191       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15192   {
15193     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15194       MovDir[dropx][dropy] = drop_direction;
15195
15196     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15197
15198     // do not cause impact style collision by dropping elements that can fall
15199     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15200   }
15201
15202   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15203   player->is_dropping = TRUE;
15204
15205   player->drop_pressed_delay = 0;
15206   player->is_dropping_pressed = FALSE;
15207
15208   player->drop_x = dropx;
15209   player->drop_y = dropy;
15210
15211   return TRUE;
15212 }
15213
15214 // ----------------------------------------------------------------------------
15215 // game sound playing functions
15216 // ----------------------------------------------------------------------------
15217
15218 static int *loop_sound_frame = NULL;
15219 static int *loop_sound_volume = NULL;
15220
15221 void InitPlayLevelSound(void)
15222 {
15223   int num_sounds = getSoundListSize();
15224
15225   checked_free(loop_sound_frame);
15226   checked_free(loop_sound_volume);
15227
15228   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15229   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15230 }
15231
15232 static void PlayLevelSound(int x, int y, int nr)
15233 {
15234   int sx = SCREENX(x), sy = SCREENY(y);
15235   int volume, stereo_position;
15236   int max_distance = 8;
15237   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15238
15239   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15240       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15241     return;
15242
15243   if (!IN_LEV_FIELD(x, y) ||
15244       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15245       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15246     return;
15247
15248   volume = SOUND_MAX_VOLUME;
15249
15250   if (!IN_SCR_FIELD(sx, sy))
15251   {
15252     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15253     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15254
15255     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15256   }
15257
15258   stereo_position = (SOUND_MAX_LEFT +
15259                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15260                      (SCR_FIELDX + 2 * max_distance));
15261
15262   if (IS_LOOP_SOUND(nr))
15263   {
15264     /* This assures that quieter loop sounds do not overwrite louder ones,
15265        while restarting sound volume comparison with each new game frame. */
15266
15267     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15268       return;
15269
15270     loop_sound_volume[nr] = volume;
15271     loop_sound_frame[nr] = FrameCounter;
15272   }
15273
15274   PlaySoundExt(nr, volume, stereo_position, type);
15275 }
15276
15277 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15278 {
15279   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15280                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15281                  y < LEVELY(BY1) ? LEVELY(BY1) :
15282                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15283                  sound_action);
15284 }
15285
15286 static void PlayLevelSoundAction(int x, int y, int action)
15287 {
15288   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15289 }
15290
15291 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15292 {
15293   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15294
15295   if (sound_effect != SND_UNDEFINED)
15296     PlayLevelSound(x, y, sound_effect);
15297 }
15298
15299 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15300                                               int action)
15301 {
15302   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15303
15304   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15305     PlayLevelSound(x, y, sound_effect);
15306 }
15307
15308 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15309 {
15310   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15311
15312   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15313     PlayLevelSound(x, y, sound_effect);
15314 }
15315
15316 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15317 {
15318   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15319
15320   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15321     StopSound(sound_effect);
15322 }
15323
15324 static int getLevelMusicNr(void)
15325 {
15326   if (levelset.music[level_nr] != MUS_UNDEFINED)
15327     return levelset.music[level_nr];            // from config file
15328   else
15329     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15330 }
15331
15332 static void FadeLevelSounds(void)
15333 {
15334   FadeSounds();
15335 }
15336
15337 static void FadeLevelMusic(void)
15338 {
15339   int music_nr = getLevelMusicNr();
15340   char *curr_music = getCurrentlyPlayingMusicFilename();
15341   char *next_music = getMusicInfoEntryFilename(music_nr);
15342
15343   if (!strEqual(curr_music, next_music))
15344     FadeMusic();
15345 }
15346
15347 void FadeLevelSoundsAndMusic(void)
15348 {
15349   FadeLevelSounds();
15350   FadeLevelMusic();
15351 }
15352
15353 static void PlayLevelMusic(void)
15354 {
15355   int music_nr = getLevelMusicNr();
15356   char *curr_music = getCurrentlyPlayingMusicFilename();
15357   char *next_music = getMusicInfoEntryFilename(music_nr);
15358
15359   if (!strEqual(curr_music, next_music))
15360     PlayMusicLoop(music_nr);
15361 }
15362
15363 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15364 {
15365   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15366   int offset = 0;
15367   int x = xx - offset;
15368   int y = yy - offset;
15369
15370   switch (sample)
15371   {
15372     case SOUND_blank:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15374       break;
15375
15376     case SOUND_roll:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15378       break;
15379
15380     case SOUND_stone:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15382       break;
15383
15384     case SOUND_nut:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15386       break;
15387
15388     case SOUND_crack:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15390       break;
15391
15392     case SOUND_bug:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15394       break;
15395
15396     case SOUND_tank:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15398       break;
15399
15400     case SOUND_android_clone:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15402       break;
15403
15404     case SOUND_android_move:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15406       break;
15407
15408     case SOUND_spring:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15410       break;
15411
15412     case SOUND_slurp:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15414       break;
15415
15416     case SOUND_eater:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15418       break;
15419
15420     case SOUND_eater_eat:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15422       break;
15423
15424     case SOUND_alien:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15426       break;
15427
15428     case SOUND_collect:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15430       break;
15431
15432     case SOUND_diamond:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15434       break;
15435
15436     case SOUND_squash:
15437       // !!! CHECK THIS !!!
15438 #if 1
15439       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15440 #else
15441       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15442 #endif
15443       break;
15444
15445     case SOUND_wonderfall:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15447       break;
15448
15449     case SOUND_drip:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15451       break;
15452
15453     case SOUND_push:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15455       break;
15456
15457     case SOUND_dirt:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15459       break;
15460
15461     case SOUND_acid:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15463       break;
15464
15465     case SOUND_ball:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15467       break;
15468
15469     case SOUND_slide:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15471       break;
15472
15473     case SOUND_wonder:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15475       break;
15476
15477     case SOUND_door:
15478       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15479       break;
15480
15481     case SOUND_exit_open:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15483       break;
15484
15485     case SOUND_exit_leave:
15486       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15487       break;
15488
15489     case SOUND_dynamite:
15490       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15491       break;
15492
15493     case SOUND_tick:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15495       break;
15496
15497     case SOUND_press:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15499       break;
15500
15501     case SOUND_wheel:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15503       break;
15504
15505     case SOUND_boom:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15507       break;
15508
15509     case SOUND_die:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15511       break;
15512
15513     case SOUND_time:
15514       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15515       break;
15516
15517     default:
15518       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15519       break;
15520   }
15521 }
15522
15523 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15524 {
15525   int element = map_element_SP_to_RND(element_sp);
15526   int action = map_action_SP_to_RND(action_sp);
15527   int offset = (setup.sp_show_border_elements ? 0 : 1);
15528   int x = xx - offset;
15529   int y = yy - offset;
15530
15531   PlayLevelSoundElementAction(x, y, element, action);
15532 }
15533
15534 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15535 {
15536   int element = map_element_MM_to_RND(element_mm);
15537   int action = map_action_MM_to_RND(action_mm);
15538   int offset = 0;
15539   int x = xx - offset;
15540   int y = yy - offset;
15541
15542   if (!IS_MM_ELEMENT(element))
15543     element = EL_MM_DEFAULT;
15544
15545   PlayLevelSoundElementAction(x, y, element, action);
15546 }
15547
15548 void PlaySound_MM(int sound_mm)
15549 {
15550   int sound = map_sound_MM_to_RND(sound_mm);
15551
15552   if (sound == SND_UNDEFINED)
15553     return;
15554
15555   PlaySound(sound);
15556 }
15557
15558 void PlaySoundLoop_MM(int sound_mm)
15559 {
15560   int sound = map_sound_MM_to_RND(sound_mm);
15561
15562   if (sound == SND_UNDEFINED)
15563     return;
15564
15565   PlaySoundLoop(sound);
15566 }
15567
15568 void StopSound_MM(int sound_mm)
15569 {
15570   int sound = map_sound_MM_to_RND(sound_mm);
15571
15572   if (sound == SND_UNDEFINED)
15573     return;
15574
15575   StopSound(sound);
15576 }
15577
15578 void RaiseScore(int value)
15579 {
15580   game.score += value;
15581
15582   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15583
15584   DisplayGameControlValues();
15585 }
15586
15587 void RaiseScoreElement(int element)
15588 {
15589   switch (element)
15590   {
15591     case EL_EMERALD:
15592     case EL_BD_DIAMOND:
15593     case EL_EMERALD_YELLOW:
15594     case EL_EMERALD_RED:
15595     case EL_EMERALD_PURPLE:
15596     case EL_SP_INFOTRON:
15597       RaiseScore(level.score[SC_EMERALD]);
15598       break;
15599     case EL_DIAMOND:
15600       RaiseScore(level.score[SC_DIAMOND]);
15601       break;
15602     case EL_CRYSTAL:
15603       RaiseScore(level.score[SC_CRYSTAL]);
15604       break;
15605     case EL_PEARL:
15606       RaiseScore(level.score[SC_PEARL]);
15607       break;
15608     case EL_BUG:
15609     case EL_BD_BUTTERFLY:
15610     case EL_SP_ELECTRON:
15611       RaiseScore(level.score[SC_BUG]);
15612       break;
15613     case EL_SPACESHIP:
15614     case EL_BD_FIREFLY:
15615     case EL_SP_SNIKSNAK:
15616       RaiseScore(level.score[SC_SPACESHIP]);
15617       break;
15618     case EL_YAMYAM:
15619     case EL_DARK_YAMYAM:
15620       RaiseScore(level.score[SC_YAMYAM]);
15621       break;
15622     case EL_ROBOT:
15623       RaiseScore(level.score[SC_ROBOT]);
15624       break;
15625     case EL_PACMAN:
15626       RaiseScore(level.score[SC_PACMAN]);
15627       break;
15628     case EL_NUT:
15629       RaiseScore(level.score[SC_NUT]);
15630       break;
15631     case EL_DYNAMITE:
15632     case EL_EM_DYNAMITE:
15633     case EL_SP_DISK_RED:
15634     case EL_DYNABOMB_INCREASE_NUMBER:
15635     case EL_DYNABOMB_INCREASE_SIZE:
15636     case EL_DYNABOMB_INCREASE_POWER:
15637       RaiseScore(level.score[SC_DYNAMITE]);
15638       break;
15639     case EL_SHIELD_NORMAL:
15640     case EL_SHIELD_DEADLY:
15641       RaiseScore(level.score[SC_SHIELD]);
15642       break;
15643     case EL_EXTRA_TIME:
15644       RaiseScore(level.extra_time_score);
15645       break;
15646     case EL_KEY_1:
15647     case EL_KEY_2:
15648     case EL_KEY_3:
15649     case EL_KEY_4:
15650     case EL_EM_KEY_1:
15651     case EL_EM_KEY_2:
15652     case EL_EM_KEY_3:
15653     case EL_EM_KEY_4:
15654     case EL_EMC_KEY_5:
15655     case EL_EMC_KEY_6:
15656     case EL_EMC_KEY_7:
15657     case EL_EMC_KEY_8:
15658     case EL_DC_KEY_WHITE:
15659       RaiseScore(level.score[SC_KEY]);
15660       break;
15661     default:
15662       RaiseScore(element_info[element].collect_score);
15663       break;
15664   }
15665 }
15666
15667 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15668 {
15669   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15670   {
15671     if (!quick_quit)
15672     {
15673       // prevent short reactivation of overlay buttons while closing door
15674       SetOverlayActive(FALSE);
15675
15676       // door may still be open due to skipped or envelope style request
15677       CloseDoor(DOOR_CLOSE_1);
15678     }
15679
15680     if (network.enabled)
15681       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15682     else
15683     {
15684       if (quick_quit)
15685         FadeSkipNextFadeIn();
15686
15687       SetGameStatus(GAME_MODE_MAIN);
15688
15689       DrawMainMenu();
15690     }
15691   }
15692   else          // continue playing the game
15693   {
15694     if (tape.playing && tape.deactivate_display)
15695       TapeDeactivateDisplayOff(TRUE);
15696
15697     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15698
15699     if (tape.playing && tape.deactivate_display)
15700       TapeDeactivateDisplayOn();
15701   }
15702 }
15703
15704 void RequestQuitGame(boolean escape_key_pressed)
15705 {
15706   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15707   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15708                         level_editor_test_game);
15709   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15710                           quick_quit);
15711
15712   RequestQuitGameExt(skip_request, quick_quit,
15713                      "Do you really want to quit the game?");
15714 }
15715
15716 void RequestRestartGame(char *message)
15717 {
15718   game.restart_game_message = NULL;
15719
15720   boolean has_started_game = hasStartedNetworkGame();
15721   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15722
15723   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15724   {
15725     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15726   }
15727   else
15728   {
15729     // needed in case of envelope request to close game panel
15730     CloseDoor(DOOR_CLOSE_1);
15731
15732     SetGameStatus(GAME_MODE_MAIN);
15733
15734     DrawMainMenu();
15735   }
15736 }
15737
15738 void CheckGameOver(void)
15739 {
15740   static boolean last_game_over = FALSE;
15741   static int game_over_delay = 0;
15742   int game_over_delay_value = 50;
15743   boolean game_over = checkGameFailed();
15744
15745   // do not handle game over if request dialog is already active
15746   if (game.request_active)
15747     return;
15748
15749   // do not ask to play again if game was never actually played
15750   if (!game.GamePlayed)
15751     return;
15752
15753   if (!game_over)
15754   {
15755     last_game_over = FALSE;
15756     game_over_delay = game_over_delay_value;
15757
15758     return;
15759   }
15760
15761   if (game_over_delay > 0)
15762   {
15763     game_over_delay--;
15764
15765     return;
15766   }
15767
15768   if (last_game_over != game_over)
15769     game.restart_game_message = (hasStartedNetworkGame() ?
15770                                  "Game over! Play it again?" :
15771                                  "Game over!");
15772
15773   last_game_over = game_over;
15774 }
15775
15776 boolean checkGameSolved(void)
15777 {
15778   // set for all game engines if level was solved
15779   return game.LevelSolved_GameEnd;
15780 }
15781
15782 boolean checkGameFailed(void)
15783 {
15784   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15785     return (game_em.game_over && !game_em.level_solved);
15786   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15787     return (game_sp.game_over && !game_sp.level_solved);
15788   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15789     return (game_mm.game_over && !game_mm.level_solved);
15790   else                          // GAME_ENGINE_TYPE_RND
15791     return (game.GameOver && !game.LevelSolved);
15792 }
15793
15794 boolean checkGameEnded(void)
15795 {
15796   return (checkGameSolved() || checkGameFailed());
15797 }
15798
15799
15800 // ----------------------------------------------------------------------------
15801 // random generator functions
15802 // ----------------------------------------------------------------------------
15803
15804 unsigned int InitEngineRandom_RND(int seed)
15805 {
15806   game.num_random_calls = 0;
15807
15808   return InitEngineRandom(seed);
15809 }
15810
15811 unsigned int RND(int max)
15812 {
15813   if (max > 0)
15814   {
15815     game.num_random_calls++;
15816
15817     return GetEngineRandom(max);
15818   }
15819
15820   return 0;
15821 }
15822
15823
15824 // ----------------------------------------------------------------------------
15825 // game engine snapshot handling functions
15826 // ----------------------------------------------------------------------------
15827
15828 struct EngineSnapshotInfo
15829 {
15830   // runtime values for custom element collect score
15831   int collect_score[NUM_CUSTOM_ELEMENTS];
15832
15833   // runtime values for group element choice position
15834   int choice_pos[NUM_GROUP_ELEMENTS];
15835
15836   // runtime values for belt position animations
15837   int belt_graphic[4][NUM_BELT_PARTS];
15838   int belt_anim_mode[4][NUM_BELT_PARTS];
15839 };
15840
15841 static struct EngineSnapshotInfo engine_snapshot_rnd;
15842 static char *snapshot_level_identifier = NULL;
15843 static int snapshot_level_nr = -1;
15844
15845 static void SaveEngineSnapshotValues_RND(void)
15846 {
15847   static int belt_base_active_element[4] =
15848   {
15849     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15850     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15851     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15852     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15853   };
15854   int i, j;
15855
15856   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15857   {
15858     int element = EL_CUSTOM_START + i;
15859
15860     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15861   }
15862
15863   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15864   {
15865     int element = EL_GROUP_START + i;
15866
15867     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15868   }
15869
15870   for (i = 0; i < 4; i++)
15871   {
15872     for (j = 0; j < NUM_BELT_PARTS; j++)
15873     {
15874       int element = belt_base_active_element[i] + j;
15875       int graphic = el2img(element);
15876       int anim_mode = graphic_info[graphic].anim_mode;
15877
15878       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15879       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15880     }
15881   }
15882 }
15883
15884 static void LoadEngineSnapshotValues_RND(void)
15885 {
15886   unsigned int num_random_calls = game.num_random_calls;
15887   int i, j;
15888
15889   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15890   {
15891     int element = EL_CUSTOM_START + i;
15892
15893     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15894   }
15895
15896   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15897   {
15898     int element = EL_GROUP_START + i;
15899
15900     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15901   }
15902
15903   for (i = 0; i < 4; i++)
15904   {
15905     for (j = 0; j < NUM_BELT_PARTS; j++)
15906     {
15907       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15908       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15909
15910       graphic_info[graphic].anim_mode = anim_mode;
15911     }
15912   }
15913
15914   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15915   {
15916     InitRND(tape.random_seed);
15917     for (i = 0; i < num_random_calls; i++)
15918       RND(1);
15919   }
15920
15921   if (game.num_random_calls != num_random_calls)
15922   {
15923     Error("number of random calls out of sync");
15924     Error("number of random calls should be %d", num_random_calls);
15925     Error("number of random calls is %d", game.num_random_calls);
15926
15927     Fail("this should not happen -- please debug");
15928   }
15929 }
15930
15931 void FreeEngineSnapshotSingle(void)
15932 {
15933   FreeSnapshotSingle();
15934
15935   setString(&snapshot_level_identifier, NULL);
15936   snapshot_level_nr = -1;
15937 }
15938
15939 void FreeEngineSnapshotList(void)
15940 {
15941   FreeSnapshotList();
15942 }
15943
15944 static ListNode *SaveEngineSnapshotBuffers(void)
15945 {
15946   ListNode *buffers = NULL;
15947
15948   // copy some special values to a structure better suited for the snapshot
15949
15950   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15951     SaveEngineSnapshotValues_RND();
15952   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15953     SaveEngineSnapshotValues_EM();
15954   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15955     SaveEngineSnapshotValues_SP(&buffers);
15956   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15957     SaveEngineSnapshotValues_MM(&buffers);
15958
15959   // save values stored in special snapshot structure
15960
15961   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15962     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15963   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15964     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15965   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15966     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15967   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15968     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15969
15970   // save further RND engine values
15971
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15975
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15981
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15985
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15987
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16009
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16012
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16016
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16019
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16026
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16029
16030 #if 0
16031   ListNode *node = engine_snapshot_list_rnd;
16032   int num_bytes = 0;
16033
16034   while (node != NULL)
16035   {
16036     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16037
16038     node = node->next;
16039   }
16040
16041   Debug("game:playing:SaveEngineSnapshotBuffers",
16042         "size of engine snapshot: %d bytes", num_bytes);
16043 #endif
16044
16045   return buffers;
16046 }
16047
16048 void SaveEngineSnapshotSingle(void)
16049 {
16050   ListNode *buffers = SaveEngineSnapshotBuffers();
16051
16052   // finally save all snapshot buffers to single snapshot
16053   SaveSnapshotSingle(buffers);
16054
16055   // save level identification information
16056   setString(&snapshot_level_identifier, leveldir_current->identifier);
16057   snapshot_level_nr = level_nr;
16058 }
16059
16060 boolean CheckSaveEngineSnapshotToList(void)
16061 {
16062   boolean save_snapshot =
16063     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16064      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16065       game.snapshot.changed_action) ||
16066      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16067       game.snapshot.collected_item));
16068
16069   game.snapshot.changed_action = FALSE;
16070   game.snapshot.collected_item = FALSE;
16071   game.snapshot.save_snapshot = save_snapshot;
16072
16073   return save_snapshot;
16074 }
16075
16076 void SaveEngineSnapshotToList(void)
16077 {
16078   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16079       tape.quick_resume)
16080     return;
16081
16082   ListNode *buffers = SaveEngineSnapshotBuffers();
16083
16084   // finally save all snapshot buffers to snapshot list
16085   SaveSnapshotToList(buffers);
16086 }
16087
16088 void SaveEngineSnapshotToListInitial(void)
16089 {
16090   FreeEngineSnapshotList();
16091
16092   SaveEngineSnapshotToList();
16093 }
16094
16095 static void LoadEngineSnapshotValues(void)
16096 {
16097   // restore special values from snapshot structure
16098
16099   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16100     LoadEngineSnapshotValues_RND();
16101   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16102     LoadEngineSnapshotValues_EM();
16103   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16104     LoadEngineSnapshotValues_SP();
16105   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16106     LoadEngineSnapshotValues_MM();
16107 }
16108
16109 void LoadEngineSnapshotSingle(void)
16110 {
16111   LoadSnapshotSingle();
16112
16113   LoadEngineSnapshotValues();
16114 }
16115
16116 static void LoadEngineSnapshot_Undo(int steps)
16117 {
16118   LoadSnapshotFromList_Older(steps);
16119
16120   LoadEngineSnapshotValues();
16121 }
16122
16123 static void LoadEngineSnapshot_Redo(int steps)
16124 {
16125   LoadSnapshotFromList_Newer(steps);
16126
16127   LoadEngineSnapshotValues();
16128 }
16129
16130 boolean CheckEngineSnapshotSingle(void)
16131 {
16132   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16133           snapshot_level_nr == level_nr);
16134 }
16135
16136 boolean CheckEngineSnapshotList(void)
16137 {
16138   return CheckSnapshotList();
16139 }
16140
16141
16142 // ---------- new game button stuff -------------------------------------------
16143
16144 static struct
16145 {
16146   int graphic;
16147   struct XY *pos;
16148   int gadget_id;
16149   boolean *setup_value;
16150   boolean allowed_on_tape;
16151   boolean is_touch_button;
16152   char *infotext;
16153 } gamebutton_info[NUM_GAME_BUTTONS] =
16154 {
16155   {
16156     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16157     GAME_CTRL_ID_STOP,                          NULL,
16158     TRUE, FALSE,                                "stop game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16162     GAME_CTRL_ID_PAUSE,                         NULL,
16163     TRUE, FALSE,                                "pause game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16167     GAME_CTRL_ID_PLAY,                          NULL,
16168     TRUE, FALSE,                                "play game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16172     GAME_CTRL_ID_UNDO,                          NULL,
16173     TRUE, FALSE,                                "undo step"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16177     GAME_CTRL_ID_REDO,                          NULL,
16178     TRUE, FALSE,                                "redo step"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16182     GAME_CTRL_ID_SAVE,                          NULL,
16183     TRUE, FALSE,                                "save game"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16187     GAME_CTRL_ID_PAUSE2,                        NULL,
16188     TRUE, FALSE,                                "pause game"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16192     GAME_CTRL_ID_LOAD,                          NULL,
16193     TRUE, FALSE,                                "load game"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16197     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16198     FALSE, FALSE,                               "stop game"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16202     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16203     FALSE, FALSE,                               "pause game"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16207     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16208     FALSE, FALSE,                               "play game"
16209   },
16210   {
16211     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16212     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16213     FALSE, TRUE,                                "stop game"
16214   },
16215   {
16216     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16217     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16218     FALSE, TRUE,                                "pause game"
16219   },
16220   {
16221     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16222     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16223     TRUE, FALSE,                                "background music on/off"
16224   },
16225   {
16226     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16227     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16228     TRUE, FALSE,                                "sound loops on/off"
16229   },
16230   {
16231     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16232     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16233     TRUE, FALSE,                                "normal sounds on/off"
16234   },
16235   {
16236     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16237     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16238     FALSE, FALSE,                               "background music on/off"
16239   },
16240   {
16241     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16242     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16243     FALSE, FALSE,                               "sound loops on/off"
16244   },
16245   {
16246     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16247     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16248     FALSE, FALSE,                               "normal sounds on/off"
16249   }
16250 };
16251
16252 void CreateGameButtons(void)
16253 {
16254   int i;
16255
16256   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16257   {
16258     int graphic = gamebutton_info[i].graphic;
16259     struct GraphicInfo *gfx = &graphic_info[graphic];
16260     struct XY *pos = gamebutton_info[i].pos;
16261     struct GadgetInfo *gi;
16262     int button_type;
16263     boolean checked;
16264     unsigned int event_mask;
16265     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16266     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16267     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16268     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16269     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16270     int gd_x   = gfx->src_x;
16271     int gd_y   = gfx->src_y;
16272     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16273     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16274     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16275     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16276     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16277     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16278     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16279     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16280     int id = i;
16281
16282     if (gfx->bitmap == NULL)
16283     {
16284       game_gadget[id] = NULL;
16285
16286       continue;
16287     }
16288
16289     if (id == GAME_CTRL_ID_STOP ||
16290         id == GAME_CTRL_ID_PANEL_STOP ||
16291         id == GAME_CTRL_ID_TOUCH_STOP ||
16292         id == GAME_CTRL_ID_PLAY ||
16293         id == GAME_CTRL_ID_PANEL_PLAY ||
16294         id == GAME_CTRL_ID_SAVE ||
16295         id == GAME_CTRL_ID_LOAD)
16296     {
16297       button_type = GD_TYPE_NORMAL_BUTTON;
16298       checked = FALSE;
16299       event_mask = GD_EVENT_RELEASED;
16300     }
16301     else if (id == GAME_CTRL_ID_UNDO ||
16302              id == GAME_CTRL_ID_REDO)
16303     {
16304       button_type = GD_TYPE_NORMAL_BUTTON;
16305       checked = FALSE;
16306       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16307     }
16308     else
16309     {
16310       button_type = GD_TYPE_CHECK_BUTTON;
16311       checked = (gamebutton_info[i].setup_value != NULL ?
16312                  *gamebutton_info[i].setup_value : FALSE);
16313       event_mask = GD_EVENT_PRESSED;
16314     }
16315
16316     gi = CreateGadget(GDI_CUSTOM_ID, id,
16317                       GDI_IMAGE_ID, graphic,
16318                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16319                       GDI_X, base_x + x,
16320                       GDI_Y, base_y + y,
16321                       GDI_WIDTH, gfx->width,
16322                       GDI_HEIGHT, gfx->height,
16323                       GDI_TYPE, button_type,
16324                       GDI_STATE, GD_BUTTON_UNPRESSED,
16325                       GDI_CHECKED, checked,
16326                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16327                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16328                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16329                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16330                       GDI_DIRECT_DRAW, FALSE,
16331                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16332                       GDI_EVENT_MASK, event_mask,
16333                       GDI_CALLBACK_ACTION, HandleGameButtons,
16334                       GDI_END);
16335
16336     if (gi == NULL)
16337       Fail("cannot create gadget");
16338
16339     game_gadget[id] = gi;
16340   }
16341 }
16342
16343 void FreeGameButtons(void)
16344 {
16345   int i;
16346
16347   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16348     FreeGadget(game_gadget[i]);
16349 }
16350
16351 static void UnmapGameButtonsAtSamePosition(int id)
16352 {
16353   int i;
16354
16355   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16356     if (i != id &&
16357         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16358         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16359       UnmapGadget(game_gadget[i]);
16360 }
16361
16362 static void UnmapGameButtonsAtSamePosition_All(void)
16363 {
16364   if (setup.show_load_save_buttons)
16365   {
16366     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16368     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16369   }
16370   else if (setup.show_undo_redo_buttons)
16371   {
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16374     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16375   }
16376   else
16377   {
16378     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16379     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16380     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16381
16382     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16383     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16384     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16385   }
16386 }
16387
16388 void MapLoadSaveButtons(void)
16389 {
16390   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16391   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16392
16393   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16394   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16395 }
16396
16397 void MapUndoRedoButtons(void)
16398 {
16399   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16400   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16401
16402   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16403   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16404 }
16405
16406 void ModifyPauseButtons(void)
16407 {
16408   static int ids[] =
16409   {
16410     GAME_CTRL_ID_PAUSE,
16411     GAME_CTRL_ID_PAUSE2,
16412     GAME_CTRL_ID_PANEL_PAUSE,
16413     GAME_CTRL_ID_TOUCH_PAUSE,
16414     -1
16415   };
16416   int i;
16417
16418   for (i = 0; ids[i] > -1; i++)
16419     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16420 }
16421
16422 static void MapGameButtonsExt(boolean on_tape)
16423 {
16424   int i;
16425
16426   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16427     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16428       MapGadget(game_gadget[i]);
16429
16430   UnmapGameButtonsAtSamePosition_All();
16431
16432   RedrawGameButtons();
16433 }
16434
16435 static void UnmapGameButtonsExt(boolean on_tape)
16436 {
16437   int i;
16438
16439   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16440     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16441       UnmapGadget(game_gadget[i]);
16442 }
16443
16444 static void RedrawGameButtonsExt(boolean on_tape)
16445 {
16446   int i;
16447
16448   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16449     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16450       RedrawGadget(game_gadget[i]);
16451 }
16452
16453 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16454 {
16455   if (gi == NULL)
16456     return;
16457
16458   gi->checked = state;
16459 }
16460
16461 static void RedrawSoundButtonGadget(int id)
16462 {
16463   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16464              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16465              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16466              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16467              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16468              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16469              id);
16470
16471   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16472   RedrawGadget(game_gadget[id2]);
16473 }
16474
16475 void MapGameButtons(void)
16476 {
16477   MapGameButtonsExt(FALSE);
16478 }
16479
16480 void UnmapGameButtons(void)
16481 {
16482   UnmapGameButtonsExt(FALSE);
16483 }
16484
16485 void RedrawGameButtons(void)
16486 {
16487   RedrawGameButtonsExt(FALSE);
16488 }
16489
16490 void MapGameButtonsOnTape(void)
16491 {
16492   MapGameButtonsExt(TRUE);
16493 }
16494
16495 void UnmapGameButtonsOnTape(void)
16496 {
16497   UnmapGameButtonsExt(TRUE);
16498 }
16499
16500 void RedrawGameButtonsOnTape(void)
16501 {
16502   RedrawGameButtonsExt(TRUE);
16503 }
16504
16505 static void GameUndoRedoExt(void)
16506 {
16507   ClearPlayerAction();
16508
16509   tape.pausing = TRUE;
16510
16511   RedrawPlayfield();
16512   UpdateAndDisplayGameControlValues();
16513
16514   DrawCompleteVideoDisplay();
16515   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16516   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16517   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16518
16519   ModifyPauseButtons();
16520
16521   BackToFront();
16522 }
16523
16524 static void GameUndo(int steps)
16525 {
16526   if (!CheckEngineSnapshotList())
16527     return;
16528
16529   int tape_property_bits = tape.property_bits;
16530
16531   LoadEngineSnapshot_Undo(steps);
16532
16533   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16534
16535   GameUndoRedoExt();
16536 }
16537
16538 static void GameRedo(int steps)
16539 {
16540   if (!CheckEngineSnapshotList())
16541     return;
16542
16543   int tape_property_bits = tape.property_bits;
16544
16545   LoadEngineSnapshot_Redo(steps);
16546
16547   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16548
16549   GameUndoRedoExt();
16550 }
16551
16552 static void HandleGameButtonsExt(int id, int button)
16553 {
16554   static boolean game_undo_executed = FALSE;
16555   int steps = BUTTON_STEPSIZE(button);
16556   boolean handle_game_buttons =
16557     (game_status == GAME_MODE_PLAYING ||
16558      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16559
16560   if (!handle_game_buttons)
16561     return;
16562
16563   switch (id)
16564   {
16565     case GAME_CTRL_ID_STOP:
16566     case GAME_CTRL_ID_PANEL_STOP:
16567     case GAME_CTRL_ID_TOUCH_STOP:
16568       if (game_status == GAME_MODE_MAIN)
16569         break;
16570
16571       if (tape.playing)
16572         TapeStop();
16573       else
16574         RequestQuitGame(FALSE);
16575
16576       break;
16577
16578     case GAME_CTRL_ID_PAUSE:
16579     case GAME_CTRL_ID_PAUSE2:
16580     case GAME_CTRL_ID_PANEL_PAUSE:
16581     case GAME_CTRL_ID_TOUCH_PAUSE:
16582       if (network.enabled && game_status == GAME_MODE_PLAYING)
16583       {
16584         if (tape.pausing)
16585           SendToServer_ContinuePlaying();
16586         else
16587           SendToServer_PausePlaying();
16588       }
16589       else
16590         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16591
16592       game_undo_executed = FALSE;
16593
16594       break;
16595
16596     case GAME_CTRL_ID_PLAY:
16597     case GAME_CTRL_ID_PANEL_PLAY:
16598       if (game_status == GAME_MODE_MAIN)
16599       {
16600         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16601       }
16602       else if (tape.pausing)
16603       {
16604         if (network.enabled)
16605           SendToServer_ContinuePlaying();
16606         else
16607           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16608       }
16609       break;
16610
16611     case GAME_CTRL_ID_UNDO:
16612       // Important: When using "save snapshot when collecting an item" mode,
16613       // load last (current) snapshot for first "undo" after pressing "pause"
16614       // (else the last-but-one snapshot would be loaded, because the snapshot
16615       // pointer already points to the last snapshot when pressing "pause",
16616       // which is fine for "every step/move" mode, but not for "every collect")
16617       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16618           !game_undo_executed)
16619         steps--;
16620
16621       game_undo_executed = TRUE;
16622
16623       GameUndo(steps);
16624       break;
16625
16626     case GAME_CTRL_ID_REDO:
16627       GameRedo(steps);
16628       break;
16629
16630     case GAME_CTRL_ID_SAVE:
16631       TapeQuickSave();
16632       break;
16633
16634     case GAME_CTRL_ID_LOAD:
16635       TapeQuickLoad();
16636       break;
16637
16638     case SOUND_CTRL_ID_MUSIC:
16639     case SOUND_CTRL_ID_PANEL_MUSIC:
16640       if (setup.sound_music)
16641       { 
16642         setup.sound_music = FALSE;
16643
16644         FadeMusic();
16645       }
16646       else if (audio.music_available)
16647       { 
16648         setup.sound = setup.sound_music = TRUE;
16649
16650         SetAudioMode(setup.sound);
16651
16652         if (game_status == GAME_MODE_PLAYING)
16653           PlayLevelMusic();
16654       }
16655
16656       RedrawSoundButtonGadget(id);
16657
16658       break;
16659
16660     case SOUND_CTRL_ID_LOOPS:
16661     case SOUND_CTRL_ID_PANEL_LOOPS:
16662       if (setup.sound_loops)
16663         setup.sound_loops = FALSE;
16664       else if (audio.loops_available)
16665       {
16666         setup.sound = setup.sound_loops = TRUE;
16667
16668         SetAudioMode(setup.sound);
16669       }
16670
16671       RedrawSoundButtonGadget(id);
16672
16673       break;
16674
16675     case SOUND_CTRL_ID_SIMPLE:
16676     case SOUND_CTRL_ID_PANEL_SIMPLE:
16677       if (setup.sound_simple)
16678         setup.sound_simple = FALSE;
16679       else if (audio.sound_available)
16680       {
16681         setup.sound = setup.sound_simple = TRUE;
16682
16683         SetAudioMode(setup.sound);
16684       }
16685
16686       RedrawSoundButtonGadget(id);
16687
16688       break;
16689
16690     default:
16691       break;
16692   }
16693 }
16694
16695 static void HandleGameButtons(struct GadgetInfo *gi)
16696 {
16697   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16698 }
16699
16700 void HandleSoundButtonKeys(Key key)
16701 {
16702   if (key == setup.shortcut.sound_simple)
16703     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16704   else if (key == setup.shortcut.sound_loops)
16705     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16706   else if (key == setup.shortcut.sound_music)
16707     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16708 }