fixed half-tile shifting when teleporting between playfield borders
[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 struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize if element can trigger global animations -----------
3199
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[i];
3203
3204     ei->has_anim_event = FALSE;
3205   }
3206
3207   InitGlobalAnimEventsForCustomElements();
3208
3209   // ---------- initialize internal run-time variables ------------------------
3210
3211   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       ei->change_page[j].can_change_or_has_action =
3218         (ei->change_page[j].can_change |
3219          ei->change_page[j].has_action);
3220     }
3221   }
3222
3223   // add change events from custom element configuration
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       if (!ei->change_page[j].can_change_or_has_action)
3231         continue;
3232
3233       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234       {
3235         // only add event page for the first page found with this event
3236         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237         {
3238           ei->has_change_event[k] = TRUE;
3239
3240           ei->event_page_nr[k] = j;
3241           ei->event_page[k] = &ei->change_page[j];
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize reference elements in change conditions ------------
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265
3266   // ---------- initialize run-time trigger player and element ----------------
3267
3268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       struct ElementChangeInfo *change = &ei->change_page[j];
3275
3276       change->actual_trigger_element = EL_EMPTY;
3277       change->actual_trigger_player = EL_EMPTY;
3278       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3279       change->actual_trigger_side = CH_SIDE_NONE;
3280       change->actual_trigger_ce_value = 0;
3281       change->actual_trigger_ce_score = 0;
3282     }
3283   }
3284
3285   // ---------- initialize trigger events -------------------------------------
3286
3287   // initialize trigger events information
3288   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290       trigger_events[i][j] = FALSE;
3291
3292   // add trigger events from element change event properties
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       struct ElementChangeInfo *change = &ei->change_page[j];
3300
3301       if (!change->can_change_or_has_action)
3302         continue;
3303
3304       if (change->has_event[CE_BY_OTHER_ACTION])
3305       {
3306         int trigger_element = change->trigger_element;
3307
3308         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3309         {
3310           if (change->has_event[k])
3311           {
3312             if (IS_GROUP_ELEMENT(trigger_element))
3313             {
3314               struct ElementGroupInfo *group =
3315                 element_info[trigger_element].group;
3316
3317               for (l = 0; l < group->num_elements_resolved; l++)
3318                 trigger_events[group->element_resolved[l]][k] = TRUE;
3319             }
3320             else if (trigger_element == EL_ANY_ELEMENT)
3321               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3322                 trigger_events[l][k] = TRUE;
3323             else
3324               trigger_events[trigger_element][k] = TRUE;
3325           }
3326         }
3327       }
3328     }
3329   }
3330
3331   // ---------- initialize push delay -----------------------------------------
3332
3333   // initialize push delay values to default
3334   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3335   {
3336     if (!IS_CUSTOM_ELEMENT(i))
3337     {
3338       // set default push delay values (corrected since version 3.0.7-1)
3339       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3340       {
3341         element_info[i].push_delay_fixed = 2;
3342         element_info[i].push_delay_random = 8;
3343       }
3344       else
3345       {
3346         element_info[i].push_delay_fixed = 8;
3347         element_info[i].push_delay_random = 8;
3348       }
3349     }
3350   }
3351
3352   // set push delay value for certain elements from pre-defined list
3353   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3354   {
3355     int e = push_delay_list[i].element;
3356
3357     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3358     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3359   }
3360
3361   // set push delay value for Supaplex elements for newer engine versions
3362   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3363   {
3364     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     {
3366       if (IS_SP_ELEMENT(i))
3367       {
3368         // set SP push delay to just enough to push under a falling zonk
3369         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3370
3371         element_info[i].push_delay_fixed  = delay;
3372         element_info[i].push_delay_random = 0;
3373       }
3374     }
3375   }
3376
3377   // ---------- initialize move stepsize --------------------------------------
3378
3379   // initialize move stepsize values to default
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381     if (!IS_CUSTOM_ELEMENT(i))
3382       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3383
3384   // set move stepsize value for certain elements from pre-defined list
3385   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = move_stepsize_list[i].element;
3388
3389     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3390
3391     // set move stepsize value for certain elements for older engine versions
3392     if (use_old_move_stepsize_for_magic_wall)
3393     {
3394       if (e == EL_MAGIC_WALL_FILLING ||
3395           e == EL_MAGIC_WALL_EMPTYING ||
3396           e == EL_BD_MAGIC_WALL_FILLING ||
3397           e == EL_BD_MAGIC_WALL_EMPTYING)
3398         element_info[e].move_stepsize *= 2;
3399     }
3400   }
3401
3402   // ---------- initialize collect score --------------------------------------
3403
3404   // initialize collect score values for custom elements from initial value
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (IS_CUSTOM_ELEMENT(i))
3407       element_info[i].collect_score = element_info[i].collect_score_initial;
3408
3409   // ---------- initialize collect count --------------------------------------
3410
3411   // initialize collect count values for non-custom elements
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].collect_count_initial = 0;
3415
3416   // add collect count values for all elements from pre-defined list
3417   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3418     element_info[collect_count_list[i].element].collect_count_initial =
3419       collect_count_list[i].count;
3420
3421   // ---------- initialize access direction -----------------------------------
3422
3423   // initialize access direction values to default (access from every side)
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3427
3428   // set access direction value for certain elements from pre-defined list
3429   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3430     element_info[access_direction_list[i].element].access_direction =
3431       access_direction_list[i].direction;
3432
3433   // ---------- initialize explosion content ----------------------------------
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435   {
3436     if (IS_CUSTOM_ELEMENT(i))
3437       continue;
3438
3439     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3440     {
3441       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3442
3443       element_info[i].content.e[x][y] =
3444         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3445          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3446          i == EL_PLAYER_3 ? EL_EMERALD :
3447          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3448          i == EL_MOLE ? EL_EMERALD_RED :
3449          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3450          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3451          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3452          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3453          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3454          i == EL_WALL_EMERALD ? EL_EMERALD :
3455          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3456          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3457          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3458          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3459          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3460          i == EL_WALL_PEARL ? EL_PEARL :
3461          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3462          EL_EMPTY);
3463     }
3464   }
3465
3466   // ---------- initialize recursion detection --------------------------------
3467   recursion_loop_depth = 0;
3468   recursion_loop_detected = FALSE;
3469   recursion_loop_element = EL_UNDEFINED;
3470
3471   // ---------- initialize graphics engine ------------------------------------
3472   game.scroll_delay_value =
3473     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3474      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3475      !setup.forced_scroll_delay           ? 0 :
3476      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3477   if (game.forced_scroll_delay_value == -1)
3478     game.scroll_delay_value =
3479       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3480
3481   // ---------- initialize game engine snapshots ------------------------------
3482   for (i = 0; i < MAX_PLAYERS; i++)
3483     game.snapshot.last_action[i] = 0;
3484   game.snapshot.changed_action = FALSE;
3485   game.snapshot.collected_item = FALSE;
3486   game.snapshot.mode =
3487     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3488      SNAPSHOT_MODE_EVERY_STEP :
3489      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3490      SNAPSHOT_MODE_EVERY_MOVE :
3491      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3492      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3493   game.snapshot.save_snapshot = FALSE;
3494
3495   // ---------- initialize level time for Supaplex engine ---------------------
3496   // Supaplex levels with time limit currently unsupported -- should be added
3497   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3498     level.time = 0;
3499
3500   // ---------- initialize flags for handling game actions --------------------
3501
3502   // set flags for game actions to default values
3503   game.use_key_actions = TRUE;
3504   game.use_mouse_actions = FALSE;
3505
3506   // when using Mirror Magic game engine, handle mouse events only
3507   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3508   {
3509     game.use_key_actions = FALSE;
3510     game.use_mouse_actions = TRUE;
3511   }
3512
3513   // check for custom elements with mouse click events
3514   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3515   {
3516     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3517     {
3518       int element = EL_CUSTOM_START + i;
3519
3520       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3521           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3522           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3523           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3524         game.use_mouse_actions = TRUE;
3525     }
3526   }
3527 }
3528
3529 static int get_num_special_action(int element, int action_first,
3530                                   int action_last)
3531 {
3532   int num_special_action = 0;
3533   int i, j;
3534
3535   for (i = action_first; i <= action_last; i++)
3536   {
3537     boolean found = FALSE;
3538
3539     for (j = 0; j < NUM_DIRECTIONS; j++)
3540       if (el_act_dir2img(element, i, j) !=
3541           el_act_dir2img(element, ACTION_DEFAULT, j))
3542         found = TRUE;
3543
3544     if (found)
3545       num_special_action++;
3546     else
3547       break;
3548   }
3549
3550   return num_special_action;
3551 }
3552
3553
3554 // ============================================================================
3555 // InitGame()
3556 // ----------------------------------------------------------------------------
3557 // initialize and start new game
3558 // ============================================================================
3559
3560 #if DEBUG_INIT_PLAYER
3561 static void DebugPrintPlayerStatus(char *message)
3562 {
3563   int i;
3564
3565   if (!options.debug)
3566     return;
3567
3568   Debug("game:init:player", "%s:", message);
3569
3570   for (i = 0; i < MAX_PLAYERS; i++)
3571   {
3572     struct PlayerInfo *player = &stored_player[i];
3573
3574     Debug("game:init:player",
3575           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3576           i + 1,
3577           player->present,
3578           player->connected,
3579           player->connected_locally,
3580           player->connected_network,
3581           player->active,
3582           (local_player == player ? " (local player)" : ""));
3583   }
3584 }
3585 #endif
3586
3587 void InitGame(void)
3588 {
3589   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3590   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3591   int fade_mask = REDRAW_FIELD;
3592   boolean restarting = (game_status == GAME_MODE_PLAYING);
3593   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3594   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3595   int initial_move_dir = MV_DOWN;
3596   int i, j, x, y;
3597
3598   // required here to update video display before fading (FIX THIS)
3599   DrawMaskedBorder(REDRAW_DOOR_2);
3600
3601   if (!game.restart_level)
3602     CloseDoor(DOOR_CLOSE_1);
3603
3604   if (restarting)
3605   {
3606     // force fading out global animations displayed during game play
3607     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3608   }
3609   else
3610   {
3611     SetGameStatus(GAME_MODE_PLAYING);
3612   }
3613
3614   if (level_editor_test_game)
3615     FadeSkipNextFadeOut();
3616   else
3617     FadeSetEnterScreen();
3618
3619   if (CheckFadeAll())
3620     fade_mask = REDRAW_ALL;
3621
3622   FadeLevelSoundsAndMusic();
3623
3624   ExpireSoundLoops(TRUE);
3625
3626   FadeOut(fade_mask);
3627
3628   if (restarting)
3629   {
3630     // force restarting global animations displayed during game play
3631     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3632
3633     // this is required for "transforming" fade modes like cross-fading
3634     // (else global animations will be stopped, but not restarted here)
3635     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3636
3637     SetGameStatus(GAME_MODE_PLAYING);
3638   }
3639
3640   if (level_editor_test_game)
3641     FadeSkipNextFadeIn();
3642
3643   // needed if different viewport properties defined for playing
3644   ChangeViewportPropertiesIfNeeded();
3645
3646   ClearField();
3647
3648   DrawCompleteVideoDisplay();
3649
3650   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3651
3652   InitGameEngine();
3653   InitGameControlValues();
3654
3655   if (tape.recording)
3656   {
3657     // initialize tape actions from game when recording tape
3658     tape.use_key_actions   = game.use_key_actions;
3659     tape.use_mouse_actions = game.use_mouse_actions;
3660
3661     // initialize visible playfield size when recording tape (for team mode)
3662     tape.scr_fieldx = SCR_FIELDX;
3663     tape.scr_fieldy = SCR_FIELDY;
3664   }
3665
3666   // don't play tapes over network
3667   network_playing = (network.enabled && !tape.playing);
3668
3669   for (i = 0; i < MAX_PLAYERS; i++)
3670   {
3671     struct PlayerInfo *player = &stored_player[i];
3672
3673     player->index_nr = i;
3674     player->index_bit = (1 << i);
3675     player->element_nr = EL_PLAYER_1 + i;
3676
3677     player->present = FALSE;
3678     player->active = FALSE;
3679     player->mapped = FALSE;
3680
3681     player->killed = FALSE;
3682     player->reanimated = FALSE;
3683     player->buried = FALSE;
3684
3685     player->action = 0;
3686     player->effective_action = 0;
3687     player->programmed_action = 0;
3688     player->snap_action = 0;
3689
3690     player->mouse_action.lx = 0;
3691     player->mouse_action.ly = 0;
3692     player->mouse_action.button = 0;
3693     player->mouse_action.button_hint = 0;
3694
3695     player->effective_mouse_action.lx = 0;
3696     player->effective_mouse_action.ly = 0;
3697     player->effective_mouse_action.button = 0;
3698     player->effective_mouse_action.button_hint = 0;
3699
3700     for (j = 0; j < MAX_NUM_KEYS; j++)
3701       player->key[j] = FALSE;
3702
3703     player->num_white_keys = 0;
3704
3705     player->dynabomb_count = 0;
3706     player->dynabomb_size = 1;
3707     player->dynabombs_left = 0;
3708     player->dynabomb_xl = FALSE;
3709
3710     player->MovDir = initial_move_dir;
3711     player->MovPos = 0;
3712     player->GfxPos = 0;
3713     player->GfxDir = initial_move_dir;
3714     player->GfxAction = ACTION_DEFAULT;
3715     player->Frame = 0;
3716     player->StepFrame = 0;
3717
3718     player->initial_element = player->element_nr;
3719     player->artwork_element =
3720       (level.use_artwork_element[i] ? level.artwork_element[i] :
3721        player->element_nr);
3722     player->use_murphy = FALSE;
3723
3724     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3725     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3726
3727     player->gravity = level.initial_player_gravity[i];
3728
3729     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3730
3731     player->actual_frame_counter.count = 0;
3732     player->actual_frame_counter.value = 1;
3733
3734     player->step_counter = 0;
3735
3736     player->last_move_dir = initial_move_dir;
3737
3738     player->is_active = FALSE;
3739
3740     player->is_waiting = FALSE;
3741     player->is_moving = FALSE;
3742     player->is_auto_moving = FALSE;
3743     player->is_digging = FALSE;
3744     player->is_snapping = FALSE;
3745     player->is_collecting = FALSE;
3746     player->is_pushing = FALSE;
3747     player->is_switching = FALSE;
3748     player->is_dropping = FALSE;
3749     player->is_dropping_pressed = FALSE;
3750
3751     player->is_bored = FALSE;
3752     player->is_sleeping = FALSE;
3753
3754     player->was_waiting = TRUE;
3755     player->was_moving = FALSE;
3756     player->was_snapping = FALSE;
3757     player->was_dropping = FALSE;
3758
3759     player->force_dropping = FALSE;
3760
3761     player->frame_counter_bored = -1;
3762     player->frame_counter_sleeping = -1;
3763
3764     player->anim_delay_counter = 0;
3765     player->post_delay_counter = 0;
3766
3767     player->dir_waiting = initial_move_dir;
3768     player->action_waiting = ACTION_DEFAULT;
3769     player->last_action_waiting = ACTION_DEFAULT;
3770     player->special_action_bored = ACTION_DEFAULT;
3771     player->special_action_sleeping = ACTION_DEFAULT;
3772
3773     player->switch_x = -1;
3774     player->switch_y = -1;
3775
3776     player->drop_x = -1;
3777     player->drop_y = -1;
3778
3779     player->show_envelope = 0;
3780
3781     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3782
3783     player->push_delay       = -1;      // initialized when pushing starts
3784     player->push_delay_value = game.initial_push_delay_value;
3785
3786     player->drop_delay = 0;
3787     player->drop_pressed_delay = 0;
3788
3789     player->last_jx = -1;
3790     player->last_jy = -1;
3791     player->jx = -1;
3792     player->jy = -1;
3793
3794     player->shield_normal_time_left = 0;
3795     player->shield_deadly_time_left = 0;
3796
3797     player->last_removed_element = EL_UNDEFINED;
3798
3799     player->inventory_infinite_element = EL_UNDEFINED;
3800     player->inventory_size = 0;
3801
3802     if (level.use_initial_inventory[i])
3803     {
3804       for (j = 0; j < level.initial_inventory_size[i]; j++)
3805       {
3806         int element = level.initial_inventory_content[i][j];
3807         int collect_count = element_info[element].collect_count_initial;
3808         int k;
3809
3810         if (!IS_CUSTOM_ELEMENT(element))
3811           collect_count = 1;
3812
3813         if (collect_count == 0)
3814           player->inventory_infinite_element = element;
3815         else
3816           for (k = 0; k < collect_count; k++)
3817             if (player->inventory_size < MAX_INVENTORY_SIZE)
3818               player->inventory_element[player->inventory_size++] = element;
3819       }
3820     }
3821
3822     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3823     SnapField(player, 0, 0);
3824
3825     map_player_action[i] = i;
3826   }
3827
3828   network_player_action_received = FALSE;
3829
3830   // initial null action
3831   if (network_playing)
3832     SendToServer_MovePlayer(MV_NONE);
3833
3834   FrameCounter = 0;
3835   TimeFrames = 0;
3836   TimePlayed = 0;
3837   TimeLeft = level.time;
3838   TapeTime = 0;
3839
3840   ScreenMovDir = MV_NONE;
3841   ScreenMovPos = 0;
3842   ScreenGfxPos = 0;
3843
3844   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3845
3846   game.robot_wheel_x = -1;
3847   game.robot_wheel_y = -1;
3848
3849   game.exit_x = -1;
3850   game.exit_y = -1;
3851
3852   game.all_players_gone = FALSE;
3853
3854   game.LevelSolved = FALSE;
3855   game.GameOver = FALSE;
3856
3857   game.GamePlayed = !tape.playing;
3858
3859   game.LevelSolved_GameWon = FALSE;
3860   game.LevelSolved_GameEnd = FALSE;
3861   game.LevelSolved_SaveTape = FALSE;
3862   game.LevelSolved_SaveScore = FALSE;
3863
3864   game.LevelSolved_CountingTime = 0;
3865   game.LevelSolved_CountingScore = 0;
3866   game.LevelSolved_CountingHealth = 0;
3867
3868   game.panel.active = TRUE;
3869
3870   game.no_level_time_limit = (level.time == 0);
3871   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3872
3873   game.yamyam_content_nr = 0;
3874   game.robot_wheel_active = FALSE;
3875   game.magic_wall_active = FALSE;
3876   game.magic_wall_time_left = 0;
3877   game.light_time_left = 0;
3878   game.timegate_time_left = 0;
3879   game.switchgate_pos = 0;
3880   game.wind_direction = level.wind_direction_initial;
3881
3882   game.time_final = 0;
3883   game.score_time_final = 0;
3884
3885   game.score = 0;
3886   game.score_final = 0;
3887
3888   game.health = MAX_HEALTH;
3889   game.health_final = MAX_HEALTH;
3890
3891   game.gems_still_needed = level.gems_needed;
3892   game.sokoban_fields_still_needed = 0;
3893   game.sokoban_objects_still_needed = 0;
3894   game.lights_still_needed = 0;
3895   game.players_still_needed = 0;
3896   game.friends_still_needed = 0;
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_active = level.ball_active_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.explosions_delayed = TRUE;
3905
3906   game.envelope_active = FALSE;
3907
3908   // special case: set custom artwork setting to initial value
3909   game.use_masked_elements = game.use_masked_elements_initial;
3910
3911   for (i = 0; i < NUM_BELTS; i++)
3912   {
3913     game.belt_dir[i] = MV_NONE;
3914     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3915   }
3916
3917   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3918     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3919
3920 #if DEBUG_INIT_PLAYER
3921   DebugPrintPlayerStatus("Player status at level initialization");
3922 #endif
3923
3924   SCAN_PLAYFIELD(x, y)
3925   {
3926     Tile[x][y] = Last[x][y] = level.field[x][y];
3927     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3928     ChangeDelay[x][y] = 0;
3929     ChangePage[x][y] = -1;
3930     CustomValue[x][y] = 0;              // initialized in InitField()
3931     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3932     AmoebaNr[x][y] = 0;
3933     WasJustMoving[x][y] = 0;
3934     WasJustFalling[x][y] = 0;
3935     CheckCollision[x][y] = 0;
3936     CheckImpact[x][y] = 0;
3937     Stop[x][y] = FALSE;
3938     Pushed[x][y] = FALSE;
3939
3940     ChangeCount[x][y] = 0;
3941     ChangeEvent[x][y] = -1;
3942
3943     ExplodePhase[x][y] = 0;
3944     ExplodeDelay[x][y] = 0;
3945     ExplodeField[x][y] = EX_TYPE_NONE;
3946
3947     RunnerVisit[x][y] = 0;
3948     PlayerVisit[x][y] = 0;
3949
3950     GfxFrame[x][y] = 0;
3951     GfxRandom[x][y] = INIT_GFX_RANDOM();
3952     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3953     GfxElement[x][y] = EL_UNDEFINED;
3954     GfxElementEmpty[x][y] = EL_EMPTY;
3955     GfxAction[x][y] = ACTION_DEFAULT;
3956     GfxDir[x][y] = MV_NONE;
3957     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3958   }
3959
3960   SCAN_PLAYFIELD(x, y)
3961   {
3962     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3963       emulate_bd = FALSE;
3964     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3965       emulate_sp = FALSE;
3966
3967     InitField(x, y, TRUE);
3968
3969     ResetGfxAnimation(x, y);
3970   }
3971
3972   InitBeltMovement();
3973
3974   for (i = 0; i < MAX_PLAYERS; i++)
3975   {
3976     struct PlayerInfo *player = &stored_player[i];
3977
3978     // set number of special actions for bored and sleeping animation
3979     player->num_special_action_bored =
3980       get_num_special_action(player->artwork_element,
3981                              ACTION_BORING_1, ACTION_BORING_LAST);
3982     player->num_special_action_sleeping =
3983       get_num_special_action(player->artwork_element,
3984                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3985   }
3986
3987   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3988                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3989
3990   // initialize type of slippery elements
3991   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3992   {
3993     if (!IS_CUSTOM_ELEMENT(i))
3994     {
3995       // default: elements slip down either to the left or right randomly
3996       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3997
3998       // SP style elements prefer to slip down on the left side
3999       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4000         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4001
4002       // BD style elements prefer to slip down on the left side
4003       if (game.emulation == EMU_BOULDERDASH)
4004         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4005     }
4006   }
4007
4008   // initialize explosion and ignition delay
4009   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4010   {
4011     if (!IS_CUSTOM_ELEMENT(i))
4012     {
4013       int num_phase = 8;
4014       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4015                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4016                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4017       int last_phase = (num_phase + 1) * delay;
4018       int half_phase = (num_phase / 2) * delay;
4019
4020       element_info[i].explosion_delay = last_phase - 1;
4021       element_info[i].ignition_delay = half_phase;
4022
4023       if (i == EL_BLACK_ORB)
4024         element_info[i].ignition_delay = 1;
4025     }
4026   }
4027
4028   // correct non-moving belts to start moving left
4029   for (i = 0; i < NUM_BELTS; i++)
4030     if (game.belt_dir[i] == MV_NONE)
4031       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4032
4033 #if USE_NEW_PLAYER_ASSIGNMENTS
4034   // use preferred player also in local single-player mode
4035   if (!network.enabled && !game.team_mode)
4036   {
4037     int new_index_nr = setup.network_player_nr;
4038
4039     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4040     {
4041       for (i = 0; i < MAX_PLAYERS; i++)
4042         stored_player[i].connected_locally = FALSE;
4043
4044       stored_player[new_index_nr].connected_locally = TRUE;
4045     }
4046   }
4047
4048   for (i = 0; i < MAX_PLAYERS; i++)
4049   {
4050     stored_player[i].connected = FALSE;
4051
4052     // in network game mode, the local player might not be the first player
4053     if (stored_player[i].connected_locally)
4054       local_player = &stored_player[i];
4055   }
4056
4057   if (!network.enabled)
4058     local_player->connected = TRUE;
4059
4060   if (tape.playing)
4061   {
4062     for (i = 0; i < MAX_PLAYERS; i++)
4063       stored_player[i].connected = tape.player_participates[i];
4064   }
4065   else if (network.enabled)
4066   {
4067     // add team mode players connected over the network (needed for correct
4068     // assignment of player figures from level to locally playing players)
4069
4070     for (i = 0; i < MAX_PLAYERS; i++)
4071       if (stored_player[i].connected_network)
4072         stored_player[i].connected = TRUE;
4073   }
4074   else if (game.team_mode)
4075   {
4076     // try to guess locally connected team mode players (needed for correct
4077     // assignment of player figures from level to locally playing players)
4078
4079     for (i = 0; i < MAX_PLAYERS; i++)
4080       if (setup.input[i].use_joystick ||
4081           setup.input[i].key.left != KSYM_UNDEFINED)
4082         stored_player[i].connected = TRUE;
4083   }
4084
4085 #if DEBUG_INIT_PLAYER
4086   DebugPrintPlayerStatus("Player status after level initialization");
4087 #endif
4088
4089 #if DEBUG_INIT_PLAYER
4090   Debug("game:init:player", "Reassigning players ...");
4091 #endif
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       struct PlayerInfo *field_player = NULL;
4101
4102 #if DEBUG_INIT_PLAYER
4103       Debug("game:init:player",
4104             "- looking for field player for player %d ...", i + 1);
4105 #endif
4106
4107       // assign first free player found that is present in the playfield
4108
4109       // first try: look for unmapped playfield player that is not connected
4110       for (j = 0; j < MAX_PLAYERS; j++)
4111         if (field_player == NULL &&
4112             stored_player[j].present &&
4113             !stored_player[j].mapped &&
4114             !stored_player[j].connected)
4115           field_player = &stored_player[j];
4116
4117       // second try: look for *any* unmapped playfield player
4118       for (j = 0; j < MAX_PLAYERS; j++)
4119         if (field_player == NULL &&
4120             stored_player[j].present &&
4121             !stored_player[j].mapped)
4122           field_player = &stored_player[j];
4123
4124       if (field_player != NULL)
4125       {
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128 #if DEBUG_INIT_PLAYER
4129         Debug("game:init:player", "- found player %d",
4130               field_player->index_nr + 1);
4131 #endif
4132
4133         player->present = FALSE;
4134         player->active = FALSE;
4135
4136         field_player->present = TRUE;
4137         field_player->active = TRUE;
4138
4139         /*
4140         player->initial_element = field_player->initial_element;
4141         player->artwork_element = field_player->artwork_element;
4142
4143         player->block_last_field       = field_player->block_last_field;
4144         player->block_delay_adjustment = field_player->block_delay_adjustment;
4145         */
4146
4147         StorePlayer[jx][jy] = field_player->element_nr;
4148
4149         field_player->jx = field_player->last_jx = jx;
4150         field_player->jy = field_player->last_jy = jy;
4151
4152         if (local_player == player)
4153           local_player = field_player;
4154
4155         map_player_action[field_player->index_nr] = i;
4156
4157         field_player->mapped = TRUE;
4158
4159 #if DEBUG_INIT_PLAYER
4160         Debug("game:init:player", "- map_player_action[%d] == %d",
4161               field_player->index_nr + 1, i + 1);
4162 #endif
4163       }
4164     }
4165
4166     if (player->connected && player->present)
4167       player->mapped = TRUE;
4168   }
4169
4170 #if DEBUG_INIT_PLAYER
4171   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4172 #endif
4173
4174 #else
4175
4176   // check if any connected player was not found in playfield
4177   for (i = 0; i < MAX_PLAYERS; i++)
4178   {
4179     struct PlayerInfo *player = &stored_player[i];
4180
4181     if (player->connected && !player->present)
4182     {
4183       for (j = 0; j < MAX_PLAYERS; j++)
4184       {
4185         struct PlayerInfo *field_player = &stored_player[j];
4186         int jx = field_player->jx, jy = field_player->jy;
4187
4188         // assign first free player found that is present in the playfield
4189         if (field_player->present && !field_player->connected)
4190         {
4191           player->present = TRUE;
4192           player->active = TRUE;
4193
4194           field_player->present = FALSE;
4195           field_player->active = FALSE;
4196
4197           player->initial_element = field_player->initial_element;
4198           player->artwork_element = field_player->artwork_element;
4199
4200           player->block_last_field       = field_player->block_last_field;
4201           player->block_delay_adjustment = field_player->block_delay_adjustment;
4202
4203           StorePlayer[jx][jy] = player->element_nr;
4204
4205           player->jx = player->last_jx = jx;
4206           player->jy = player->last_jy = jy;
4207
4208           break;
4209         }
4210       }
4211     }
4212   }
4213 #endif
4214
4215 #if 0
4216   Debug("game:init:player", "local_player->present == %d",
4217         local_player->present);
4218 #endif
4219
4220   // set focus to local player for network games, else to all players
4221   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4222   game.centered_player_nr_next = game.centered_player_nr;
4223   game.set_centered_player = FALSE;
4224   game.set_centered_player_wrap = FALSE;
4225
4226   if (network_playing && tape.recording)
4227   {
4228     // store client dependent player focus when recording network games
4229     tape.centered_player_nr_next = game.centered_player_nr_next;
4230     tape.set_centered_player = TRUE;
4231   }
4232
4233   if (tape.playing)
4234   {
4235     // when playing a tape, eliminate all players who do not participate
4236
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238
4239     if (!game.team_mode)
4240     {
4241       for (i = 0; i < MAX_PLAYERS; i++)
4242       {
4243         if (stored_player[i].active &&
4244             !tape.player_participates[map_player_action[i]])
4245         {
4246           struct PlayerInfo *player = &stored_player[i];
4247           int jx = player->jx, jy = player->jy;
4248
4249 #if DEBUG_INIT_PLAYER
4250           Debug("game:init:player", "Removing player %d at (%d, %d)",
4251                 i + 1, jx, jy);
4252 #endif
4253
4254           player->active = FALSE;
4255           StorePlayer[jx][jy] = 0;
4256           Tile[jx][jy] = EL_EMPTY;
4257         }
4258       }
4259     }
4260
4261 #else
4262
4263     for (i = 0; i < MAX_PLAYERS; i++)
4264     {
4265       if (stored_player[i].active &&
4266           !tape.player_participates[i])
4267       {
4268         struct PlayerInfo *player = &stored_player[i];
4269         int jx = player->jx, jy = player->jy;
4270
4271         player->active = FALSE;
4272         StorePlayer[jx][jy] = 0;
4273         Tile[jx][jy] = EL_EMPTY;
4274       }
4275     }
4276 #endif
4277   }
4278   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4279   {
4280     // when in single player mode, eliminate all but the local player
4281
4282     for (i = 0; i < MAX_PLAYERS; i++)
4283     {
4284       struct PlayerInfo *player = &stored_player[i];
4285
4286       if (player->active && player != local_player)
4287       {
4288         int jx = player->jx, jy = player->jy;
4289
4290         player->active = FALSE;
4291         player->present = FALSE;
4292
4293         StorePlayer[jx][jy] = 0;
4294         Tile[jx][jy] = EL_EMPTY;
4295       }
4296     }
4297   }
4298
4299   for (i = 0; i < MAX_PLAYERS; i++)
4300     if (stored_player[i].active)
4301       game.players_still_needed++;
4302
4303   if (level.solved_by_one_player)
4304     game.players_still_needed = 1;
4305
4306   // when recording the game, store which players take part in the game
4307   if (tape.recording)
4308   {
4309 #if USE_NEW_PLAYER_ASSIGNMENTS
4310     for (i = 0; i < MAX_PLAYERS; i++)
4311       if (stored_player[i].connected)
4312         tape.player_participates[i] = TRUE;
4313 #else
4314     for (i = 0; i < MAX_PLAYERS; i++)
4315       if (stored_player[i].active)
4316         tape.player_participates[i] = TRUE;
4317 #endif
4318   }
4319
4320 #if DEBUG_INIT_PLAYER
4321   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4322 #endif
4323
4324   if (BorderElement == EL_EMPTY)
4325   {
4326     SBX_Left = 0;
4327     SBX_Right = lev_fieldx - SCR_FIELDX;
4328     SBY_Upper = 0;
4329     SBY_Lower = lev_fieldy - SCR_FIELDY;
4330   }
4331   else
4332   {
4333     SBX_Left = -1;
4334     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4335     SBY_Upper = -1;
4336     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4337   }
4338
4339   if (full_lev_fieldx <= SCR_FIELDX)
4340     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4341   if (full_lev_fieldy <= SCR_FIELDY)
4342     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4343
4344   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4345     SBX_Left--;
4346   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4347     SBY_Upper--;
4348
4349   // if local player not found, look for custom element that might create
4350   // the player (make some assumptions about the right custom element)
4351   if (!local_player->present)
4352   {
4353     int start_x = 0, start_y = 0;
4354     int found_rating = 0;
4355     int found_element = EL_UNDEFINED;
4356     int player_nr = local_player->index_nr;
4357
4358     SCAN_PLAYFIELD(x, y)
4359     {
4360       int element = Tile[x][y];
4361       int content;
4362       int xx, yy;
4363       boolean is_player;
4364
4365       if (level.use_start_element[player_nr] &&
4366           level.start_element[player_nr] == element &&
4367           found_rating < 4)
4368       {
4369         start_x = x;
4370         start_y = y;
4371
4372         found_rating = 4;
4373         found_element = element;
4374       }
4375
4376       if (!IS_CUSTOM_ELEMENT(element))
4377         continue;
4378
4379       if (CAN_CHANGE(element))
4380       {
4381         for (i = 0; i < element_info[element].num_change_pages; i++)
4382         {
4383           // check for player created from custom element as single target
4384           content = element_info[element].change_page[i].target_element;
4385           is_player = IS_PLAYER_ELEMENT(content);
4386
4387           if (is_player && (found_rating < 3 ||
4388                             (found_rating == 3 && element < found_element)))
4389           {
4390             start_x = x;
4391             start_y = y;
4392
4393             found_rating = 3;
4394             found_element = element;
4395           }
4396         }
4397       }
4398
4399       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4400       {
4401         // check for player created from custom element as explosion content
4402         content = element_info[element].content.e[xx][yy];
4403         is_player = IS_PLAYER_ELEMENT(content);
4404
4405         if (is_player && (found_rating < 2 ||
4406                           (found_rating == 2 && element < found_element)))
4407         {
4408           start_x = x + xx - 1;
4409           start_y = y + yy - 1;
4410
4411           found_rating = 2;
4412           found_element = element;
4413         }
4414
4415         if (!CAN_CHANGE(element))
4416           continue;
4417
4418         for (i = 0; i < element_info[element].num_change_pages; i++)
4419         {
4420           // check for player created from custom element as extended target
4421           content =
4422             element_info[element].change_page[i].target_content.e[xx][yy];
4423
4424           is_player = IS_PLAYER_ELEMENT(content);
4425
4426           if (is_player && (found_rating < 1 ||
4427                             (found_rating == 1 && element < found_element)))
4428           {
4429             start_x = x + xx - 1;
4430             start_y = y + yy - 1;
4431
4432             found_rating = 1;
4433             found_element = element;
4434           }
4435         }
4436       }
4437     }
4438
4439     scroll_x = SCROLL_POSITION_X(start_x);
4440     scroll_y = SCROLL_POSITION_Y(start_y);
4441   }
4442   else
4443   {
4444     scroll_x = SCROLL_POSITION_X(local_player->jx);
4445     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4446   }
4447
4448   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4449     scroll_x = game.forced_scroll_x;
4450   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4451     scroll_y = game.forced_scroll_y;
4452
4453   // !!! FIX THIS (START) !!!
4454   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4455   {
4456     InitGameEngine_EM();
4457   }
4458   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4459   {
4460     InitGameEngine_SP();
4461   }
4462   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4463   {
4464     InitGameEngine_MM();
4465   }
4466   else
4467   {
4468     DrawLevel(REDRAW_FIELD);
4469     DrawAllPlayers();
4470
4471     // after drawing the level, correct some elements
4472     if (game.timegate_time_left == 0)
4473       CloseAllOpenTimegates();
4474   }
4475
4476   // blit playfield from scroll buffer to normal back buffer for fading in
4477   BlitScreenToBitmap(backbuffer);
4478   // !!! FIX THIS (END) !!!
4479
4480   DrawMaskedBorder(fade_mask);
4481
4482   FadeIn(fade_mask);
4483
4484 #if 1
4485   // full screen redraw is required at this point in the following cases:
4486   // - special editor door undrawn when game was started from level editor
4487   // - drawing area (playfield) was changed and has to be removed completely
4488   redraw_mask = REDRAW_ALL;
4489   BackToFront();
4490 #endif
4491
4492   if (!game.restart_level)
4493   {
4494     // copy default game door content to main double buffer
4495
4496     // !!! CHECK AGAIN !!!
4497     SetPanelBackground();
4498     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4499     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4500   }
4501
4502   SetPanelBackground();
4503   SetDrawBackgroundMask(REDRAW_DOOR_1);
4504
4505   UpdateAndDisplayGameControlValues();
4506
4507   if (!game.restart_level)
4508   {
4509     UnmapGameButtons();
4510     UnmapTapeButtons();
4511
4512     FreeGameButtons();
4513     CreateGameButtons();
4514
4515     MapGameButtons();
4516     MapTapeButtons();
4517
4518     // copy actual game door content to door double buffer for OpenDoor()
4519     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4520
4521     OpenDoor(DOOR_OPEN_ALL);
4522
4523     KeyboardAutoRepeatOffUnlessAutoplay();
4524
4525 #if DEBUG_INIT_PLAYER
4526     DebugPrintPlayerStatus("Player status (final)");
4527 #endif
4528   }
4529
4530   UnmapAllGadgets();
4531
4532   MapGameButtons();
4533   MapTapeButtons();
4534
4535   if (!game.restart_level && !tape.playing)
4536   {
4537     LevelStats_incPlayed(level_nr);
4538
4539     SaveLevelSetup_SeriesInfo();
4540   }
4541
4542   game.restart_level = FALSE;
4543
4544   game.request_active = FALSE;
4545   game.request_active_or_moving = FALSE;
4546
4547   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4548     InitGameActions_MM();
4549
4550   SaveEngineSnapshotToListInitial();
4551
4552   if (!game.restart_level)
4553   {
4554     PlaySound(SND_GAME_STARTING);
4555
4556     if (setup.sound_music)
4557       PlayLevelMusic();
4558   }
4559
4560   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4561 }
4562
4563 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4564                         int actual_player_x, int actual_player_y)
4565 {
4566   // this is used for non-R'n'D game engines to update certain engine values
4567
4568   // needed to determine if sounds are played within the visible screen area
4569   scroll_x = actual_scroll_x;
4570   scroll_y = actual_scroll_y;
4571
4572   // needed to get player position for "follow finger" playing input method
4573   local_player->jx = actual_player_x;
4574   local_player->jy = actual_player_y;
4575 }
4576
4577 void InitMovDir(int x, int y)
4578 {
4579   int i, element = Tile[x][y];
4580   static int xy[4][2] =
4581   {
4582     {  0, +1 },
4583     { +1,  0 },
4584     {  0, -1 },
4585     { -1,  0 }
4586   };
4587   static int direction[3][4] =
4588   {
4589     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4590     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4591     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4592   };
4593
4594   switch (element)
4595   {
4596     case EL_BUG_RIGHT:
4597     case EL_BUG_UP:
4598     case EL_BUG_LEFT:
4599     case EL_BUG_DOWN:
4600       Tile[x][y] = EL_BUG;
4601       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4602       break;
4603
4604     case EL_SPACESHIP_RIGHT:
4605     case EL_SPACESHIP_UP:
4606     case EL_SPACESHIP_LEFT:
4607     case EL_SPACESHIP_DOWN:
4608       Tile[x][y] = EL_SPACESHIP;
4609       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4610       break;
4611
4612     case EL_BD_BUTTERFLY_RIGHT:
4613     case EL_BD_BUTTERFLY_UP:
4614     case EL_BD_BUTTERFLY_LEFT:
4615     case EL_BD_BUTTERFLY_DOWN:
4616       Tile[x][y] = EL_BD_BUTTERFLY;
4617       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4618       break;
4619
4620     case EL_BD_FIREFLY_RIGHT:
4621     case EL_BD_FIREFLY_UP:
4622     case EL_BD_FIREFLY_LEFT:
4623     case EL_BD_FIREFLY_DOWN:
4624       Tile[x][y] = EL_BD_FIREFLY;
4625       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4626       break;
4627
4628     case EL_PACMAN_RIGHT:
4629     case EL_PACMAN_UP:
4630     case EL_PACMAN_LEFT:
4631     case EL_PACMAN_DOWN:
4632       Tile[x][y] = EL_PACMAN;
4633       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4634       break;
4635
4636     case EL_YAMYAM_LEFT:
4637     case EL_YAMYAM_RIGHT:
4638     case EL_YAMYAM_UP:
4639     case EL_YAMYAM_DOWN:
4640       Tile[x][y] = EL_YAMYAM;
4641       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4642       break;
4643
4644     case EL_SP_SNIKSNAK:
4645       MovDir[x][y] = MV_UP;
4646       break;
4647
4648     case EL_SP_ELECTRON:
4649       MovDir[x][y] = MV_LEFT;
4650       break;
4651
4652     case EL_MOLE_LEFT:
4653     case EL_MOLE_RIGHT:
4654     case EL_MOLE_UP:
4655     case EL_MOLE_DOWN:
4656       Tile[x][y] = EL_MOLE;
4657       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4658       break;
4659
4660     case EL_SPRING_LEFT:
4661     case EL_SPRING_RIGHT:
4662       Tile[x][y] = EL_SPRING;
4663       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4664       break;
4665
4666     default:
4667       if (IS_CUSTOM_ELEMENT(element))
4668       {
4669         struct ElementInfo *ei = &element_info[element];
4670         int move_direction_initial = ei->move_direction_initial;
4671         int move_pattern = ei->move_pattern;
4672
4673         if (move_direction_initial == MV_START_PREVIOUS)
4674         {
4675           if (MovDir[x][y] != MV_NONE)
4676             return;
4677
4678           move_direction_initial = MV_START_AUTOMATIC;
4679         }
4680
4681         if (move_direction_initial == MV_START_RANDOM)
4682           MovDir[x][y] = 1 << RND(4);
4683         else if (move_direction_initial & MV_ANY_DIRECTION)
4684           MovDir[x][y] = move_direction_initial;
4685         else if (move_pattern == MV_ALL_DIRECTIONS ||
4686                  move_pattern == MV_TURNING_LEFT ||
4687                  move_pattern == MV_TURNING_RIGHT ||
4688                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4689                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4690                  move_pattern == MV_TURNING_RANDOM)
4691           MovDir[x][y] = 1 << RND(4);
4692         else if (move_pattern == MV_HORIZONTAL)
4693           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4694         else if (move_pattern == MV_VERTICAL)
4695           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4696         else if (move_pattern & MV_ANY_DIRECTION)
4697           MovDir[x][y] = element_info[element].move_pattern;
4698         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4699                  move_pattern == MV_ALONG_RIGHT_SIDE)
4700         {
4701           // use random direction as default start direction
4702           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4703             MovDir[x][y] = 1 << RND(4);
4704
4705           for (i = 0; i < NUM_DIRECTIONS; i++)
4706           {
4707             int x1 = x + xy[i][0];
4708             int y1 = y + xy[i][1];
4709
4710             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4711             {
4712               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4713                 MovDir[x][y] = direction[0][i];
4714               else
4715                 MovDir[x][y] = direction[1][i];
4716
4717               break;
4718             }
4719           }
4720         }                
4721       }
4722       else
4723       {
4724         MovDir[x][y] = 1 << RND(4);
4725
4726         if (element != EL_BUG &&
4727             element != EL_SPACESHIP &&
4728             element != EL_BD_BUTTERFLY &&
4729             element != EL_BD_FIREFLY)
4730           break;
4731
4732         for (i = 0; i < NUM_DIRECTIONS; i++)
4733         {
4734           int x1 = x + xy[i][0];
4735           int y1 = y + xy[i][1];
4736
4737           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4738           {
4739             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4740             {
4741               MovDir[x][y] = direction[0][i];
4742               break;
4743             }
4744             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4745                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4746             {
4747               MovDir[x][y] = direction[1][i];
4748               break;
4749             }
4750           }
4751         }
4752       }
4753       break;
4754   }
4755
4756   GfxDir[x][y] = MovDir[x][y];
4757 }
4758
4759 void InitAmoebaNr(int x, int y)
4760 {
4761   int i;
4762   int group_nr = AmoebaNeighbourNr(x, y);
4763
4764   if (group_nr == 0)
4765   {
4766     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4767     {
4768       if (AmoebaCnt[i] == 0)
4769       {
4770         group_nr = i;
4771         break;
4772       }
4773     }
4774   }
4775
4776   AmoebaNr[x][y] = group_nr;
4777   AmoebaCnt[group_nr]++;
4778   AmoebaCnt2[group_nr]++;
4779 }
4780
4781 static void LevelSolved_SetFinalGameValues(void)
4782 {
4783   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4784   game.score_time_final = (level.use_step_counter ? TimePlayed :
4785                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4786
4787   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4788                       game_em.lev->score :
4789                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4790                       game_mm.score :
4791                       game.score);
4792
4793   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4794                        MM_HEALTH(game_mm.laser_overload_value) :
4795                        game.health);
4796
4797   game.LevelSolved_CountingTime = game.time_final;
4798   game.LevelSolved_CountingScore = game.score_final;
4799   game.LevelSolved_CountingHealth = game.health_final;
4800 }
4801
4802 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4803 {
4804   game.LevelSolved_CountingTime = time;
4805   game.LevelSolved_CountingScore = score;
4806   game.LevelSolved_CountingHealth = health;
4807
4808   game_panel_controls[GAME_PANEL_TIME].value = time;
4809   game_panel_controls[GAME_PANEL_SCORE].value = score;
4810   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4811
4812   DisplayGameControlValues();
4813 }
4814
4815 static void LevelSolved(void)
4816 {
4817   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4818       game.players_still_needed > 0)
4819     return;
4820
4821   game.LevelSolved = TRUE;
4822   game.GameOver = TRUE;
4823
4824   tape.solved = TRUE;
4825
4826   // needed here to display correct panel values while player walks into exit
4827   LevelSolved_SetFinalGameValues();
4828 }
4829
4830 void GameWon(void)
4831 {
4832   static int time_count_steps;
4833   static int time, time_final;
4834   static float score, score_final; // needed for time score < 10 for 10 seconds
4835   static int health, health_final;
4836   static int game_over_delay_1 = 0;
4837   static int game_over_delay_2 = 0;
4838   static int game_over_delay_3 = 0;
4839   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4840   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4841
4842   if (!game.LevelSolved_GameWon)
4843   {
4844     int i;
4845
4846     // do not start end game actions before the player stops moving (to exit)
4847     if (local_player->active && local_player->MovPos)
4848       return;
4849
4850     // calculate final game values after player finished walking into exit
4851     LevelSolved_SetFinalGameValues();
4852
4853     game.LevelSolved_GameWon = TRUE;
4854     game.LevelSolved_SaveTape = tape.recording;
4855     game.LevelSolved_SaveScore = !tape.playing;
4856
4857     if (!tape.playing)
4858     {
4859       LevelStats_incSolved(level_nr);
4860
4861       SaveLevelSetup_SeriesInfo();
4862     }
4863
4864     if (tape.auto_play)         // tape might already be stopped here
4865       tape.auto_play_level_solved = TRUE;
4866
4867     TapeStop();
4868
4869     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4870     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4871     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4872
4873     time = time_final = game.time_final;
4874     score = score_final = game.score_final;
4875     health = health_final = game.health_final;
4876
4877     // update game panel values before (delayed) counting of score (if any)
4878     LevelSolved_DisplayFinalGameValues(time, score, health);
4879
4880     // if level has time score defined, calculate new final game values
4881     if (time_score > 0)
4882     {
4883       int time_final_max = 999;
4884       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4885       int time_frames = 0;
4886       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4887       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4888
4889       if (TimeLeft > 0)
4890       {
4891         time_final = 0;
4892         time_frames = time_frames_left;
4893       }
4894       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4895       {
4896         time_final = time_final_max;
4897         time_frames = time_frames_final_max - time_frames_played;
4898       }
4899
4900       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4901
4902       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4903
4904       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4905       {
4906         health_final = 0;
4907         score_final += health * time_score;
4908       }
4909
4910       game.score_final = score_final;
4911       game.health_final = health_final;
4912     }
4913
4914     // if not counting score after game, immediately update game panel values
4915     if (level_editor_test_game || !setup.count_score_after_game)
4916     {
4917       time = time_final;
4918       score = score_final;
4919
4920       LevelSolved_DisplayFinalGameValues(time, score, health);
4921     }
4922
4923     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4924     {
4925       // check if last player has left the level
4926       if (game.exit_x >= 0 &&
4927           game.exit_y >= 0)
4928       {
4929         int x = game.exit_x;
4930         int y = game.exit_y;
4931         int element = Tile[x][y];
4932
4933         // close exit door after last player
4934         if ((game.all_players_gone &&
4935              (element == EL_EXIT_OPEN ||
4936               element == EL_SP_EXIT_OPEN ||
4937               element == EL_STEEL_EXIT_OPEN)) ||
4938             element == EL_EM_EXIT_OPEN ||
4939             element == EL_EM_STEEL_EXIT_OPEN)
4940         {
4941
4942           Tile[x][y] =
4943             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4944              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4945              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4946              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4947              EL_EM_STEEL_EXIT_CLOSING);
4948
4949           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4950         }
4951
4952         // player disappears
4953         DrawLevelField(x, y);
4954       }
4955
4956       for (i = 0; i < MAX_PLAYERS; i++)
4957       {
4958         struct PlayerInfo *player = &stored_player[i];
4959
4960         if (player->present)
4961         {
4962           RemovePlayer(player);
4963
4964           // player disappears
4965           DrawLevelField(player->jx, player->jy);
4966         }
4967       }
4968     }
4969
4970     PlaySound(SND_GAME_WINNING);
4971   }
4972
4973   if (setup.count_score_after_game)
4974   {
4975     if (time != time_final)
4976     {
4977       if (game_over_delay_1 > 0)
4978       {
4979         game_over_delay_1--;
4980
4981         return;
4982       }
4983
4984       int time_to_go = ABS(time_final - time);
4985       int time_count_dir = (time < time_final ? +1 : -1);
4986
4987       if (time_to_go < time_count_steps)
4988         time_count_steps = 1;
4989
4990       time  += time_count_steps * time_count_dir;
4991       score += time_count_steps * time_score;
4992
4993       // set final score to correct rounding differences after counting score
4994       if (time == time_final)
4995         score = score_final;
4996
4997       LevelSolved_DisplayFinalGameValues(time, score, health);
4998
4999       if (time == time_final)
5000         StopSound(SND_GAME_LEVELTIME_BONUS);
5001       else if (setup.sound_loops)
5002         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5003       else
5004         PlaySound(SND_GAME_LEVELTIME_BONUS);
5005
5006       return;
5007     }
5008
5009     if (health != health_final)
5010     {
5011       if (game_over_delay_2 > 0)
5012       {
5013         game_over_delay_2--;
5014
5015         return;
5016       }
5017
5018       int health_count_dir = (health < health_final ? +1 : -1);
5019
5020       health += health_count_dir;
5021       score  += time_score;
5022
5023       LevelSolved_DisplayFinalGameValues(time, score, health);
5024
5025       if (health == health_final)
5026         StopSound(SND_GAME_LEVELTIME_BONUS);
5027       else if (setup.sound_loops)
5028         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5029       else
5030         PlaySound(SND_GAME_LEVELTIME_BONUS);
5031
5032       return;
5033     }
5034   }
5035
5036   game.panel.active = FALSE;
5037
5038   if (game_over_delay_3 > 0)
5039   {
5040     game_over_delay_3--;
5041
5042     return;
5043   }
5044
5045   GameEnd();
5046 }
5047
5048 void GameEnd(void)
5049 {
5050   // used instead of "level_nr" (needed for network games)
5051   int last_level_nr = levelset.level_nr;
5052   boolean tape_saved = FALSE;
5053
5054   game.LevelSolved_GameEnd = TRUE;
5055
5056   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5057   {
5058     // make sure that request dialog to save tape does not open door again
5059     if (!global.use_envelope_request)
5060       CloseDoor(DOOR_CLOSE_1);
5061
5062     // ask to save tape
5063     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5064
5065     // set unique basename for score tape (also saved in high score table)
5066     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5067   }
5068
5069   // if no tape is to be saved, close both doors simultaneously
5070   CloseDoor(DOOR_CLOSE_ALL);
5071
5072   if (level_editor_test_game || score_info_tape_play)
5073   {
5074     SetGameStatus(GAME_MODE_MAIN);
5075
5076     DrawMainMenu();
5077
5078     return;
5079   }
5080
5081   if (!game.LevelSolved_SaveScore)
5082   {
5083     SetGameStatus(GAME_MODE_MAIN);
5084
5085     DrawMainMenu();
5086
5087     return;
5088   }
5089
5090   if (level_nr == leveldir_current->handicap_level)
5091   {
5092     leveldir_current->handicap_level++;
5093
5094     SaveLevelSetup_SeriesInfo();
5095   }
5096
5097   // save score and score tape before potentially erasing tape below
5098   NewHighScore(last_level_nr, tape_saved);
5099
5100   if (setup.increment_levels &&
5101       level_nr < leveldir_current->last_level &&
5102       !network_playing)
5103   {
5104     level_nr++;         // advance to next level
5105     TapeErase();        // start with empty tape
5106
5107     if (setup.auto_play_next_level)
5108     {
5109       scores.continue_playing = TRUE;
5110       scores.next_level_nr = level_nr;
5111
5112       LoadLevel(level_nr);
5113
5114       SaveLevelSetup_SeriesInfo();
5115     }
5116   }
5117
5118   if (scores.last_added >= 0 && setup.show_scores_after_game)
5119   {
5120     SetGameStatus(GAME_MODE_SCORES);
5121
5122     DrawHallOfFame(last_level_nr);
5123   }
5124   else if (scores.continue_playing)
5125   {
5126     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5127   }
5128   else
5129   {
5130     SetGameStatus(GAME_MODE_MAIN);
5131
5132     DrawMainMenu();
5133   }
5134 }
5135
5136 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5137                          boolean one_score_entry_per_name)
5138 {
5139   int i;
5140
5141   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5142     return -1;
5143
5144   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5145   {
5146     struct ScoreEntry *entry = &list->entry[i];
5147     boolean score_is_better = (new_entry->score >  entry->score);
5148     boolean score_is_equal  = (new_entry->score == entry->score);
5149     boolean time_is_better  = (new_entry->time  <  entry->time);
5150     boolean time_is_equal   = (new_entry->time  == entry->time);
5151     boolean better_by_score = (score_is_better ||
5152                                (score_is_equal && time_is_better));
5153     boolean better_by_time  = (time_is_better ||
5154                                (time_is_equal && score_is_better));
5155     boolean is_better = (level.rate_time_over_score ? better_by_time :
5156                          better_by_score);
5157     boolean entry_is_empty = (entry->score == 0 &&
5158                               entry->time == 0);
5159
5160     // prevent adding server score entries if also existing in local score file
5161     // (special case: historic score entries have an empty tape basename entry)
5162     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5163         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5164     {
5165       // add fields from server score entry not stored in local score entry
5166       // (currently, this means setting platform, version and country fields;
5167       // in rare cases, this may also correct an invalid score value, as
5168       // historic scores might have been truncated to 16-bit values locally)
5169       *entry = *new_entry;
5170
5171       return -1;
5172     }
5173
5174     if (is_better || entry_is_empty)
5175     {
5176       // player has made it to the hall of fame
5177
5178       if (i < MAX_SCORE_ENTRIES - 1)
5179       {
5180         int m = MAX_SCORE_ENTRIES - 1;
5181         int l;
5182
5183         if (one_score_entry_per_name)
5184         {
5185           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5186             if (strEqual(list->entry[l].name, new_entry->name))
5187               m = l;
5188
5189           if (m == i)   // player's new highscore overwrites his old one
5190             goto put_into_list;
5191         }
5192
5193         for (l = m; l > i; l--)
5194           list->entry[l] = list->entry[l - 1];
5195       }
5196
5197       put_into_list:
5198
5199       *entry = *new_entry;
5200
5201       return i;
5202     }
5203     else if (one_score_entry_per_name &&
5204              strEqual(entry->name, new_entry->name))
5205     {
5206       // player already in high score list with better score or time
5207
5208       return -1;
5209     }
5210   }
5211
5212   // special case: new score is beyond the last high score list position
5213   return MAX_SCORE_ENTRIES;
5214 }
5215
5216 void NewHighScore(int level_nr, boolean tape_saved)
5217 {
5218   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5219   boolean one_per_name = FALSE;
5220
5221   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5222   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5223
5224   new_entry.score = game.score_final;
5225   new_entry.time = game.score_time_final;
5226
5227   LoadScore(level_nr);
5228
5229   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5230
5231   if (scores.last_added >= MAX_SCORE_ENTRIES)
5232   {
5233     scores.last_added = MAX_SCORE_ENTRIES - 1;
5234     scores.force_last_added = TRUE;
5235
5236     scores.entry[scores.last_added] = new_entry;
5237
5238     // store last added local score entry (before merging server scores)
5239     scores.last_added_local = scores.last_added;
5240
5241     return;
5242   }
5243
5244   if (scores.last_added < 0)
5245     return;
5246
5247   SaveScore(level_nr);
5248
5249   // store last added local score entry (before merging server scores)
5250   scores.last_added_local = scores.last_added;
5251
5252   if (!game.LevelSolved_SaveTape)
5253     return;
5254
5255   SaveScoreTape(level_nr);
5256
5257   if (setup.ask_for_using_api_server)
5258   {
5259     setup.use_api_server =
5260       Request("Upload your score and tape to the high score server?", REQ_ASK);
5261
5262     if (!setup.use_api_server)
5263       Request("Not using high score server! Use setup menu to enable again!",
5264               REQ_CONFIRM);
5265
5266     runtime.use_api_server = setup.use_api_server;
5267
5268     // after asking for using API server once, do not ask again
5269     setup.ask_for_using_api_server = FALSE;
5270
5271     SaveSetup_ServerSetup();
5272   }
5273
5274   SaveServerScore(level_nr, tape_saved);
5275 }
5276
5277 void MergeServerScore(void)
5278 {
5279   struct ScoreEntry last_added_entry;
5280   boolean one_per_name = FALSE;
5281   int i;
5282
5283   if (scores.last_added >= 0)
5284     last_added_entry = scores.entry[scores.last_added];
5285
5286   for (i = 0; i < server_scores.num_entries; i++)
5287   {
5288     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5289
5290     if (pos >= 0 && pos <= scores.last_added)
5291       scores.last_added++;
5292   }
5293
5294   if (scores.last_added >= MAX_SCORE_ENTRIES)
5295   {
5296     scores.last_added = MAX_SCORE_ENTRIES - 1;
5297     scores.force_last_added = TRUE;
5298
5299     scores.entry[scores.last_added] = last_added_entry;
5300   }
5301 }
5302
5303 static int getElementMoveStepsizeExt(int x, int y, int direction)
5304 {
5305   int element = Tile[x][y];
5306   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5307   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5308   int horiz_move = (dx != 0);
5309   int sign = (horiz_move ? dx : dy);
5310   int step = sign * element_info[element].move_stepsize;
5311
5312   // special values for move stepsize for spring and things on conveyor belt
5313   if (horiz_move)
5314   {
5315     if (CAN_FALL(element) &&
5316         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5317       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5318     else if (element == EL_SPRING)
5319       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5320   }
5321
5322   return step;
5323 }
5324
5325 static int getElementMoveStepsize(int x, int y)
5326 {
5327   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5328 }
5329
5330 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5331 {
5332   if (player->GfxAction != action || player->GfxDir != dir)
5333   {
5334     player->GfxAction = action;
5335     player->GfxDir = dir;
5336     player->Frame = 0;
5337     player->StepFrame = 0;
5338   }
5339 }
5340
5341 static void ResetGfxFrame(int x, int y)
5342 {
5343   // profiling showed that "autotest" spends 10~20% of its time in this function
5344   if (DrawingDeactivatedField())
5345     return;
5346
5347   int element = Tile[x][y];
5348   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5349
5350   if (graphic_info[graphic].anim_global_sync)
5351     GfxFrame[x][y] = FrameCounter;
5352   else if (graphic_info[graphic].anim_global_anim_sync)
5353     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5354   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5355     GfxFrame[x][y] = CustomValue[x][y];
5356   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5357     GfxFrame[x][y] = element_info[element].collect_score;
5358   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5359     GfxFrame[x][y] = ChangeDelay[x][y];
5360 }
5361
5362 static void ResetGfxAnimation(int x, int y)
5363 {
5364   GfxAction[x][y] = ACTION_DEFAULT;
5365   GfxDir[x][y] = MovDir[x][y];
5366   GfxFrame[x][y] = 0;
5367
5368   ResetGfxFrame(x, y);
5369 }
5370
5371 static void ResetRandomAnimationValue(int x, int y)
5372 {
5373   GfxRandom[x][y] = INIT_GFX_RANDOM();
5374 }
5375
5376 static void InitMovingField(int x, int y, int direction)
5377 {
5378   int element = Tile[x][y];
5379   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5380   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5381   int newx = x + dx;
5382   int newy = y + dy;
5383   boolean is_moving_before, is_moving_after;
5384
5385   // check if element was/is moving or being moved before/after mode change
5386   is_moving_before = (WasJustMoving[x][y] != 0);
5387   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5388
5389   // reset animation only for moving elements which change direction of moving
5390   // or which just started or stopped moving
5391   // (else CEs with property "can move" / "not moving" are reset each frame)
5392   if (is_moving_before != is_moving_after ||
5393       direction != MovDir[x][y])
5394     ResetGfxAnimation(x, y);
5395
5396   MovDir[x][y] = direction;
5397   GfxDir[x][y] = direction;
5398
5399   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5400                      direction == MV_DOWN && CAN_FALL(element) ?
5401                      ACTION_FALLING : ACTION_MOVING);
5402
5403   // this is needed for CEs with property "can move" / "not moving"
5404
5405   if (is_moving_after)
5406   {
5407     if (Tile[newx][newy] == EL_EMPTY)
5408       Tile[newx][newy] = EL_BLOCKED;
5409
5410     MovDir[newx][newy] = MovDir[x][y];
5411
5412     CustomValue[newx][newy] = CustomValue[x][y];
5413
5414     GfxFrame[newx][newy] = GfxFrame[x][y];
5415     GfxRandom[newx][newy] = GfxRandom[x][y];
5416     GfxAction[newx][newy] = GfxAction[x][y];
5417     GfxDir[newx][newy] = GfxDir[x][y];
5418   }
5419 }
5420
5421 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5422 {
5423   int direction = MovDir[x][y];
5424   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5425   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5426
5427   *goes_to_x = newx;
5428   *goes_to_y = newy;
5429 }
5430
5431 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5432 {
5433   int direction = MovDir[x][y];
5434   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5435   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5436
5437   *comes_from_x = oldx;
5438   *comes_from_y = oldy;
5439 }
5440
5441 static int MovingOrBlocked2Element(int x, int y)
5442 {
5443   int element = Tile[x][y];
5444
5445   if (element == EL_BLOCKED)
5446   {
5447     int oldx, oldy;
5448
5449     Blocked2Moving(x, y, &oldx, &oldy);
5450
5451     return Tile[oldx][oldy];
5452   }
5453
5454   return element;
5455 }
5456
5457 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5458 {
5459   // like MovingOrBlocked2Element(), but if element is moving
5460   // and (x, y) is the field the moving element is just leaving,
5461   // return EL_BLOCKED instead of the element value
5462   int element = Tile[x][y];
5463
5464   if (IS_MOVING(x, y))
5465   {
5466     if (element == EL_BLOCKED)
5467     {
5468       int oldx, oldy;
5469
5470       Blocked2Moving(x, y, &oldx, &oldy);
5471       return Tile[oldx][oldy];
5472     }
5473     else
5474       return EL_BLOCKED;
5475   }
5476   else
5477     return element;
5478 }
5479
5480 static void RemoveField(int x, int y)
5481 {
5482   Tile[x][y] = EL_EMPTY;
5483
5484   MovPos[x][y] = 0;
5485   MovDir[x][y] = 0;
5486   MovDelay[x][y] = 0;
5487
5488   CustomValue[x][y] = 0;
5489
5490   AmoebaNr[x][y] = 0;
5491   ChangeDelay[x][y] = 0;
5492   ChangePage[x][y] = -1;
5493   Pushed[x][y] = FALSE;
5494
5495   GfxElement[x][y] = EL_UNDEFINED;
5496   GfxAction[x][y] = ACTION_DEFAULT;
5497   GfxDir[x][y] = MV_NONE;
5498 }
5499
5500 static void RemoveMovingField(int x, int y)
5501 {
5502   int oldx = x, oldy = y, newx = x, newy = y;
5503   int element = Tile[x][y];
5504   int next_element = EL_UNDEFINED;
5505
5506   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5507     return;
5508
5509   if (IS_MOVING(x, y))
5510   {
5511     Moving2Blocked(x, y, &newx, &newy);
5512
5513     if (Tile[newx][newy] != EL_BLOCKED)
5514     {
5515       // element is moving, but target field is not free (blocked), but
5516       // already occupied by something different (example: acid pool);
5517       // in this case, only remove the moving field, but not the target
5518
5519       RemoveField(oldx, oldy);
5520
5521       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5522
5523       TEST_DrawLevelField(oldx, oldy);
5524
5525       return;
5526     }
5527   }
5528   else if (element == EL_BLOCKED)
5529   {
5530     Blocked2Moving(x, y, &oldx, &oldy);
5531     if (!IS_MOVING(oldx, oldy))
5532       return;
5533   }
5534
5535   if (element == EL_BLOCKED &&
5536       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5537        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5538        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5539        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5540        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5541        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5542     next_element = get_next_element(Tile[oldx][oldy]);
5543
5544   RemoveField(oldx, oldy);
5545   RemoveField(newx, newy);
5546
5547   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5548
5549   if (next_element != EL_UNDEFINED)
5550     Tile[oldx][oldy] = next_element;
5551
5552   TEST_DrawLevelField(oldx, oldy);
5553   TEST_DrawLevelField(newx, newy);
5554 }
5555
5556 void DrawDynamite(int x, int y)
5557 {
5558   int sx = SCREENX(x), sy = SCREENY(y);
5559   int graphic = el2img(Tile[x][y]);
5560   int frame;
5561
5562   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5563     return;
5564
5565   if (IS_WALKABLE_INSIDE(Back[x][y]))
5566     return;
5567
5568   if (Back[x][y])
5569     DrawLevelElement(x, y, Back[x][y]);
5570   else if (Store[x][y])
5571     DrawLevelElement(x, y, Store[x][y]);
5572   else if (game.use_masked_elements)
5573     DrawLevelElement(x, y, EL_EMPTY);
5574
5575   frame = getGraphicAnimationFrameXY(graphic, x, y);
5576
5577   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5578     DrawGraphicThruMask(sx, sy, graphic, frame);
5579   else
5580     DrawGraphic(sx, sy, graphic, frame);
5581 }
5582
5583 static void CheckDynamite(int x, int y)
5584 {
5585   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5586   {
5587     MovDelay[x][y]--;
5588
5589     if (MovDelay[x][y] != 0)
5590     {
5591       DrawDynamite(x, y);
5592       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5593
5594       return;
5595     }
5596   }
5597
5598   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5599
5600   Bang(x, y);
5601 }
5602
5603 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5604 {
5605   boolean num_checked_players = 0;
5606   int i;
5607
5608   for (i = 0; i < MAX_PLAYERS; i++)
5609   {
5610     if (stored_player[i].active)
5611     {
5612       int sx = stored_player[i].jx;
5613       int sy = stored_player[i].jy;
5614
5615       if (num_checked_players == 0)
5616       {
5617         *sx1 = *sx2 = sx;
5618         *sy1 = *sy2 = sy;
5619       }
5620       else
5621       {
5622         *sx1 = MIN(*sx1, sx);
5623         *sy1 = MIN(*sy1, sy);
5624         *sx2 = MAX(*sx2, sx);
5625         *sy2 = MAX(*sy2, sy);
5626       }
5627
5628       num_checked_players++;
5629     }
5630   }
5631 }
5632
5633 static boolean checkIfAllPlayersFitToScreen_RND(void)
5634 {
5635   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5636
5637   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5638
5639   return (sx2 - sx1 < SCR_FIELDX &&
5640           sy2 - sy1 < SCR_FIELDY);
5641 }
5642
5643 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5644 {
5645   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5646
5647   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5648
5649   *sx = (sx1 + sx2) / 2;
5650   *sy = (sy1 + sy2) / 2;
5651 }
5652
5653 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5654                                boolean center_screen, boolean quick_relocation)
5655 {
5656   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5657   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5658   boolean no_delay = (tape.warp_forward);
5659   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5660   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5661   int new_scroll_x, new_scroll_y;
5662
5663   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5664   {
5665     // case 1: quick relocation inside visible screen (without scrolling)
5666
5667     RedrawPlayfield();
5668
5669     return;
5670   }
5671
5672   if (!level.shifted_relocation || center_screen)
5673   {
5674     // relocation _with_ centering of screen
5675
5676     new_scroll_x = SCROLL_POSITION_X(x);
5677     new_scroll_y = SCROLL_POSITION_Y(y);
5678   }
5679   else
5680   {
5681     // relocation _without_ centering of screen
5682
5683     // apply distance between old and new player position to scroll position
5684     int shifted_scroll_x = scroll_x + (x - old_x);
5685     int shifted_scroll_y = scroll_y + (y - old_y);
5686
5687     // make sure that shifted scroll position does not scroll beyond screen
5688     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5689     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5690
5691     // special case for teleporting from one end of the playfield to the other
5692     // (this kludge prevents the destination area to be shifted by half a tile
5693     // against the source destination for even screen width or screen height;
5694     // probably most useful when used with high "game.forced_scroll_delay_value"
5695     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5696     if (quick_relocation)
5697     {
5698       if (EVEN(SCR_FIELDX))
5699       {
5700         // relocate (teleport) between left and right border (half or full)
5701         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5702           new_scroll_x = SBX_Right;
5703         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5704           new_scroll_x = SBX_Right - 1;
5705         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5706           new_scroll_x = SBX_Left;
5707         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5708           new_scroll_x = SBX_Left + 1;
5709       }
5710
5711       if (EVEN(SCR_FIELDY))
5712       {
5713         // relocate (teleport) between top and bottom border (half or full)
5714         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5715           new_scroll_y = SBY_Lower;
5716         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5717           new_scroll_y = SBY_Lower - 1;
5718         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5719           new_scroll_y = SBY_Upper;
5720         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5721           new_scroll_y = SBY_Upper + 1;
5722       }
5723     }
5724   }
5725
5726   if (quick_relocation)
5727   {
5728     // case 2: quick relocation (redraw without visible scrolling)
5729
5730     scroll_x = new_scroll_x;
5731     scroll_y = new_scroll_y;
5732
5733     RedrawPlayfield();
5734
5735     return;
5736   }
5737
5738   // case 3: visible relocation (with scrolling to new position)
5739
5740   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5741
5742   SetVideoFrameDelay(wait_delay_value);
5743
5744   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5745   {
5746     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5747     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5748
5749     if (dx == 0 && dy == 0)             // no scrolling needed at all
5750       break;
5751
5752     scroll_x -= dx;
5753     scroll_y -= dy;
5754
5755     // set values for horizontal/vertical screen scrolling (half tile size)
5756     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5757     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5758     int pos_x = dx * TILEX / 2;
5759     int pos_y = dy * TILEY / 2;
5760     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5761     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5762
5763     ScrollLevel(dx, dy);
5764     DrawAllPlayers();
5765
5766     // scroll in two steps of half tile size to make things smoother
5767     BlitScreenToBitmapExt_RND(window, fx, fy);
5768
5769     // scroll second step to align at full tile size
5770     BlitScreenToBitmap(window);
5771   }
5772
5773   DrawAllPlayers();
5774   BackToFront();
5775
5776   SetVideoFrameDelay(frame_delay_value_old);
5777 }
5778
5779 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5780 {
5781   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5782   int player_nr = GET_PLAYER_NR(el_player);
5783   struct PlayerInfo *player = &stored_player[player_nr];
5784   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5785   boolean no_delay = (tape.warp_forward);
5786   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5787   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5788   int old_jx = player->jx;
5789   int old_jy = player->jy;
5790   int old_element = Tile[old_jx][old_jy];
5791   int element = Tile[jx][jy];
5792   boolean player_relocated = (old_jx != jx || old_jy != jy);
5793
5794   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5795   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5796   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5797   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5798   int leave_side_horiz = move_dir_horiz;
5799   int leave_side_vert  = move_dir_vert;
5800   int enter_side = enter_side_horiz | enter_side_vert;
5801   int leave_side = leave_side_horiz | leave_side_vert;
5802
5803   if (player->buried)           // do not reanimate dead player
5804     return;
5805
5806   if (!player_relocated)        // no need to relocate the player
5807     return;
5808
5809   if (IS_PLAYER(jx, jy))        // player already placed at new position
5810   {
5811     RemoveField(jx, jy);        // temporarily remove newly placed player
5812     DrawLevelField(jx, jy);
5813   }
5814
5815   if (player->present)
5816   {
5817     while (player->MovPos)
5818     {
5819       ScrollPlayer(player, SCROLL_GO_ON);
5820       ScrollScreen(NULL, SCROLL_GO_ON);
5821
5822       AdvanceFrameAndPlayerCounters(player->index_nr);
5823
5824       DrawPlayer(player);
5825
5826       BackToFront_WithFrameDelay(wait_delay_value);
5827     }
5828
5829     DrawPlayer(player);         // needed here only to cleanup last field
5830     DrawLevelField(player->jx, player->jy);     // remove player graphic
5831
5832     player->is_moving = FALSE;
5833   }
5834
5835   if (IS_CUSTOM_ELEMENT(old_element))
5836     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5837                                CE_LEFT_BY_PLAYER,
5838                                player->index_bit, leave_side);
5839
5840   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5841                                       CE_PLAYER_LEAVES_X,
5842                                       player->index_bit, leave_side);
5843
5844   Tile[jx][jy] = el_player;
5845   InitPlayerField(jx, jy, el_player, TRUE);
5846
5847   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5848      possible that the relocation target field did not contain a player element,
5849      but a walkable element, to which the new player was relocated -- in this
5850      case, restore that (already initialized!) element on the player field */
5851   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5852   {
5853     Tile[jx][jy] = element;     // restore previously existing element
5854   }
5855
5856   // only visually relocate centered player
5857   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5858                      FALSE, level.instant_relocation);
5859
5860   TestIfPlayerTouchesBadThing(jx, jy);
5861   TestIfPlayerTouchesCustomElement(jx, jy);
5862
5863   if (IS_CUSTOM_ELEMENT(element))
5864     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5865                                player->index_bit, enter_side);
5866
5867   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5868                                       player->index_bit, enter_side);
5869
5870   if (player->is_switching)
5871   {
5872     /* ensure that relocation while still switching an element does not cause
5873        a new element to be treated as also switched directly after relocation
5874        (this is important for teleporter switches that teleport the player to
5875        a place where another teleporter switch is in the same direction, which
5876        would then incorrectly be treated as immediately switched before the
5877        direction key that caused the switch was released) */
5878
5879     player->switch_x += jx - old_jx;
5880     player->switch_y += jy - old_jy;
5881   }
5882 }
5883
5884 static void Explode(int ex, int ey, int phase, int mode)
5885 {
5886   int x, y;
5887   int last_phase;
5888   int border_element;
5889
5890   if (game.explosions_delayed)
5891   {
5892     ExplodeField[ex][ey] = mode;
5893     return;
5894   }
5895
5896   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5897   {
5898     int center_element = Tile[ex][ey];
5899     int ce_value = CustomValue[ex][ey];
5900     int ce_score = element_info[center_element].collect_score;
5901     int artwork_element, explosion_element;     // set these values later
5902
5903     // remove things displayed in background while burning dynamite
5904     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5905       Back[ex][ey] = 0;
5906
5907     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5908     {
5909       // put moving element to center field (and let it explode there)
5910       center_element = MovingOrBlocked2Element(ex, ey);
5911       RemoveMovingField(ex, ey);
5912       Tile[ex][ey] = center_element;
5913     }
5914
5915     // now "center_element" is finally determined -- set related values now
5916     artwork_element = center_element;           // for custom player artwork
5917     explosion_element = center_element;         // for custom player artwork
5918
5919     if (IS_PLAYER(ex, ey))
5920     {
5921       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5922
5923       artwork_element = stored_player[player_nr].artwork_element;
5924
5925       if (level.use_explosion_element[player_nr])
5926       {
5927         explosion_element = level.explosion_element[player_nr];
5928         artwork_element = explosion_element;
5929       }
5930     }
5931
5932     if (mode == EX_TYPE_NORMAL ||
5933         mode == EX_TYPE_CENTER ||
5934         mode == EX_TYPE_CROSS)
5935       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5936
5937     last_phase = element_info[explosion_element].explosion_delay + 1;
5938
5939     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5940     {
5941       int xx = x - ex + 1;
5942       int yy = y - ey + 1;
5943       int element;
5944
5945       if (!IN_LEV_FIELD(x, y) ||
5946           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5947           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5948         continue;
5949
5950       element = Tile[x][y];
5951
5952       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5953       {
5954         element = MovingOrBlocked2Element(x, y);
5955
5956         if (!IS_EXPLOSION_PROOF(element))
5957           RemoveMovingField(x, y);
5958       }
5959
5960       // indestructible elements can only explode in center (but not flames)
5961       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5962                                            mode == EX_TYPE_BORDER)) ||
5963           element == EL_FLAMES)
5964         continue;
5965
5966       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5967          behaviour, for example when touching a yamyam that explodes to rocks
5968          with active deadly shield, a rock is created under the player !!! */
5969       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5970 #if 0
5971       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5972           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5973            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5974 #else
5975       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5976 #endif
5977       {
5978         if (IS_ACTIVE_BOMB(element))
5979         {
5980           // re-activate things under the bomb like gate or penguin
5981           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5982           Back[x][y] = 0;
5983         }
5984
5985         continue;
5986       }
5987
5988       // save walkable background elements while explosion on same tile
5989       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5990           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5991         Back[x][y] = element;
5992
5993       // ignite explodable elements reached by other explosion
5994       if (element == EL_EXPLOSION)
5995         element = Store2[x][y];
5996
5997       if (AmoebaNr[x][y] &&
5998           (element == EL_AMOEBA_FULL ||
5999            element == EL_BD_AMOEBA ||
6000            element == EL_AMOEBA_GROWING))
6001       {
6002         AmoebaCnt[AmoebaNr[x][y]]--;
6003         AmoebaCnt2[AmoebaNr[x][y]]--;
6004       }
6005
6006       RemoveField(x, y);
6007
6008       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6009       {
6010         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6011
6012         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6013
6014         if (PLAYERINFO(ex, ey)->use_murphy)
6015           Store[x][y] = EL_EMPTY;
6016       }
6017
6018       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6019       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6020       else if (IS_PLAYER_ELEMENT(center_element))
6021         Store[x][y] = EL_EMPTY;
6022       else if (center_element == EL_YAMYAM)
6023         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6024       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6025         Store[x][y] = element_info[center_element].content.e[xx][yy];
6026 #if 1
6027       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6028       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6029       // otherwise) -- FIX THIS !!!
6030       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6031         Store[x][y] = element_info[element].content.e[1][1];
6032 #else
6033       else if (!CAN_EXPLODE(element))
6034         Store[x][y] = element_info[element].content.e[1][1];
6035 #endif
6036       else
6037         Store[x][y] = EL_EMPTY;
6038
6039       if (IS_CUSTOM_ELEMENT(center_element))
6040         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6041                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6042                        Store[x][y] >= EL_PREV_CE_8 &&
6043                        Store[x][y] <= EL_NEXT_CE_8 ?
6044                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6045                        Store[x][y]);
6046
6047       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6048           center_element == EL_AMOEBA_TO_DIAMOND)
6049         Store2[x][y] = element;
6050
6051       Tile[x][y] = EL_EXPLOSION;
6052       GfxElement[x][y] = artwork_element;
6053
6054       ExplodePhase[x][y] = 1;
6055       ExplodeDelay[x][y] = last_phase;
6056
6057       Stop[x][y] = TRUE;
6058     }
6059
6060     if (center_element == EL_YAMYAM)
6061       game.yamyam_content_nr =
6062         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6063
6064     return;
6065   }
6066
6067   if (Stop[ex][ey])
6068     return;
6069
6070   x = ex;
6071   y = ey;
6072
6073   if (phase == 1)
6074     GfxFrame[x][y] = 0;         // restart explosion animation
6075
6076   last_phase = ExplodeDelay[x][y];
6077
6078   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6079
6080   // this can happen if the player leaves an explosion just in time
6081   if (GfxElement[x][y] == EL_UNDEFINED)
6082     GfxElement[x][y] = EL_EMPTY;
6083
6084   border_element = Store2[x][y];
6085   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6086     border_element = StorePlayer[x][y];
6087
6088   if (phase == element_info[border_element].ignition_delay ||
6089       phase == last_phase)
6090   {
6091     boolean border_explosion = FALSE;
6092
6093     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6094         !PLAYER_EXPLOSION_PROTECTED(x, y))
6095     {
6096       KillPlayerUnlessExplosionProtected(x, y);
6097       border_explosion = TRUE;
6098     }
6099     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6100     {
6101       Tile[x][y] = Store2[x][y];
6102       Store2[x][y] = 0;
6103       Bang(x, y);
6104       border_explosion = TRUE;
6105     }
6106     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6107     {
6108       AmoebaToDiamond(x, y);
6109       Store2[x][y] = 0;
6110       border_explosion = TRUE;
6111     }
6112
6113     // if an element just explodes due to another explosion (chain-reaction),
6114     // do not immediately end the new explosion when it was the last frame of
6115     // the explosion (as it would be done in the following "if"-statement!)
6116     if (border_explosion && phase == last_phase)
6117       return;
6118   }
6119
6120   // this can happen if the player was just killed by an explosion
6121   if (GfxElement[x][y] == EL_UNDEFINED)
6122     GfxElement[x][y] = EL_EMPTY;
6123
6124   if (phase == last_phase)
6125   {
6126     int element;
6127
6128     element = Tile[x][y] = Store[x][y];
6129     Store[x][y] = Store2[x][y] = 0;
6130     GfxElement[x][y] = EL_UNDEFINED;
6131
6132     // player can escape from explosions and might therefore be still alive
6133     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6134         element <= EL_PLAYER_IS_EXPLODING_4)
6135     {
6136       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6137       int explosion_element = EL_PLAYER_1 + player_nr;
6138       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6139       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6140
6141       if (level.use_explosion_element[player_nr])
6142         explosion_element = level.explosion_element[player_nr];
6143
6144       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6145                     element_info[explosion_element].content.e[xx][yy]);
6146     }
6147
6148     // restore probably existing indestructible background element
6149     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6150       element = Tile[x][y] = Back[x][y];
6151     Back[x][y] = 0;
6152
6153     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6154     GfxDir[x][y] = MV_NONE;
6155     ChangeDelay[x][y] = 0;
6156     ChangePage[x][y] = -1;
6157
6158     CustomValue[x][y] = 0;
6159
6160     InitField_WithBug2(x, y, FALSE);
6161
6162     TEST_DrawLevelField(x, y);
6163
6164     TestIfElementTouchesCustomElement(x, y);
6165
6166     if (GFX_CRUMBLED(element))
6167       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6168
6169     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6170       StorePlayer[x][y] = 0;
6171
6172     if (IS_PLAYER_ELEMENT(element))
6173       RelocatePlayer(x, y, element);
6174   }
6175   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6176   {
6177     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6178     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6179
6180     if (phase == 1)
6181       TEST_DrawLevelFieldCrumbled(x, y);
6182
6183     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6184     {
6185       DrawLevelElement(x, y, Back[x][y]);
6186       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6187     }
6188     else if (IS_WALKABLE_UNDER(Back[x][y]))
6189     {
6190       DrawLevelGraphic(x, y, graphic, frame);
6191       DrawLevelElementThruMask(x, y, Back[x][y]);
6192     }
6193     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6194       DrawLevelGraphic(x, y, graphic, frame);
6195   }
6196 }
6197
6198 static void DynaExplode(int ex, int ey)
6199 {
6200   int i, j;
6201   int dynabomb_element = Tile[ex][ey];
6202   int dynabomb_size = 1;
6203   boolean dynabomb_xl = FALSE;
6204   struct PlayerInfo *player;
6205   struct XY *xy = xy_topdown;
6206
6207   if (IS_ACTIVE_BOMB(dynabomb_element))
6208   {
6209     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6210     dynabomb_size = player->dynabomb_size;
6211     dynabomb_xl = player->dynabomb_xl;
6212     player->dynabombs_left++;
6213   }
6214
6215   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6216
6217   for (i = 0; i < NUM_DIRECTIONS; i++)
6218   {
6219     for (j = 1; j <= dynabomb_size; j++)
6220     {
6221       int x = ex + j * xy[i].x;
6222       int y = ey + j * xy[i].y;
6223       int element;
6224
6225       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6226         break;
6227
6228       element = Tile[x][y];
6229
6230       // do not restart explosions of fields with active bombs
6231       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6232         continue;
6233
6234       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6235
6236       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6237           !IS_DIGGABLE(element) && !dynabomb_xl)
6238         break;
6239     }
6240   }
6241 }
6242
6243 void Bang(int x, int y)
6244 {
6245   int element = MovingOrBlocked2Element(x, y);
6246   int explosion_type = EX_TYPE_NORMAL;
6247
6248   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6249   {
6250     struct PlayerInfo *player = PLAYERINFO(x, y);
6251
6252     element = Tile[x][y] = player->initial_element;
6253
6254     if (level.use_explosion_element[player->index_nr])
6255     {
6256       int explosion_element = level.explosion_element[player->index_nr];
6257
6258       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6259         explosion_type = EX_TYPE_CROSS;
6260       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6261         explosion_type = EX_TYPE_CENTER;
6262     }
6263   }
6264
6265   switch (element)
6266   {
6267     case EL_BUG:
6268     case EL_SPACESHIP:
6269     case EL_BD_BUTTERFLY:
6270     case EL_BD_FIREFLY:
6271     case EL_YAMYAM:
6272     case EL_DARK_YAMYAM:
6273     case EL_ROBOT:
6274     case EL_PACMAN:
6275     case EL_MOLE:
6276       RaiseScoreElement(element);
6277       break;
6278
6279     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6280     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6281     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6282     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6283     case EL_DYNABOMB_INCREASE_NUMBER:
6284     case EL_DYNABOMB_INCREASE_SIZE:
6285     case EL_DYNABOMB_INCREASE_POWER:
6286       explosion_type = EX_TYPE_DYNA;
6287       break;
6288
6289     case EL_DC_LANDMINE:
6290       explosion_type = EX_TYPE_CENTER;
6291       break;
6292
6293     case EL_PENGUIN:
6294     case EL_LAMP:
6295     case EL_LAMP_ACTIVE:
6296     case EL_AMOEBA_TO_DIAMOND:
6297       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6298         explosion_type = EX_TYPE_CENTER;
6299       break;
6300
6301     default:
6302       if (element_info[element].explosion_type == EXPLODES_CROSS)
6303         explosion_type = EX_TYPE_CROSS;
6304       else if (element_info[element].explosion_type == EXPLODES_1X1)
6305         explosion_type = EX_TYPE_CENTER;
6306       break;
6307   }
6308
6309   if (explosion_type == EX_TYPE_DYNA)
6310     DynaExplode(x, y);
6311   else
6312     Explode(x, y, EX_PHASE_START, explosion_type);
6313
6314   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6315 }
6316
6317 static void SplashAcid(int x, int y)
6318 {
6319   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6320       (!IN_LEV_FIELD(x - 1, y - 2) ||
6321        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6322     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6323
6324   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6325       (!IN_LEV_FIELD(x + 1, y - 2) ||
6326        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6327     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6328
6329   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6330 }
6331
6332 static void InitBeltMovement(void)
6333 {
6334   static int belt_base_element[4] =
6335   {
6336     EL_CONVEYOR_BELT_1_LEFT,
6337     EL_CONVEYOR_BELT_2_LEFT,
6338     EL_CONVEYOR_BELT_3_LEFT,
6339     EL_CONVEYOR_BELT_4_LEFT
6340   };
6341   static int belt_base_active_element[4] =
6342   {
6343     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6344     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6345     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6346     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6347   };
6348
6349   int x, y, i, j;
6350
6351   // set frame order for belt animation graphic according to belt direction
6352   for (i = 0; i < NUM_BELTS; i++)
6353   {
6354     int belt_nr = i;
6355
6356     for (j = 0; j < NUM_BELT_PARTS; j++)
6357     {
6358       int element = belt_base_active_element[belt_nr] + j;
6359       int graphic_1 = el2img(element);
6360       int graphic_2 = el2panelimg(element);
6361
6362       if (game.belt_dir[i] == MV_LEFT)
6363       {
6364         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6365         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6366       }
6367       else
6368       {
6369         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6370         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6371       }
6372     }
6373   }
6374
6375   SCAN_PLAYFIELD(x, y)
6376   {
6377     int element = Tile[x][y];
6378
6379     for (i = 0; i < NUM_BELTS; i++)
6380     {
6381       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6382       {
6383         int e_belt_nr = getBeltNrFromBeltElement(element);
6384         int belt_nr = i;
6385
6386         if (e_belt_nr == belt_nr)
6387         {
6388           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6389
6390           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6391         }
6392       }
6393     }
6394   }
6395 }
6396
6397 static void ToggleBeltSwitch(int x, int y)
6398 {
6399   static int belt_base_element[4] =
6400   {
6401     EL_CONVEYOR_BELT_1_LEFT,
6402     EL_CONVEYOR_BELT_2_LEFT,
6403     EL_CONVEYOR_BELT_3_LEFT,
6404     EL_CONVEYOR_BELT_4_LEFT
6405   };
6406   static int belt_base_active_element[4] =
6407   {
6408     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6409     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6410     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6411     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6412   };
6413   static int belt_base_switch_element[4] =
6414   {
6415     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6416     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6417     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6418     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6419   };
6420   static int belt_move_dir[4] =
6421   {
6422     MV_LEFT,
6423     MV_NONE,
6424     MV_RIGHT,
6425     MV_NONE,
6426   };
6427
6428   int element = Tile[x][y];
6429   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6430   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6431   int belt_dir = belt_move_dir[belt_dir_nr];
6432   int xx, yy, i;
6433
6434   if (!IS_BELT_SWITCH(element))
6435     return;
6436
6437   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6438   game.belt_dir[belt_nr] = belt_dir;
6439
6440   if (belt_dir_nr == 3)
6441     belt_dir_nr = 1;
6442
6443   // set frame order for belt animation graphic according to belt direction
6444   for (i = 0; i < NUM_BELT_PARTS; i++)
6445   {
6446     int element = belt_base_active_element[belt_nr] + i;
6447     int graphic_1 = el2img(element);
6448     int graphic_2 = el2panelimg(element);
6449
6450     if (belt_dir == MV_LEFT)
6451     {
6452       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6453       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6454     }
6455     else
6456     {
6457       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6458       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6459     }
6460   }
6461
6462   SCAN_PLAYFIELD(xx, yy)
6463   {
6464     int element = Tile[xx][yy];
6465
6466     if (IS_BELT_SWITCH(element))
6467     {
6468       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6469
6470       if (e_belt_nr == belt_nr)
6471       {
6472         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6473         TEST_DrawLevelField(xx, yy);
6474       }
6475     }
6476     else if (IS_BELT(element) && belt_dir != MV_NONE)
6477     {
6478       int e_belt_nr = getBeltNrFromBeltElement(element);
6479
6480       if (e_belt_nr == belt_nr)
6481       {
6482         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6483
6484         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6485         TEST_DrawLevelField(xx, yy);
6486       }
6487     }
6488     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6489     {
6490       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6491
6492       if (e_belt_nr == belt_nr)
6493       {
6494         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6495
6496         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6497         TEST_DrawLevelField(xx, yy);
6498       }
6499     }
6500   }
6501 }
6502
6503 static void ToggleSwitchgateSwitch(void)
6504 {
6505   int xx, yy;
6506
6507   game.switchgate_pos = !game.switchgate_pos;
6508
6509   SCAN_PLAYFIELD(xx, yy)
6510   {
6511     int element = Tile[xx][yy];
6512
6513     if (element == EL_SWITCHGATE_SWITCH_UP)
6514     {
6515       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6516       TEST_DrawLevelField(xx, yy);
6517     }
6518     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6519     {
6520       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6521       TEST_DrawLevelField(xx, yy);
6522     }
6523     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6524     {
6525       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6526       TEST_DrawLevelField(xx, yy);
6527     }
6528     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6529     {
6530       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6531       TEST_DrawLevelField(xx, yy);
6532     }
6533     else if (element == EL_SWITCHGATE_OPEN ||
6534              element == EL_SWITCHGATE_OPENING)
6535     {
6536       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6537
6538       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6539     }
6540     else if (element == EL_SWITCHGATE_CLOSED ||
6541              element == EL_SWITCHGATE_CLOSING)
6542     {
6543       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6544
6545       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6546     }
6547   }
6548 }
6549
6550 static int getInvisibleActiveFromInvisibleElement(int element)
6551 {
6552   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6553           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6554           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6555           element);
6556 }
6557
6558 static int getInvisibleFromInvisibleActiveElement(int element)
6559 {
6560   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6561           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6562           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6563           element);
6564 }
6565
6566 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6567 {
6568   int x, y;
6569
6570   SCAN_PLAYFIELD(x, y)
6571   {
6572     int element = Tile[x][y];
6573
6574     if (element == EL_LIGHT_SWITCH &&
6575         game.light_time_left > 0)
6576     {
6577       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6578       TEST_DrawLevelField(x, y);
6579     }
6580     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6581              game.light_time_left == 0)
6582     {
6583       Tile[x][y] = EL_LIGHT_SWITCH;
6584       TEST_DrawLevelField(x, y);
6585     }
6586     else if (element == EL_EMC_DRIPPER &&
6587              game.light_time_left > 0)
6588     {
6589       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6590       TEST_DrawLevelField(x, y);
6591     }
6592     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6593              game.light_time_left == 0)
6594     {
6595       Tile[x][y] = EL_EMC_DRIPPER;
6596       TEST_DrawLevelField(x, y);
6597     }
6598     else if (element == EL_INVISIBLE_STEELWALL ||
6599              element == EL_INVISIBLE_WALL ||
6600              element == EL_INVISIBLE_SAND)
6601     {
6602       if (game.light_time_left > 0)
6603         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6604
6605       TEST_DrawLevelField(x, y);
6606
6607       // uncrumble neighbour fields, if needed
6608       if (element == EL_INVISIBLE_SAND)
6609         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6610     }
6611     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6612              element == EL_INVISIBLE_WALL_ACTIVE ||
6613              element == EL_INVISIBLE_SAND_ACTIVE)
6614     {
6615       if (game.light_time_left == 0)
6616         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6617
6618       TEST_DrawLevelField(x, y);
6619
6620       // re-crumble neighbour fields, if needed
6621       if (element == EL_INVISIBLE_SAND)
6622         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6623     }
6624   }
6625 }
6626
6627 static void RedrawAllInvisibleElementsForLenses(void)
6628 {
6629   int x, y;
6630
6631   SCAN_PLAYFIELD(x, y)
6632   {
6633     int element = Tile[x][y];
6634
6635     if (element == EL_EMC_DRIPPER &&
6636         game.lenses_time_left > 0)
6637     {
6638       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6639       TEST_DrawLevelField(x, y);
6640     }
6641     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6642              game.lenses_time_left == 0)
6643     {
6644       Tile[x][y] = EL_EMC_DRIPPER;
6645       TEST_DrawLevelField(x, y);
6646     }
6647     else if (element == EL_INVISIBLE_STEELWALL ||
6648              element == EL_INVISIBLE_WALL ||
6649              element == EL_INVISIBLE_SAND)
6650     {
6651       if (game.lenses_time_left > 0)
6652         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6653
6654       TEST_DrawLevelField(x, y);
6655
6656       // uncrumble neighbour fields, if needed
6657       if (element == EL_INVISIBLE_SAND)
6658         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6659     }
6660     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6661              element == EL_INVISIBLE_WALL_ACTIVE ||
6662              element == EL_INVISIBLE_SAND_ACTIVE)
6663     {
6664       if (game.lenses_time_left == 0)
6665         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6666
6667       TEST_DrawLevelField(x, y);
6668
6669       // re-crumble neighbour fields, if needed
6670       if (element == EL_INVISIBLE_SAND)
6671         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6672     }
6673   }
6674 }
6675
6676 static void RedrawAllInvisibleElementsForMagnifier(void)
6677 {
6678   int x, y;
6679
6680   SCAN_PLAYFIELD(x, y)
6681   {
6682     int element = Tile[x][y];
6683
6684     if (element == EL_EMC_FAKE_GRASS &&
6685         game.magnify_time_left > 0)
6686     {
6687       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6688       TEST_DrawLevelField(x, y);
6689     }
6690     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6691              game.magnify_time_left == 0)
6692     {
6693       Tile[x][y] = EL_EMC_FAKE_GRASS;
6694       TEST_DrawLevelField(x, y);
6695     }
6696     else if (IS_GATE_GRAY(element) &&
6697              game.magnify_time_left > 0)
6698     {
6699       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6700                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6701                     IS_EM_GATE_GRAY(element) ?
6702                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6703                     IS_EMC_GATE_GRAY(element) ?
6704                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6705                     IS_DC_GATE_GRAY(element) ?
6706                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6707                     element);
6708       TEST_DrawLevelField(x, y);
6709     }
6710     else if (IS_GATE_GRAY_ACTIVE(element) &&
6711              game.magnify_time_left == 0)
6712     {
6713       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6714                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6715                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6716                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6717                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6718                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6719                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6720                     EL_DC_GATE_WHITE_GRAY :
6721                     element);
6722       TEST_DrawLevelField(x, y);
6723     }
6724   }
6725 }
6726
6727 static void ToggleLightSwitch(int x, int y)
6728 {
6729   int element = Tile[x][y];
6730
6731   game.light_time_left =
6732     (element == EL_LIGHT_SWITCH ?
6733      level.time_light * FRAMES_PER_SECOND : 0);
6734
6735   RedrawAllLightSwitchesAndInvisibleElements();
6736 }
6737
6738 static void ActivateTimegateSwitch(int x, int y)
6739 {
6740   int xx, yy;
6741
6742   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6743
6744   SCAN_PLAYFIELD(xx, yy)
6745   {
6746     int element = Tile[xx][yy];
6747
6748     if (element == EL_TIMEGATE_CLOSED ||
6749         element == EL_TIMEGATE_CLOSING)
6750     {
6751       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6752       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6753     }
6754
6755     /*
6756     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6757     {
6758       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6759       TEST_DrawLevelField(xx, yy);
6760     }
6761     */
6762
6763   }
6764
6765   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6766                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6767 }
6768
6769 static void Impact(int x, int y)
6770 {
6771   boolean last_line = (y == lev_fieldy - 1);
6772   boolean object_hit = FALSE;
6773   boolean impact = (last_line || object_hit);
6774   int element = Tile[x][y];
6775   int smashed = EL_STEELWALL;
6776
6777   if (!last_line)       // check if element below was hit
6778   {
6779     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6780       return;
6781
6782     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6783                                          MovDir[x][y + 1] != MV_DOWN ||
6784                                          MovPos[x][y + 1] <= TILEY / 2));
6785
6786     // do not smash moving elements that left the smashed field in time
6787     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6788         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6789       object_hit = FALSE;
6790
6791 #if USE_QUICKSAND_IMPACT_BUGFIX
6792     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6793     {
6794       RemoveMovingField(x, y + 1);
6795       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6796       Tile[x][y + 2] = EL_ROCK;
6797       TEST_DrawLevelField(x, y + 2);
6798
6799       object_hit = TRUE;
6800     }
6801
6802     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6803     {
6804       RemoveMovingField(x, y + 1);
6805       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6806       Tile[x][y + 2] = EL_ROCK;
6807       TEST_DrawLevelField(x, y + 2);
6808
6809       object_hit = TRUE;
6810     }
6811 #endif
6812
6813     if (object_hit)
6814       smashed = MovingOrBlocked2Element(x, y + 1);
6815
6816     impact = (last_line || object_hit);
6817   }
6818
6819   if (!last_line && smashed == EL_ACID) // element falls into acid
6820   {
6821     SplashAcid(x, y + 1);
6822     return;
6823   }
6824
6825   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6826   // only reset graphic animation if graphic really changes after impact
6827   if (impact &&
6828       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6829   {
6830     ResetGfxAnimation(x, y);
6831     TEST_DrawLevelField(x, y);
6832   }
6833
6834   if (impact && CAN_EXPLODE_IMPACT(element))
6835   {
6836     Bang(x, y);
6837     return;
6838   }
6839   else if (impact && element == EL_PEARL &&
6840            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6841   {
6842     ResetGfxAnimation(x, y);
6843
6844     Tile[x][y] = EL_PEARL_BREAKING;
6845     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6846     return;
6847   }
6848   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6849   {
6850     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6851
6852     return;
6853   }
6854
6855   if (impact && element == EL_AMOEBA_DROP)
6856   {
6857     if (object_hit && IS_PLAYER(x, y + 1))
6858       KillPlayerUnlessEnemyProtected(x, y + 1);
6859     else if (object_hit && smashed == EL_PENGUIN)
6860       Bang(x, y + 1);
6861     else
6862     {
6863       Tile[x][y] = EL_AMOEBA_GROWING;
6864       Store[x][y] = EL_AMOEBA_WET;
6865
6866       ResetRandomAnimationValue(x, y);
6867     }
6868     return;
6869   }
6870
6871   if (object_hit)               // check which object was hit
6872   {
6873     if ((CAN_PASS_MAGIC_WALL(element) && 
6874          (smashed == EL_MAGIC_WALL ||
6875           smashed == EL_BD_MAGIC_WALL)) ||
6876         (CAN_PASS_DC_MAGIC_WALL(element) &&
6877          smashed == EL_DC_MAGIC_WALL))
6878     {
6879       int xx, yy;
6880       int activated_magic_wall =
6881         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6882          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6883          EL_DC_MAGIC_WALL_ACTIVE);
6884
6885       // activate magic wall / mill
6886       SCAN_PLAYFIELD(xx, yy)
6887       {
6888         if (Tile[xx][yy] == smashed)
6889           Tile[xx][yy] = activated_magic_wall;
6890       }
6891
6892       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6893       game.magic_wall_active = TRUE;
6894
6895       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6896                             SND_MAGIC_WALL_ACTIVATING :
6897                             smashed == EL_BD_MAGIC_WALL ?
6898                             SND_BD_MAGIC_WALL_ACTIVATING :
6899                             SND_DC_MAGIC_WALL_ACTIVATING));
6900     }
6901
6902     if (IS_PLAYER(x, y + 1))
6903     {
6904       if (CAN_SMASH_PLAYER(element))
6905       {
6906         KillPlayerUnlessEnemyProtected(x, y + 1);
6907         return;
6908       }
6909     }
6910     else if (smashed == EL_PENGUIN)
6911     {
6912       if (CAN_SMASH_PLAYER(element))
6913       {
6914         Bang(x, y + 1);
6915         return;
6916       }
6917     }
6918     else if (element == EL_BD_DIAMOND)
6919     {
6920       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6921       {
6922         Bang(x, y + 1);
6923         return;
6924       }
6925     }
6926     else if (((element == EL_SP_INFOTRON ||
6927                element == EL_SP_ZONK) &&
6928               (smashed == EL_SP_SNIKSNAK ||
6929                smashed == EL_SP_ELECTRON ||
6930                smashed == EL_SP_DISK_ORANGE)) ||
6931              (element == EL_SP_INFOTRON &&
6932               smashed == EL_SP_DISK_YELLOW))
6933     {
6934       Bang(x, y + 1);
6935       return;
6936     }
6937     else if (CAN_SMASH_EVERYTHING(element))
6938     {
6939       if (IS_CLASSIC_ENEMY(smashed) ||
6940           CAN_EXPLODE_SMASHED(smashed))
6941       {
6942         Bang(x, y + 1);
6943         return;
6944       }
6945       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6946       {
6947         if (smashed == EL_LAMP ||
6948             smashed == EL_LAMP_ACTIVE)
6949         {
6950           Bang(x, y + 1);
6951           return;
6952         }
6953         else if (smashed == EL_NUT)
6954         {
6955           Tile[x][y + 1] = EL_NUT_BREAKING;
6956           PlayLevelSound(x, y, SND_NUT_BREAKING);
6957           RaiseScoreElement(EL_NUT);
6958           return;
6959         }
6960         else if (smashed == EL_PEARL)
6961         {
6962           ResetGfxAnimation(x, y);
6963
6964           Tile[x][y + 1] = EL_PEARL_BREAKING;
6965           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6966           return;
6967         }
6968         else if (smashed == EL_DIAMOND)
6969         {
6970           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6971           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6972           return;
6973         }
6974         else if (IS_BELT_SWITCH(smashed))
6975         {
6976           ToggleBeltSwitch(x, y + 1);
6977         }
6978         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6979                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6980                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6981                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6982         {
6983           ToggleSwitchgateSwitch();
6984         }
6985         else if (smashed == EL_LIGHT_SWITCH ||
6986                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6987         {
6988           ToggleLightSwitch(x, y + 1);
6989         }
6990         else
6991         {
6992           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6993
6994           CheckElementChangeBySide(x, y + 1, smashed, element,
6995                                    CE_SWITCHED, CH_SIDE_TOP);
6996           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6997                                             CH_SIDE_TOP);
6998         }
6999       }
7000       else
7001       {
7002         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7003       }
7004     }
7005   }
7006
7007   // play sound of magic wall / mill
7008   if (!last_line &&
7009       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7010        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7011        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7012   {
7013     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7014       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7015     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7016       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7017     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7018       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7019
7020     return;
7021   }
7022
7023   // play sound of object that hits the ground
7024   if (last_line || object_hit)
7025     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7026 }
7027
7028 static void TurnRoundExt(int x, int y)
7029 {
7030   static struct
7031   {
7032     int dx, dy;
7033   } move_xy[] =
7034   {
7035     {  0,  0 },
7036     { -1,  0 },
7037     { +1,  0 },
7038     {  0,  0 },
7039     {  0, -1 },
7040     {  0,  0 }, { 0, 0 }, { 0, 0 },
7041     {  0, +1 }
7042   };
7043   static struct
7044   {
7045     int left, right, back;
7046   } turn[] =
7047   {
7048     { 0,        0,              0        },
7049     { MV_DOWN,  MV_UP,          MV_RIGHT },
7050     { MV_UP,    MV_DOWN,        MV_LEFT  },
7051     { 0,        0,              0        },
7052     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7053     { 0,        0,              0        },
7054     { 0,        0,              0        },
7055     { 0,        0,              0        },
7056     { MV_RIGHT, MV_LEFT,        MV_UP    }
7057   };
7058
7059   int element = Tile[x][y];
7060   int move_pattern = element_info[element].move_pattern;
7061
7062   int old_move_dir = MovDir[x][y];
7063   int left_dir  = turn[old_move_dir].left;
7064   int right_dir = turn[old_move_dir].right;
7065   int back_dir  = turn[old_move_dir].back;
7066
7067   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7068   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7069   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7070   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7071
7072   int left_x  = x + left_dx,  left_y  = y + left_dy;
7073   int right_x = x + right_dx, right_y = y + right_dy;
7074   int move_x  = x + move_dx,  move_y  = y + move_dy;
7075
7076   int xx, yy;
7077
7078   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7079   {
7080     TestIfBadThingTouchesOtherBadThing(x, y);
7081
7082     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7083       MovDir[x][y] = right_dir;
7084     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7085       MovDir[x][y] = left_dir;
7086
7087     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7088       MovDelay[x][y] = 9;
7089     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7090       MovDelay[x][y] = 1;
7091   }
7092   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7093   {
7094     TestIfBadThingTouchesOtherBadThing(x, y);
7095
7096     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7097       MovDir[x][y] = left_dir;
7098     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7099       MovDir[x][y] = right_dir;
7100
7101     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7102       MovDelay[x][y] = 9;
7103     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7104       MovDelay[x][y] = 1;
7105   }
7106   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7107   {
7108     TestIfBadThingTouchesOtherBadThing(x, y);
7109
7110     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7111       MovDir[x][y] = left_dir;
7112     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7113       MovDir[x][y] = right_dir;
7114
7115     if (MovDir[x][y] != old_move_dir)
7116       MovDelay[x][y] = 9;
7117   }
7118   else if (element == EL_YAMYAM)
7119   {
7120     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7121     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7122
7123     if (can_turn_left && can_turn_right)
7124       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7125     else if (can_turn_left)
7126       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7127     else if (can_turn_right)
7128       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7129     else
7130       MovDir[x][y] = back_dir;
7131
7132     MovDelay[x][y] = 16 + 16 * RND(3);
7133   }
7134   else if (element == EL_DARK_YAMYAM)
7135   {
7136     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7137                                                          left_x, left_y);
7138     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7139                                                          right_x, right_y);
7140
7141     if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7143     else if (can_turn_left)
7144       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7145     else if (can_turn_right)
7146       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7147     else
7148       MovDir[x][y] = back_dir;
7149
7150     MovDelay[x][y] = 16 + 16 * RND(3);
7151   }
7152   else if (element == EL_PACMAN)
7153   {
7154     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7155     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7156
7157     if (can_turn_left && can_turn_right)
7158       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7159     else if (can_turn_left)
7160       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7161     else if (can_turn_right)
7162       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7163     else
7164       MovDir[x][y] = back_dir;
7165
7166     MovDelay[x][y] = 6 + RND(40);
7167   }
7168   else if (element == EL_PIG)
7169   {
7170     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7171     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7172     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7173     boolean should_turn_left, should_turn_right, should_move_on;
7174     int rnd_value = 24;
7175     int rnd = RND(rnd_value);
7176
7177     should_turn_left = (can_turn_left &&
7178                         (!can_move_on ||
7179                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7180                                                    y + back_dy + left_dy)));
7181     should_turn_right = (can_turn_right &&
7182                          (!can_move_on ||
7183                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7184                                                     y + back_dy + right_dy)));
7185     should_move_on = (can_move_on &&
7186                       (!can_turn_left ||
7187                        !can_turn_right ||
7188                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7189                                                  y + move_dy + left_dy) ||
7190                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7191                                                  y + move_dy + right_dy)));
7192
7193     if (should_turn_left || should_turn_right || should_move_on)
7194     {
7195       if (should_turn_left && should_turn_right && should_move_on)
7196         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7197                         rnd < 2 * rnd_value / 3 ? right_dir :
7198                         old_move_dir);
7199       else if (should_turn_left && should_turn_right)
7200         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7201       else if (should_turn_left && should_move_on)
7202         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7203       else if (should_turn_right && should_move_on)
7204         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7205       else if (should_turn_left)
7206         MovDir[x][y] = left_dir;
7207       else if (should_turn_right)
7208         MovDir[x][y] = right_dir;
7209       else if (should_move_on)
7210         MovDir[x][y] = old_move_dir;
7211     }
7212     else if (can_move_on && rnd > rnd_value / 8)
7213       MovDir[x][y] = old_move_dir;
7214     else if (can_turn_left && can_turn_right)
7215       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7216     else if (can_turn_left && rnd > rnd_value / 8)
7217       MovDir[x][y] = left_dir;
7218     else if (can_turn_right && rnd > rnd_value/8)
7219       MovDir[x][y] = right_dir;
7220     else
7221       MovDir[x][y] = back_dir;
7222
7223     xx = x + move_xy[MovDir[x][y]].dx;
7224     yy = y + move_xy[MovDir[x][y]].dy;
7225
7226     if (!IN_LEV_FIELD(xx, yy) ||
7227         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7228       MovDir[x][y] = old_move_dir;
7229
7230     MovDelay[x][y] = 0;
7231   }
7232   else if (element == EL_DRAGON)
7233   {
7234     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7235     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7236     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7237     int rnd_value = 24;
7238     int rnd = RND(rnd_value);
7239
7240     if (can_move_on && rnd > rnd_value / 8)
7241       MovDir[x][y] = old_move_dir;
7242     else if (can_turn_left && can_turn_right)
7243       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7244     else if (can_turn_left && rnd > rnd_value / 8)
7245       MovDir[x][y] = left_dir;
7246     else if (can_turn_right && rnd > rnd_value / 8)
7247       MovDir[x][y] = right_dir;
7248     else
7249       MovDir[x][y] = back_dir;
7250
7251     xx = x + move_xy[MovDir[x][y]].dx;
7252     yy = y + move_xy[MovDir[x][y]].dy;
7253
7254     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7255       MovDir[x][y] = old_move_dir;
7256
7257     MovDelay[x][y] = 0;
7258   }
7259   else if (element == EL_MOLE)
7260   {
7261     boolean can_move_on =
7262       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7263                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7264                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7265     if (!can_move_on)
7266     {
7267       boolean can_turn_left =
7268         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7269                               IS_AMOEBOID(Tile[left_x][left_y])));
7270
7271       boolean can_turn_right =
7272         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7273                               IS_AMOEBOID(Tile[right_x][right_y])));
7274
7275       if (can_turn_left && can_turn_right)
7276         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7277       else if (can_turn_left)
7278         MovDir[x][y] = left_dir;
7279       else
7280         MovDir[x][y] = right_dir;
7281     }
7282
7283     if (MovDir[x][y] != old_move_dir)
7284       MovDelay[x][y] = 9;
7285   }
7286   else if (element == EL_BALLOON)
7287   {
7288     MovDir[x][y] = game.wind_direction;
7289     MovDelay[x][y] = 0;
7290   }
7291   else if (element == EL_SPRING)
7292   {
7293     if (MovDir[x][y] & MV_HORIZONTAL)
7294     {
7295       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7296           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7297       {
7298         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7299         ResetGfxAnimation(move_x, move_y);
7300         TEST_DrawLevelField(move_x, move_y);
7301
7302         MovDir[x][y] = back_dir;
7303       }
7304       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7305                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7306         MovDir[x][y] = MV_NONE;
7307     }
7308
7309     MovDelay[x][y] = 0;
7310   }
7311   else if (element == EL_ROBOT ||
7312            element == EL_SATELLITE ||
7313            element == EL_PENGUIN ||
7314            element == EL_EMC_ANDROID)
7315   {
7316     int attr_x = -1, attr_y = -1;
7317
7318     if (game.all_players_gone)
7319     {
7320       attr_x = game.exit_x;
7321       attr_y = game.exit_y;
7322     }
7323     else
7324     {
7325       int i;
7326
7327       for (i = 0; i < MAX_PLAYERS; i++)
7328       {
7329         struct PlayerInfo *player = &stored_player[i];
7330         int jx = player->jx, jy = player->jy;
7331
7332         if (!player->active)
7333           continue;
7334
7335         if (attr_x == -1 ||
7336             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7337         {
7338           attr_x = jx;
7339           attr_y = jy;
7340         }
7341       }
7342     }
7343
7344     if (element == EL_ROBOT &&
7345         game.robot_wheel_x >= 0 &&
7346         game.robot_wheel_y >= 0 &&
7347         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7348          game.engine_version < VERSION_IDENT(3,1,0,0)))
7349     {
7350       attr_x = game.robot_wheel_x;
7351       attr_y = game.robot_wheel_y;
7352     }
7353
7354     if (element == EL_PENGUIN)
7355     {
7356       int i;
7357       struct XY *xy = xy_topdown;
7358
7359       for (i = 0; i < NUM_DIRECTIONS; i++)
7360       {
7361         int ex = x + xy[i].x;
7362         int ey = y + xy[i].y;
7363
7364         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7365                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7366                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7367                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7368         {
7369           attr_x = ex;
7370           attr_y = ey;
7371           break;
7372         }
7373       }
7374     }
7375
7376     MovDir[x][y] = MV_NONE;
7377     if (attr_x < x)
7378       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7379     else if (attr_x > x)
7380       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7381     if (attr_y < y)
7382       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7383     else if (attr_y > y)
7384       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7385
7386     if (element == EL_ROBOT)
7387     {
7388       int newx, newy;
7389
7390       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7391         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7392       Moving2Blocked(x, y, &newx, &newy);
7393
7394       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7395         MovDelay[x][y] = 8 + 8 * !RND(3);
7396       else
7397         MovDelay[x][y] = 16;
7398     }
7399     else if (element == EL_PENGUIN)
7400     {
7401       int newx, newy;
7402
7403       MovDelay[x][y] = 1;
7404
7405       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7406       {
7407         boolean first_horiz = RND(2);
7408         int new_move_dir = MovDir[x][y];
7409
7410         MovDir[x][y] =
7411           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7412         Moving2Blocked(x, y, &newx, &newy);
7413
7414         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7415           return;
7416
7417         MovDir[x][y] =
7418           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7419         Moving2Blocked(x, y, &newx, &newy);
7420
7421         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7422           return;
7423
7424         MovDir[x][y] = old_move_dir;
7425         return;
7426       }
7427     }
7428     else if (element == EL_SATELLITE)
7429     {
7430       int newx, newy;
7431
7432       MovDelay[x][y] = 1;
7433
7434       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7435       {
7436         boolean first_horiz = RND(2);
7437         int new_move_dir = MovDir[x][y];
7438
7439         MovDir[x][y] =
7440           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7441         Moving2Blocked(x, y, &newx, &newy);
7442
7443         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7444           return;
7445
7446         MovDir[x][y] =
7447           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7448         Moving2Blocked(x, y, &newx, &newy);
7449
7450         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7451           return;
7452
7453         MovDir[x][y] = old_move_dir;
7454         return;
7455       }
7456     }
7457     else if (element == EL_EMC_ANDROID)
7458     {
7459       static int check_pos[16] =
7460       {
7461         -1,             //  0 => (invalid)
7462         7,              //  1 => MV_LEFT
7463         3,              //  2 => MV_RIGHT
7464         -1,             //  3 => (invalid)
7465         1,              //  4 =>            MV_UP
7466         0,              //  5 => MV_LEFT  | MV_UP
7467         2,              //  6 => MV_RIGHT | MV_UP
7468         -1,             //  7 => (invalid)
7469         5,              //  8 =>            MV_DOWN
7470         6,              //  9 => MV_LEFT  | MV_DOWN
7471         4,              // 10 => MV_RIGHT | MV_DOWN
7472         -1,             // 11 => (invalid)
7473         -1,             // 12 => (invalid)
7474         -1,             // 13 => (invalid)
7475         -1,             // 14 => (invalid)
7476         -1,             // 15 => (invalid)
7477       };
7478       static struct
7479       {
7480         int dx, dy;
7481         int dir;
7482       } check_xy[8] =
7483       {
7484         { -1, -1,       MV_LEFT  | MV_UP   },
7485         {  0, -1,                  MV_UP   },
7486         { +1, -1,       MV_RIGHT | MV_UP   },
7487         { +1,  0,       MV_RIGHT           },
7488         { +1, +1,       MV_RIGHT | MV_DOWN },
7489         {  0, +1,                  MV_DOWN },
7490         { -1, +1,       MV_LEFT  | MV_DOWN },
7491         { -1,  0,       MV_LEFT            },
7492       };
7493       int start_pos, check_order;
7494       boolean can_clone = FALSE;
7495       int i;
7496
7497       // check if there is any free field around current position
7498       for (i = 0; i < 8; i++)
7499       {
7500         int newx = x + check_xy[i].dx;
7501         int newy = y + check_xy[i].dy;
7502
7503         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7504         {
7505           can_clone = TRUE;
7506
7507           break;
7508         }
7509       }
7510
7511       if (can_clone)            // randomly find an element to clone
7512       {
7513         can_clone = FALSE;
7514
7515         start_pos = check_pos[RND(8)];
7516         check_order = (RND(2) ? -1 : +1);
7517
7518         for (i = 0; i < 8; i++)
7519         {
7520           int pos_raw = start_pos + i * check_order;
7521           int pos = (pos_raw + 8) % 8;
7522           int newx = x + check_xy[pos].dx;
7523           int newy = y + check_xy[pos].dy;
7524
7525           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7526           {
7527             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7528             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7529
7530             Store[x][y] = Tile[newx][newy];
7531
7532             can_clone = TRUE;
7533
7534             break;
7535           }
7536         }
7537       }
7538
7539       if (can_clone)            // randomly find a direction to move
7540       {
7541         can_clone = FALSE;
7542
7543         start_pos = check_pos[RND(8)];
7544         check_order = (RND(2) ? -1 : +1);
7545
7546         for (i = 0; i < 8; i++)
7547         {
7548           int pos_raw = start_pos + i * check_order;
7549           int pos = (pos_raw + 8) % 8;
7550           int newx = x + check_xy[pos].dx;
7551           int newy = y + check_xy[pos].dy;
7552           int new_move_dir = check_xy[pos].dir;
7553
7554           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7555           {
7556             MovDir[x][y] = new_move_dir;
7557             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7558
7559             can_clone = TRUE;
7560
7561             break;
7562           }
7563         }
7564       }
7565
7566       if (can_clone)            // cloning and moving successful
7567         return;
7568
7569       // cannot clone -- try to move towards player
7570
7571       start_pos = check_pos[MovDir[x][y] & 0x0f];
7572       check_order = (RND(2) ? -1 : +1);
7573
7574       for (i = 0; i < 3; i++)
7575       {
7576         // first check start_pos, then previous/next or (next/previous) pos
7577         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7578         int pos = (pos_raw + 8) % 8;
7579         int newx = x + check_xy[pos].dx;
7580         int newy = y + check_xy[pos].dy;
7581         int new_move_dir = check_xy[pos].dir;
7582
7583         if (IS_PLAYER(newx, newy))
7584           break;
7585
7586         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7587         {
7588           MovDir[x][y] = new_move_dir;
7589           MovDelay[x][y] = level.android_move_time * 8 + 1;
7590
7591           break;
7592         }
7593       }
7594     }
7595   }
7596   else if (move_pattern == MV_TURNING_LEFT ||
7597            move_pattern == MV_TURNING_RIGHT ||
7598            move_pattern == MV_TURNING_LEFT_RIGHT ||
7599            move_pattern == MV_TURNING_RIGHT_LEFT ||
7600            move_pattern == MV_TURNING_RANDOM ||
7601            move_pattern == MV_ALL_DIRECTIONS)
7602   {
7603     boolean can_turn_left =
7604       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7605     boolean can_turn_right =
7606       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7607
7608     if (element_info[element].move_stepsize == 0)       // "not moving"
7609       return;
7610
7611     if (move_pattern == MV_TURNING_LEFT)
7612       MovDir[x][y] = left_dir;
7613     else if (move_pattern == MV_TURNING_RIGHT)
7614       MovDir[x][y] = right_dir;
7615     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7616       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7617     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7618       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7619     else if (move_pattern == MV_TURNING_RANDOM)
7620       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7621                       can_turn_right && !can_turn_left ? right_dir :
7622                       RND(2) ? left_dir : right_dir);
7623     else if (can_turn_left && can_turn_right)
7624       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7625     else if (can_turn_left)
7626       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7627     else if (can_turn_right)
7628       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7629     else
7630       MovDir[x][y] = back_dir;
7631
7632     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7633   }
7634   else if (move_pattern == MV_HORIZONTAL ||
7635            move_pattern == MV_VERTICAL)
7636   {
7637     if (move_pattern & old_move_dir)
7638       MovDir[x][y] = back_dir;
7639     else if (move_pattern == MV_HORIZONTAL)
7640       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7641     else if (move_pattern == MV_VERTICAL)
7642       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7643
7644     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7645   }
7646   else if (move_pattern & MV_ANY_DIRECTION)
7647   {
7648     MovDir[x][y] = move_pattern;
7649     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7650   }
7651   else if (move_pattern & MV_WIND_DIRECTION)
7652   {
7653     MovDir[x][y] = game.wind_direction;
7654     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7655   }
7656   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7657   {
7658     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7659       MovDir[x][y] = left_dir;
7660     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7661       MovDir[x][y] = right_dir;
7662
7663     if (MovDir[x][y] != old_move_dir)
7664       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7665   }
7666   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7667   {
7668     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7669       MovDir[x][y] = right_dir;
7670     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7671       MovDir[x][y] = left_dir;
7672
7673     if (MovDir[x][y] != old_move_dir)
7674       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7675   }
7676   else if (move_pattern == MV_TOWARDS_PLAYER ||
7677            move_pattern == MV_AWAY_FROM_PLAYER)
7678   {
7679     int attr_x = -1, attr_y = -1;
7680     int newx, newy;
7681     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7682
7683     if (game.all_players_gone)
7684     {
7685       attr_x = game.exit_x;
7686       attr_y = game.exit_y;
7687     }
7688     else
7689     {
7690       int i;
7691
7692       for (i = 0; i < MAX_PLAYERS; i++)
7693       {
7694         struct PlayerInfo *player = &stored_player[i];
7695         int jx = player->jx, jy = player->jy;
7696
7697         if (!player->active)
7698           continue;
7699
7700         if (attr_x == -1 ||
7701             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7702         {
7703           attr_x = jx;
7704           attr_y = jy;
7705         }
7706       }
7707     }
7708
7709     MovDir[x][y] = MV_NONE;
7710     if (attr_x < x)
7711       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7712     else if (attr_x > x)
7713       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7714     if (attr_y < y)
7715       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7716     else if (attr_y > y)
7717       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7718
7719     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7720
7721     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7722     {
7723       boolean first_horiz = RND(2);
7724       int new_move_dir = MovDir[x][y];
7725
7726       if (element_info[element].move_stepsize == 0)     // "not moving"
7727       {
7728         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7729         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7730
7731         return;
7732       }
7733
7734       MovDir[x][y] =
7735         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7736       Moving2Blocked(x, y, &newx, &newy);
7737
7738       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7739         return;
7740
7741       MovDir[x][y] =
7742         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7743       Moving2Blocked(x, y, &newx, &newy);
7744
7745       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7746         return;
7747
7748       MovDir[x][y] = old_move_dir;
7749     }
7750   }
7751   else if (move_pattern == MV_WHEN_PUSHED ||
7752            move_pattern == MV_WHEN_DROPPED)
7753   {
7754     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7755       MovDir[x][y] = MV_NONE;
7756
7757     MovDelay[x][y] = 0;
7758   }
7759   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7760   {
7761     struct XY *test_xy = xy_topdown;
7762     static int test_dir[4] =
7763     {
7764       MV_UP,
7765       MV_LEFT,
7766       MV_RIGHT,
7767       MV_DOWN
7768     };
7769     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7770     int move_preference = -1000000;     // start with very low preference
7771     int new_move_dir = MV_NONE;
7772     int start_test = RND(4);
7773     int i;
7774
7775     for (i = 0; i < NUM_DIRECTIONS; i++)
7776     {
7777       int j = (start_test + i) % 4;
7778       int move_dir = test_dir[j];
7779       int move_dir_preference;
7780
7781       xx = x + test_xy[j].x;
7782       yy = y + test_xy[j].y;
7783
7784       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7785           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7786       {
7787         new_move_dir = move_dir;
7788
7789         break;
7790       }
7791
7792       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7793         continue;
7794
7795       move_dir_preference = -1 * RunnerVisit[xx][yy];
7796       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7797         move_dir_preference = PlayerVisit[xx][yy];
7798
7799       if (move_dir_preference > move_preference)
7800       {
7801         // prefer field that has not been visited for the longest time
7802         move_preference = move_dir_preference;
7803         new_move_dir = move_dir;
7804       }
7805       else if (move_dir_preference == move_preference &&
7806                move_dir == old_move_dir)
7807       {
7808         // prefer last direction when all directions are preferred equally
7809         move_preference = move_dir_preference;
7810         new_move_dir = move_dir;
7811       }
7812     }
7813
7814     MovDir[x][y] = new_move_dir;
7815     if (old_move_dir != new_move_dir)
7816       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7817   }
7818 }
7819
7820 static void TurnRound(int x, int y)
7821 {
7822   int direction = MovDir[x][y];
7823
7824   TurnRoundExt(x, y);
7825
7826   GfxDir[x][y] = MovDir[x][y];
7827
7828   if (direction != MovDir[x][y])
7829     GfxFrame[x][y] = 0;
7830
7831   if (MovDelay[x][y])
7832     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7833
7834   ResetGfxFrame(x, y);
7835 }
7836
7837 static boolean JustBeingPushed(int x, int y)
7838 {
7839   int i;
7840
7841   for (i = 0; i < MAX_PLAYERS; i++)
7842   {
7843     struct PlayerInfo *player = &stored_player[i];
7844
7845     if (player->active && player->is_pushing && player->MovPos)
7846     {
7847       int next_jx = player->jx + (player->jx - player->last_jx);
7848       int next_jy = player->jy + (player->jy - player->last_jy);
7849
7850       if (x == next_jx && y == next_jy)
7851         return TRUE;
7852     }
7853   }
7854
7855   return FALSE;
7856 }
7857
7858 static void StartMoving(int x, int y)
7859 {
7860   boolean started_moving = FALSE;       // some elements can fall _and_ move
7861   int element = Tile[x][y];
7862
7863   if (Stop[x][y])
7864     return;
7865
7866   if (MovDelay[x][y] == 0)
7867     GfxAction[x][y] = ACTION_DEFAULT;
7868
7869   if (CAN_FALL(element) && y < lev_fieldy - 1)
7870   {
7871     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7872         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7873       if (JustBeingPushed(x, y))
7874         return;
7875
7876     if (element == EL_QUICKSAND_FULL)
7877     {
7878       if (IS_FREE(x, y + 1))
7879       {
7880         InitMovingField(x, y, MV_DOWN);
7881         started_moving = TRUE;
7882
7883         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7884 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7885         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7886           Store[x][y] = EL_ROCK;
7887 #else
7888         Store[x][y] = EL_ROCK;
7889 #endif
7890
7891         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7892       }
7893       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7894       {
7895         if (!MovDelay[x][y])
7896         {
7897           MovDelay[x][y] = TILEY + 1;
7898
7899           ResetGfxAnimation(x, y);
7900           ResetGfxAnimation(x, y + 1);
7901         }
7902
7903         if (MovDelay[x][y])
7904         {
7905           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7906           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7907
7908           MovDelay[x][y]--;
7909           if (MovDelay[x][y])
7910             return;
7911         }
7912
7913         Tile[x][y] = EL_QUICKSAND_EMPTY;
7914         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7915         Store[x][y + 1] = Store[x][y];
7916         Store[x][y] = 0;
7917
7918         PlayLevelSoundAction(x, y, ACTION_FILLING);
7919       }
7920       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7921       {
7922         if (!MovDelay[x][y])
7923         {
7924           MovDelay[x][y] = TILEY + 1;
7925
7926           ResetGfxAnimation(x, y);
7927           ResetGfxAnimation(x, y + 1);
7928         }
7929
7930         if (MovDelay[x][y])
7931         {
7932           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7933           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7934
7935           MovDelay[x][y]--;
7936           if (MovDelay[x][y])
7937             return;
7938         }
7939
7940         Tile[x][y] = EL_QUICKSAND_EMPTY;
7941         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7942         Store[x][y + 1] = Store[x][y];
7943         Store[x][y] = 0;
7944
7945         PlayLevelSoundAction(x, y, ACTION_FILLING);
7946       }
7947     }
7948     else if (element == EL_QUICKSAND_FAST_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_QUICKSAND_FAST_EMPTYING;
7956 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7957         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7958           Store[x][y] = EL_ROCK;
7959 #else
7960         Store[x][y] = EL_ROCK;
7961 #endif
7962
7963         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7964       }
7965       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7966       {
7967         if (!MovDelay[x][y])
7968         {
7969           MovDelay[x][y] = TILEY + 1;
7970
7971           ResetGfxAnimation(x, y);
7972           ResetGfxAnimation(x, y + 1);
7973         }
7974
7975         if (MovDelay[x][y])
7976         {
7977           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7978           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7979
7980           MovDelay[x][y]--;
7981           if (MovDelay[x][y])
7982             return;
7983         }
7984
7985         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7986         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7987         Store[x][y + 1] = Store[x][y];
7988         Store[x][y] = 0;
7989
7990         PlayLevelSoundAction(x, y, ACTION_FILLING);
7991       }
7992       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7993       {
7994         if (!MovDelay[x][y])
7995         {
7996           MovDelay[x][y] = TILEY + 1;
7997
7998           ResetGfxAnimation(x, y);
7999           ResetGfxAnimation(x, y + 1);
8000         }
8001
8002         if (MovDelay[x][y])
8003         {
8004           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8005           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8006
8007           MovDelay[x][y]--;
8008           if (MovDelay[x][y])
8009             return;
8010         }
8011
8012         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8013         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8014         Store[x][y + 1] = Store[x][y];
8015         Store[x][y] = 0;
8016
8017         PlayLevelSoundAction(x, y, ACTION_FILLING);
8018       }
8019     }
8020     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8021              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8022     {
8023       InitMovingField(x, y, MV_DOWN);
8024       started_moving = TRUE;
8025
8026       Tile[x][y] = EL_QUICKSAND_FILLING;
8027       Store[x][y] = element;
8028
8029       PlayLevelSoundAction(x, y, ACTION_FILLING);
8030     }
8031     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8032              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8033     {
8034       InitMovingField(x, y, MV_DOWN);
8035       started_moving = TRUE;
8036
8037       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8038       Store[x][y] = element;
8039
8040       PlayLevelSoundAction(x, y, ACTION_FILLING);
8041     }
8042     else if (element == EL_MAGIC_WALL_FULL)
8043     {
8044       if (IS_FREE(x, y + 1))
8045       {
8046         InitMovingField(x, y, MV_DOWN);
8047         started_moving = TRUE;
8048
8049         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8050         Store[x][y] = EL_CHANGED(Store[x][y]);
8051       }
8052       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8053       {
8054         if (!MovDelay[x][y])
8055           MovDelay[x][y] = TILEY / 4 + 1;
8056
8057         if (MovDelay[x][y])
8058         {
8059           MovDelay[x][y]--;
8060           if (MovDelay[x][y])
8061             return;
8062         }
8063
8064         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8065         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8066         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8067         Store[x][y] = 0;
8068       }
8069     }
8070     else if (element == EL_BD_MAGIC_WALL_FULL)
8071     {
8072       if (IS_FREE(x, y + 1))
8073       {
8074         InitMovingField(x, y, MV_DOWN);
8075         started_moving = TRUE;
8076
8077         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8078         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8079       }
8080       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8081       {
8082         if (!MovDelay[x][y])
8083           MovDelay[x][y] = TILEY / 4 + 1;
8084
8085         if (MovDelay[x][y])
8086         {
8087           MovDelay[x][y]--;
8088           if (MovDelay[x][y])
8089             return;
8090         }
8091
8092         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8093         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8094         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8095         Store[x][y] = 0;
8096       }
8097     }
8098     else if (element == EL_DC_MAGIC_WALL_FULL)
8099     {
8100       if (IS_FREE(x, y + 1))
8101       {
8102         InitMovingField(x, y, MV_DOWN);
8103         started_moving = TRUE;
8104
8105         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8106         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8107       }
8108       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8109       {
8110         if (!MovDelay[x][y])
8111           MovDelay[x][y] = TILEY / 4 + 1;
8112
8113         if (MovDelay[x][y])
8114         {
8115           MovDelay[x][y]--;
8116           if (MovDelay[x][y])
8117             return;
8118         }
8119
8120         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8121         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8122         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8123         Store[x][y] = 0;
8124       }
8125     }
8126     else if ((CAN_PASS_MAGIC_WALL(element) &&
8127               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8128                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8129              (CAN_PASS_DC_MAGIC_WALL(element) &&
8130               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8131
8132     {
8133       InitMovingField(x, y, MV_DOWN);
8134       started_moving = TRUE;
8135
8136       Tile[x][y] =
8137         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8138          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8139          EL_DC_MAGIC_WALL_FILLING);
8140       Store[x][y] = element;
8141     }
8142     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8143     {
8144       SplashAcid(x, y + 1);
8145
8146       InitMovingField(x, y, MV_DOWN);
8147       started_moving = TRUE;
8148
8149       Store[x][y] = EL_ACID;
8150     }
8151     else if (
8152              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8153               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8154              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8155               CAN_FALL(element) && WasJustFalling[x][y] &&
8156               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8157
8158              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8159               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8160               (Tile[x][y + 1] == EL_BLOCKED)))
8161     {
8162       /* this is needed for a special case not covered by calling "Impact()"
8163          from "ContinueMoving()": if an element moves to a tile directly below
8164          another element which was just falling on that tile (which was empty
8165          in the previous frame), the falling element above would just stop
8166          instead of smashing the element below (in previous version, the above
8167          element was just checked for "moving" instead of "falling", resulting
8168          in incorrect smashes caused by horizontal movement of the above
8169          element; also, the case of the player being the element to smash was
8170          simply not covered here... :-/ ) */
8171
8172       CheckCollision[x][y] = 0;
8173       CheckImpact[x][y] = 0;
8174
8175       Impact(x, y);
8176     }
8177     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8178     {
8179       if (MovDir[x][y] == MV_NONE)
8180       {
8181         InitMovingField(x, y, MV_DOWN);
8182         started_moving = TRUE;
8183       }
8184     }
8185     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8186     {
8187       if (WasJustFalling[x][y]) // prevent animation from being restarted
8188         MovDir[x][y] = MV_DOWN;
8189
8190       InitMovingField(x, y, MV_DOWN);
8191       started_moving = TRUE;
8192     }
8193     else if (element == EL_AMOEBA_DROP)
8194     {
8195       Tile[x][y] = EL_AMOEBA_GROWING;
8196       Store[x][y] = EL_AMOEBA_WET;
8197     }
8198     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8199               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8200              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8201              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8202     {
8203       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8204                                 (IS_FREE(x - 1, y + 1) ||
8205                                  Tile[x - 1][y + 1] == EL_ACID));
8206       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8207                                 (IS_FREE(x + 1, y + 1) ||
8208                                  Tile[x + 1][y + 1] == EL_ACID));
8209       boolean can_fall_any  = (can_fall_left || can_fall_right);
8210       boolean can_fall_both = (can_fall_left && can_fall_right);
8211       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8212
8213       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8214       {
8215         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8216           can_fall_right = FALSE;
8217         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8218           can_fall_left = FALSE;
8219         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8220           can_fall_right = FALSE;
8221         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8222           can_fall_left = FALSE;
8223
8224         can_fall_any  = (can_fall_left || can_fall_right);
8225         can_fall_both = FALSE;
8226       }
8227
8228       if (can_fall_both)
8229       {
8230         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8231           can_fall_right = FALSE;       // slip down on left side
8232         else
8233           can_fall_left = !(can_fall_right = RND(2));
8234
8235         can_fall_both = FALSE;
8236       }
8237
8238       if (can_fall_any)
8239       {
8240         // if not determined otherwise, prefer left side for slipping down
8241         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8242         started_moving = TRUE;
8243       }
8244     }
8245     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8246     {
8247       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8248       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8249       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8250       int belt_dir = game.belt_dir[belt_nr];
8251
8252       if ((belt_dir == MV_LEFT  && left_is_free) ||
8253           (belt_dir == MV_RIGHT && right_is_free))
8254       {
8255         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8256
8257         InitMovingField(x, y, belt_dir);
8258         started_moving = TRUE;
8259
8260         Pushed[x][y] = TRUE;
8261         Pushed[nextx][y] = TRUE;
8262
8263         GfxAction[x][y] = ACTION_DEFAULT;
8264       }
8265       else
8266       {
8267         MovDir[x][y] = 0;       // if element was moving, stop it
8268       }
8269     }
8270   }
8271
8272   // not "else if" because of elements that can fall and move (EL_SPRING)
8273   if (CAN_MOVE(element) && !started_moving)
8274   {
8275     int move_pattern = element_info[element].move_pattern;
8276     int newx, newy;
8277
8278     Moving2Blocked(x, y, &newx, &newy);
8279
8280     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8281       return;
8282
8283     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8284         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8285     {
8286       WasJustMoving[x][y] = 0;
8287       CheckCollision[x][y] = 0;
8288
8289       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8290
8291       if (Tile[x][y] != element)        // element has changed
8292         return;
8293     }
8294
8295     if (!MovDelay[x][y])        // start new movement phase
8296     {
8297       // all objects that can change their move direction after each step
8298       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8299
8300       if (element != EL_YAMYAM &&
8301           element != EL_DARK_YAMYAM &&
8302           element != EL_PACMAN &&
8303           !(move_pattern & MV_ANY_DIRECTION) &&
8304           move_pattern != MV_TURNING_LEFT &&
8305           move_pattern != MV_TURNING_RIGHT &&
8306           move_pattern != MV_TURNING_LEFT_RIGHT &&
8307           move_pattern != MV_TURNING_RIGHT_LEFT &&
8308           move_pattern != MV_TURNING_RANDOM)
8309       {
8310         TurnRound(x, y);
8311
8312         if (MovDelay[x][y] && (element == EL_BUG ||
8313                                element == EL_SPACESHIP ||
8314                                element == EL_SP_SNIKSNAK ||
8315                                element == EL_SP_ELECTRON ||
8316                                element == EL_MOLE))
8317           TEST_DrawLevelField(x, y);
8318       }
8319     }
8320
8321     if (MovDelay[x][y])         // wait some time before next movement
8322     {
8323       MovDelay[x][y]--;
8324
8325       if (element == EL_ROBOT ||
8326           element == EL_YAMYAM ||
8327           element == EL_DARK_YAMYAM)
8328       {
8329         DrawLevelElementAnimationIfNeeded(x, y, element);
8330         PlayLevelSoundAction(x, y, ACTION_WAITING);
8331       }
8332       else if (element == EL_SP_ELECTRON)
8333         DrawLevelElementAnimationIfNeeded(x, y, element);
8334       else if (element == EL_DRAGON)
8335       {
8336         int i;
8337         int dir = MovDir[x][y];
8338         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8339         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8340         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8341                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8342                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8343                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8344         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8345
8346         GfxAction[x][y] = ACTION_ATTACKING;
8347
8348         if (IS_PLAYER(x, y))
8349           DrawPlayerField(x, y);
8350         else
8351           TEST_DrawLevelField(x, y);
8352
8353         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8354
8355         for (i = 1; i <= 3; i++)
8356         {
8357           int xx = x + i * dx;
8358           int yy = y + i * dy;
8359           int sx = SCREENX(xx);
8360           int sy = SCREENY(yy);
8361           int flame_graphic = graphic + (i - 1);
8362
8363           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8364             break;
8365
8366           if (MovDelay[x][y])
8367           {
8368             int flamed = MovingOrBlocked2Element(xx, yy);
8369
8370             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8371               Bang(xx, yy);
8372             else
8373               RemoveMovingField(xx, yy);
8374
8375             ChangeDelay[xx][yy] = 0;
8376
8377             Tile[xx][yy] = EL_FLAMES;
8378
8379             if (IN_SCR_FIELD(sx, sy))
8380             {
8381               TEST_DrawLevelFieldCrumbled(xx, yy);
8382               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8383             }
8384           }
8385           else
8386           {
8387             if (Tile[xx][yy] == EL_FLAMES)
8388               Tile[xx][yy] = EL_EMPTY;
8389             TEST_DrawLevelField(xx, yy);
8390           }
8391         }
8392       }
8393
8394       if (MovDelay[x][y])       // element still has to wait some time
8395       {
8396         PlayLevelSoundAction(x, y, ACTION_WAITING);
8397
8398         return;
8399       }
8400     }
8401
8402     // now make next step
8403
8404     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8405
8406     if (DONT_COLLIDE_WITH(element) &&
8407         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8408         !PLAYER_ENEMY_PROTECTED(newx, newy))
8409     {
8410       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8411
8412       return;
8413     }
8414
8415     else if (CAN_MOVE_INTO_ACID(element) &&
8416              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8417              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8418              (MovDir[x][y] == MV_DOWN ||
8419               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8420     {
8421       SplashAcid(newx, newy);
8422       Store[x][y] = EL_ACID;
8423     }
8424     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8425     {
8426       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8427           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8428           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8429           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8430       {
8431         RemoveField(x, y);
8432         TEST_DrawLevelField(x, y);
8433
8434         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8435         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8436           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8437
8438         game.friends_still_needed--;
8439         if (!game.friends_still_needed &&
8440             !game.GameOver &&
8441             game.all_players_gone)
8442           LevelSolved();
8443
8444         return;
8445       }
8446       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8447       {
8448         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8449           TEST_DrawLevelField(newx, newy);
8450         else
8451           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8452       }
8453       else if (!IS_FREE(newx, newy))
8454       {
8455         GfxAction[x][y] = ACTION_WAITING;
8456
8457         if (IS_PLAYER(x, y))
8458           DrawPlayerField(x, y);
8459         else
8460           TEST_DrawLevelField(x, y);
8461
8462         return;
8463       }
8464     }
8465     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8466     {
8467       if (IS_FOOD_PIG(Tile[newx][newy]))
8468       {
8469         if (IS_MOVING(newx, newy))
8470           RemoveMovingField(newx, newy);
8471         else
8472         {
8473           Tile[newx][newy] = EL_EMPTY;
8474           TEST_DrawLevelField(newx, newy);
8475         }
8476
8477         PlayLevelSound(x, y, SND_PIG_DIGGING);
8478       }
8479       else if (!IS_FREE(newx, newy))
8480       {
8481         if (IS_PLAYER(x, y))
8482           DrawPlayerField(x, y);
8483         else
8484           TEST_DrawLevelField(x, y);
8485
8486         return;
8487       }
8488     }
8489     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8490     {
8491       if (Store[x][y] != EL_EMPTY)
8492       {
8493         boolean can_clone = FALSE;
8494         int xx, yy;
8495
8496         // check if element to clone is still there
8497         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8498         {
8499           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8500           {
8501             can_clone = TRUE;
8502
8503             break;
8504           }
8505         }
8506
8507         // cannot clone or target field not free anymore -- do not clone
8508         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8509           Store[x][y] = EL_EMPTY;
8510       }
8511
8512       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8513       {
8514         if (IS_MV_DIAGONAL(MovDir[x][y]))
8515         {
8516           int diagonal_move_dir = MovDir[x][y];
8517           int stored = Store[x][y];
8518           int change_delay = 8;
8519           int graphic;
8520
8521           // android is moving diagonally
8522
8523           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8524
8525           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8526           GfxElement[x][y] = EL_EMC_ANDROID;
8527           GfxAction[x][y] = ACTION_SHRINKING;
8528           GfxDir[x][y] = diagonal_move_dir;
8529           ChangeDelay[x][y] = change_delay;
8530
8531           if (Store[x][y] == EL_EMPTY)
8532             Store[x][y] = GfxElementEmpty[x][y];
8533
8534           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8535                                    GfxDir[x][y]);
8536
8537           DrawLevelGraphicAnimation(x, y, graphic);
8538           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8539
8540           if (Tile[newx][newy] == EL_ACID)
8541           {
8542             SplashAcid(newx, newy);
8543
8544             return;
8545           }
8546
8547           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8548
8549           Store[newx][newy] = EL_EMC_ANDROID;
8550           GfxElement[newx][newy] = EL_EMC_ANDROID;
8551           GfxAction[newx][newy] = ACTION_GROWING;
8552           GfxDir[newx][newy] = diagonal_move_dir;
8553           ChangeDelay[newx][newy] = change_delay;
8554
8555           graphic = el_act_dir2img(GfxElement[newx][newy],
8556                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8557
8558           DrawLevelGraphicAnimation(newx, newy, graphic);
8559           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8560
8561           return;
8562         }
8563         else
8564         {
8565           Tile[newx][newy] = EL_EMPTY;
8566           TEST_DrawLevelField(newx, newy);
8567
8568           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8569         }
8570       }
8571       else if (!IS_FREE(newx, newy))
8572       {
8573         return;
8574       }
8575     }
8576     else if (IS_CUSTOM_ELEMENT(element) &&
8577              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8578     {
8579       if (!DigFieldByCE(newx, newy, element))
8580         return;
8581
8582       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8583       {
8584         RunnerVisit[x][y] = FrameCounter;
8585         PlayerVisit[x][y] /= 8;         // expire player visit path
8586       }
8587     }
8588     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8589     {
8590       if (!IS_FREE(newx, newy))
8591       {
8592         if (IS_PLAYER(x, y))
8593           DrawPlayerField(x, y);
8594         else
8595           TEST_DrawLevelField(x, y);
8596
8597         return;
8598       }
8599       else
8600       {
8601         boolean wanna_flame = !RND(10);
8602         int dx = newx - x, dy = newy - y;
8603         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8604         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8605         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8606                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8607         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8608                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8609
8610         if ((wanna_flame ||
8611              IS_CLASSIC_ENEMY(element1) ||
8612              IS_CLASSIC_ENEMY(element2)) &&
8613             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8614             element1 != EL_FLAMES && element2 != EL_FLAMES)
8615         {
8616           ResetGfxAnimation(x, y);
8617           GfxAction[x][y] = ACTION_ATTACKING;
8618
8619           if (IS_PLAYER(x, y))
8620             DrawPlayerField(x, y);
8621           else
8622             TEST_DrawLevelField(x, y);
8623
8624           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8625
8626           MovDelay[x][y] = 50;
8627
8628           Tile[newx][newy] = EL_FLAMES;
8629           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8630             Tile[newx1][newy1] = EL_FLAMES;
8631           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8632             Tile[newx2][newy2] = EL_FLAMES;
8633
8634           return;
8635         }
8636       }
8637     }
8638     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8639              Tile[newx][newy] == EL_DIAMOND)
8640     {
8641       if (IS_MOVING(newx, newy))
8642         RemoveMovingField(newx, newy);
8643       else
8644       {
8645         Tile[newx][newy] = EL_EMPTY;
8646         TEST_DrawLevelField(newx, newy);
8647       }
8648
8649       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8650     }
8651     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8652              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8653     {
8654       if (AmoebaNr[newx][newy])
8655       {
8656         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8657         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8658             Tile[newx][newy] == EL_BD_AMOEBA)
8659           AmoebaCnt[AmoebaNr[newx][newy]]--;
8660       }
8661
8662       if (IS_MOVING(newx, newy))
8663       {
8664         RemoveMovingField(newx, newy);
8665       }
8666       else
8667       {
8668         Tile[newx][newy] = EL_EMPTY;
8669         TEST_DrawLevelField(newx, newy);
8670       }
8671
8672       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8673     }
8674     else if ((element == EL_PACMAN || element == EL_MOLE)
8675              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8676     {
8677       if (AmoebaNr[newx][newy])
8678       {
8679         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8680         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8681             Tile[newx][newy] == EL_BD_AMOEBA)
8682           AmoebaCnt[AmoebaNr[newx][newy]]--;
8683       }
8684
8685       if (element == EL_MOLE)
8686       {
8687         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8688         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8689
8690         ResetGfxAnimation(x, y);
8691         GfxAction[x][y] = ACTION_DIGGING;
8692         TEST_DrawLevelField(x, y);
8693
8694         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8695
8696         return;                         // wait for shrinking amoeba
8697       }
8698       else      // element == EL_PACMAN
8699       {
8700         Tile[newx][newy] = EL_EMPTY;
8701         TEST_DrawLevelField(newx, newy);
8702         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8703       }
8704     }
8705     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8706              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8707               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8708     {
8709       // wait for shrinking amoeba to completely disappear
8710       return;
8711     }
8712     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8713     {
8714       // object was running against a wall
8715
8716       TurnRound(x, y);
8717
8718       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8719         DrawLevelElementAnimation(x, y, element);
8720
8721       if (DONT_TOUCH(element))
8722         TestIfBadThingTouchesPlayer(x, y);
8723
8724       return;
8725     }
8726
8727     InitMovingField(x, y, MovDir[x][y]);
8728
8729     PlayLevelSoundAction(x, y, ACTION_MOVING);
8730   }
8731
8732   if (MovDir[x][y])
8733     ContinueMoving(x, y);
8734 }
8735
8736 void ContinueMoving(int x, int y)
8737 {
8738   int element = Tile[x][y];
8739   struct ElementInfo *ei = &element_info[element];
8740   int direction = MovDir[x][y];
8741   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8742   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8743   int newx = x + dx, newy = y + dy;
8744   int stored = Store[x][y];
8745   int stored_new = Store[newx][newy];
8746   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8747   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8748   boolean last_line = (newy == lev_fieldy - 1);
8749   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8750
8751   if (pushed_by_player)         // special case: moving object pushed by player
8752   {
8753     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8754   }
8755   else if (use_step_delay)      // special case: moving object has step delay
8756   {
8757     if (!MovDelay[x][y])
8758       MovPos[x][y] += getElementMoveStepsize(x, y);
8759
8760     if (MovDelay[x][y])
8761       MovDelay[x][y]--;
8762     else
8763       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8764
8765     if (MovDelay[x][y])
8766     {
8767       TEST_DrawLevelField(x, y);
8768
8769       return;   // element is still waiting
8770     }
8771   }
8772   else                          // normal case: generically moving object
8773   {
8774     MovPos[x][y] += getElementMoveStepsize(x, y);
8775   }
8776
8777   if (ABS(MovPos[x][y]) < TILEX)
8778   {
8779     TEST_DrawLevelField(x, y);
8780
8781     return;     // element is still moving
8782   }
8783
8784   // element reached destination field
8785
8786   Tile[x][y] = EL_EMPTY;
8787   Tile[newx][newy] = element;
8788   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8789
8790   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8791   {
8792     element = Tile[newx][newy] = EL_ACID;
8793   }
8794   else if (element == EL_MOLE)
8795   {
8796     Tile[x][y] = EL_SAND;
8797
8798     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8799   }
8800   else if (element == EL_QUICKSAND_FILLING)
8801   {
8802     element = Tile[newx][newy] = get_next_element(element);
8803     Store[newx][newy] = Store[x][y];
8804   }
8805   else if (element == EL_QUICKSAND_EMPTYING)
8806   {
8807     Tile[x][y] = get_next_element(element);
8808     element = Tile[newx][newy] = Store[x][y];
8809   }
8810   else if (element == EL_QUICKSAND_FAST_FILLING)
8811   {
8812     element = Tile[newx][newy] = get_next_element(element);
8813     Store[newx][newy] = Store[x][y];
8814   }
8815   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8816   {
8817     Tile[x][y] = get_next_element(element);
8818     element = Tile[newx][newy] = Store[x][y];
8819   }
8820   else if (element == EL_MAGIC_WALL_FILLING)
8821   {
8822     element = Tile[newx][newy] = get_next_element(element);
8823     if (!game.magic_wall_active)
8824       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8825     Store[newx][newy] = Store[x][y];
8826   }
8827   else if (element == EL_MAGIC_WALL_EMPTYING)
8828   {
8829     Tile[x][y] = get_next_element(element);
8830     if (!game.magic_wall_active)
8831       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8832     element = Tile[newx][newy] = Store[x][y];
8833
8834     InitField(newx, newy, FALSE);
8835   }
8836   else if (element == EL_BD_MAGIC_WALL_FILLING)
8837   {
8838     element = Tile[newx][newy] = get_next_element(element);
8839     if (!game.magic_wall_active)
8840       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8841     Store[newx][newy] = Store[x][y];
8842   }
8843   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8844   {
8845     Tile[x][y] = get_next_element(element);
8846     if (!game.magic_wall_active)
8847       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8848     element = Tile[newx][newy] = Store[x][y];
8849
8850     InitField(newx, newy, FALSE);
8851   }
8852   else if (element == EL_DC_MAGIC_WALL_FILLING)
8853   {
8854     element = Tile[newx][newy] = get_next_element(element);
8855     if (!game.magic_wall_active)
8856       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8857     Store[newx][newy] = Store[x][y];
8858   }
8859   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8860   {
8861     Tile[x][y] = get_next_element(element);
8862     if (!game.magic_wall_active)
8863       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8864     element = Tile[newx][newy] = Store[x][y];
8865
8866     InitField(newx, newy, FALSE);
8867   }
8868   else if (element == EL_AMOEBA_DROPPING)
8869   {
8870     Tile[x][y] = get_next_element(element);
8871     element = Tile[newx][newy] = Store[x][y];
8872   }
8873   else if (element == EL_SOKOBAN_OBJECT)
8874   {
8875     if (Back[x][y])
8876       Tile[x][y] = Back[x][y];
8877
8878     if (Back[newx][newy])
8879       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8880
8881     Back[x][y] = Back[newx][newy] = 0;
8882   }
8883
8884   Store[x][y] = EL_EMPTY;
8885   MovPos[x][y] = 0;
8886   MovDir[x][y] = 0;
8887   MovDelay[x][y] = 0;
8888
8889   MovDelay[newx][newy] = 0;
8890
8891   if (CAN_CHANGE_OR_HAS_ACTION(element))
8892   {
8893     // copy element change control values to new field
8894     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8895     ChangePage[newx][newy]  = ChangePage[x][y];
8896     ChangeCount[newx][newy] = ChangeCount[x][y];
8897     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8898   }
8899
8900   CustomValue[newx][newy] = CustomValue[x][y];
8901
8902   ChangeDelay[x][y] = 0;
8903   ChangePage[x][y] = -1;
8904   ChangeCount[x][y] = 0;
8905   ChangeEvent[x][y] = -1;
8906
8907   CustomValue[x][y] = 0;
8908
8909   // copy animation control values to new field
8910   GfxFrame[newx][newy]  = GfxFrame[x][y];
8911   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8912   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8913   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8914
8915   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8916
8917   // some elements can leave other elements behind after moving
8918   if (ei->move_leave_element != EL_EMPTY &&
8919       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8920       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8921   {
8922     int move_leave_element = ei->move_leave_element;
8923
8924     // this makes it possible to leave the removed element again
8925     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8926       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8927
8928     Tile[x][y] = move_leave_element;
8929
8930     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8931       MovDir[x][y] = direction;
8932
8933     InitField(x, y, FALSE);
8934
8935     if (GFX_CRUMBLED(Tile[x][y]))
8936       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8937
8938     if (IS_PLAYER_ELEMENT(move_leave_element))
8939       RelocatePlayer(x, y, move_leave_element);
8940   }
8941
8942   // do this after checking for left-behind element
8943   ResetGfxAnimation(x, y);      // reset animation values for old field
8944
8945   if (!CAN_MOVE(element) ||
8946       (CAN_FALL(element) && direction == MV_DOWN &&
8947        (element == EL_SPRING ||
8948         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8949         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8950     GfxDir[x][y] = MovDir[newx][newy] = 0;
8951
8952   TEST_DrawLevelField(x, y);
8953   TEST_DrawLevelField(newx, newy);
8954
8955   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8956
8957   // prevent pushed element from moving on in pushed direction
8958   if (pushed_by_player && CAN_MOVE(element) &&
8959       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8960       !(element_info[element].move_pattern & direction))
8961     TurnRound(newx, newy);
8962
8963   // prevent elements on conveyor belt from moving on in last direction
8964   if (pushed_by_conveyor && CAN_FALL(element) &&
8965       direction & MV_HORIZONTAL)
8966     MovDir[newx][newy] = 0;
8967
8968   if (!pushed_by_player)
8969   {
8970     int nextx = newx + dx, nexty = newy + dy;
8971     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8972
8973     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8974
8975     if (CAN_FALL(element) && direction == MV_DOWN)
8976       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8977
8978     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8979       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8980
8981     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8982       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8983   }
8984
8985   if (DONT_TOUCH(element))      // object may be nasty to player or others
8986   {
8987     TestIfBadThingTouchesPlayer(newx, newy);
8988     TestIfBadThingTouchesFriend(newx, newy);
8989
8990     if (!IS_CUSTOM_ELEMENT(element))
8991       TestIfBadThingTouchesOtherBadThing(newx, newy);
8992   }
8993   else if (element == EL_PENGUIN)
8994     TestIfFriendTouchesBadThing(newx, newy);
8995
8996   if (DONT_GET_HIT_BY(element))
8997   {
8998     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8999   }
9000
9001   // give the player one last chance (one more frame) to move away
9002   if (CAN_FALL(element) && direction == MV_DOWN &&
9003       (last_line || (!IS_FREE(x, newy + 1) &&
9004                      (!IS_PLAYER(x, newy + 1) ||
9005                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9006     Impact(x, newy);
9007
9008   if (pushed_by_player && !game.use_change_when_pushing_bug)
9009   {
9010     int push_side = MV_DIR_OPPOSITE(direction);
9011     struct PlayerInfo *player = PLAYERINFO(x, y);
9012
9013     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9014                                player->index_bit, push_side);
9015     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9016                                         player->index_bit, push_side);
9017   }
9018
9019   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9020     MovDelay[newx][newy] = 1;
9021
9022   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9023
9024   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9025   TestIfElementHitsCustomElement(newx, newy, direction);
9026   TestIfPlayerTouchesCustomElement(newx, newy);
9027   TestIfElementTouchesCustomElement(newx, newy);
9028
9029   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9030       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9031     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9032                              MV_DIR_OPPOSITE(direction));
9033 }
9034
9035 int AmoebaNeighbourNr(int ax, int ay)
9036 {
9037   int i;
9038   int element = Tile[ax][ay];
9039   int group_nr = 0;
9040   struct XY *xy = xy_topdown;
9041
9042   for (i = 0; i < NUM_DIRECTIONS; i++)
9043   {
9044     int x = ax + xy[i].x;
9045     int y = ay + xy[i].y;
9046
9047     if (!IN_LEV_FIELD(x, y))
9048       continue;
9049
9050     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9051       group_nr = AmoebaNr[x][y];
9052   }
9053
9054   return group_nr;
9055 }
9056
9057 static void AmoebaMerge(int ax, int ay)
9058 {
9059   int i, x, y, xx, yy;
9060   int new_group_nr = AmoebaNr[ax][ay];
9061   struct XY *xy = xy_topdown;
9062
9063   if (new_group_nr == 0)
9064     return;
9065
9066   for (i = 0; i < NUM_DIRECTIONS; i++)
9067   {
9068     x = ax + xy[i].x;
9069     y = ay + xy[i].y;
9070
9071     if (!IN_LEV_FIELD(x, y))
9072       continue;
9073
9074     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9075          Tile[x][y] == EL_BD_AMOEBA ||
9076          Tile[x][y] == EL_AMOEBA_DEAD) &&
9077         AmoebaNr[x][y] != new_group_nr)
9078     {
9079       int old_group_nr = AmoebaNr[x][y];
9080
9081       if (old_group_nr == 0)
9082         return;
9083
9084       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9085       AmoebaCnt[old_group_nr] = 0;
9086       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9087       AmoebaCnt2[old_group_nr] = 0;
9088
9089       SCAN_PLAYFIELD(xx, yy)
9090       {
9091         if (AmoebaNr[xx][yy] == old_group_nr)
9092           AmoebaNr[xx][yy] = new_group_nr;
9093       }
9094     }
9095   }
9096 }
9097
9098 void AmoebaToDiamond(int ax, int ay)
9099 {
9100   int i, x, y;
9101
9102   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9103   {
9104     int group_nr = AmoebaNr[ax][ay];
9105
9106 #ifdef DEBUG
9107     if (group_nr == 0)
9108     {
9109       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9110       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9111
9112       return;
9113     }
9114 #endif
9115
9116     SCAN_PLAYFIELD(x, y)
9117     {
9118       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9119       {
9120         AmoebaNr[x][y] = 0;
9121         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9122       }
9123     }
9124
9125     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9126                             SND_AMOEBA_TURNING_TO_GEM :
9127                             SND_AMOEBA_TURNING_TO_ROCK));
9128     Bang(ax, ay);
9129   }
9130   else
9131   {
9132     struct XY *xy = xy_topdown;
9133
9134     for (i = 0; i < NUM_DIRECTIONS; i++)
9135     {
9136       x = ax + xy[i].x;
9137       y = ay + xy[i].y;
9138
9139       if (!IN_LEV_FIELD(x, y))
9140         continue;
9141
9142       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9143       {
9144         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9145                               SND_AMOEBA_TURNING_TO_GEM :
9146                               SND_AMOEBA_TURNING_TO_ROCK));
9147         Bang(x, y);
9148       }
9149     }
9150   }
9151 }
9152
9153 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9154 {
9155   int x, y;
9156   int group_nr = AmoebaNr[ax][ay];
9157   boolean done = FALSE;
9158
9159 #ifdef DEBUG
9160   if (group_nr == 0)
9161   {
9162     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9163     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9164
9165     return;
9166   }
9167 #endif
9168
9169   SCAN_PLAYFIELD(x, y)
9170   {
9171     if (AmoebaNr[x][y] == group_nr &&
9172         (Tile[x][y] == EL_AMOEBA_DEAD ||
9173          Tile[x][y] == EL_BD_AMOEBA ||
9174          Tile[x][y] == EL_AMOEBA_GROWING))
9175     {
9176       AmoebaNr[x][y] = 0;
9177       Tile[x][y] = new_element;
9178       InitField(x, y, FALSE);
9179       TEST_DrawLevelField(x, y);
9180       done = TRUE;
9181     }
9182   }
9183
9184   if (done)
9185     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9186                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9187                             SND_BD_AMOEBA_TURNING_TO_GEM));
9188 }
9189
9190 static void AmoebaGrowing(int x, int y)
9191 {
9192   static DelayCounter sound_delay = { 0 };
9193
9194   if (!MovDelay[x][y])          // start new growing cycle
9195   {
9196     MovDelay[x][y] = 7;
9197
9198     if (DelayReached(&sound_delay))
9199     {
9200       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9201       sound_delay.value = 30;
9202     }
9203   }
9204
9205   if (MovDelay[x][y])           // wait some time before growing bigger
9206   {
9207     MovDelay[x][y]--;
9208     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9209     {
9210       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9211                                            6 - MovDelay[x][y]);
9212
9213       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9214     }
9215
9216     if (!MovDelay[x][y])
9217     {
9218       Tile[x][y] = Store[x][y];
9219       Store[x][y] = 0;
9220       TEST_DrawLevelField(x, y);
9221     }
9222   }
9223 }
9224
9225 static void AmoebaShrinking(int x, int y)
9226 {
9227   static DelayCounter sound_delay = { 0 };
9228
9229   if (!MovDelay[x][y])          // start new shrinking cycle
9230   {
9231     MovDelay[x][y] = 7;
9232
9233     if (DelayReached(&sound_delay))
9234       sound_delay.value = 30;
9235   }
9236
9237   if (MovDelay[x][y])           // wait some time before shrinking
9238   {
9239     MovDelay[x][y]--;
9240     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9241     {
9242       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9243                                            6 - MovDelay[x][y]);
9244
9245       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9246     }
9247
9248     if (!MovDelay[x][y])
9249     {
9250       Tile[x][y] = EL_EMPTY;
9251       TEST_DrawLevelField(x, y);
9252
9253       // don't let mole enter this field in this cycle;
9254       // (give priority to objects falling to this field from above)
9255       Stop[x][y] = TRUE;
9256     }
9257   }
9258 }
9259
9260 static void AmoebaReproduce(int ax, int ay)
9261 {
9262   int i;
9263   int element = Tile[ax][ay];
9264   int graphic = el2img(element);
9265   int newax = ax, neway = ay;
9266   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9267   struct XY *xy = xy_topdown;
9268
9269   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9270   {
9271     Tile[ax][ay] = EL_AMOEBA_DEAD;
9272     TEST_DrawLevelField(ax, ay);
9273     return;
9274   }
9275
9276   if (IS_ANIMATED(graphic))
9277     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9278
9279   if (!MovDelay[ax][ay])        // start making new amoeba field
9280     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9281
9282   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9283   {
9284     MovDelay[ax][ay]--;
9285     if (MovDelay[ax][ay])
9286       return;
9287   }
9288
9289   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9290   {
9291     int start = RND(4);
9292     int x = ax + xy[start].x;
9293     int y = ay + xy[start].y;
9294
9295     if (!IN_LEV_FIELD(x, y))
9296       return;
9297
9298     if (IS_FREE(x, y) ||
9299         CAN_GROW_INTO(Tile[x][y]) ||
9300         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9301         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9302     {
9303       newax = x;
9304       neway = y;
9305     }
9306
9307     if (newax == ax && neway == ay)
9308       return;
9309   }
9310   else                          // normal or "filled" (BD style) amoeba
9311   {
9312     int start = RND(4);
9313     boolean waiting_for_player = FALSE;
9314
9315     for (i = 0; i < NUM_DIRECTIONS; i++)
9316     {
9317       int j = (start + i) % 4;
9318       int x = ax + xy[j].x;
9319       int y = ay + xy[j].y;
9320
9321       if (!IN_LEV_FIELD(x, y))
9322         continue;
9323
9324       if (IS_FREE(x, y) ||
9325           CAN_GROW_INTO(Tile[x][y]) ||
9326           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9327           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9328       {
9329         newax = x;
9330         neway = y;
9331         break;
9332       }
9333       else if (IS_PLAYER(x, y))
9334         waiting_for_player = TRUE;
9335     }
9336
9337     if (newax == ax && neway == ay)             // amoeba cannot grow
9338     {
9339       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9340       {
9341         Tile[ax][ay] = EL_AMOEBA_DEAD;
9342         TEST_DrawLevelField(ax, ay);
9343         AmoebaCnt[AmoebaNr[ax][ay]]--;
9344
9345         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9346         {
9347           if (element == EL_AMOEBA_FULL)
9348             AmoebaToDiamond(ax, ay);
9349           else if (element == EL_BD_AMOEBA)
9350             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9351         }
9352       }
9353       return;
9354     }
9355     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9356     {
9357       // amoeba gets larger by growing in some direction
9358
9359       int new_group_nr = AmoebaNr[ax][ay];
9360
9361 #ifdef DEBUG
9362   if (new_group_nr == 0)
9363   {
9364     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9365           newax, neway);
9366     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9367
9368     return;
9369   }
9370 #endif
9371
9372       AmoebaNr[newax][neway] = new_group_nr;
9373       AmoebaCnt[new_group_nr]++;
9374       AmoebaCnt2[new_group_nr]++;
9375
9376       // if amoeba touches other amoeba(s) after growing, unify them
9377       AmoebaMerge(newax, neway);
9378
9379       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9380       {
9381         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9382         return;
9383       }
9384     }
9385   }
9386
9387   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9388       (neway == lev_fieldy - 1 && newax != ax))
9389   {
9390     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9391     Store[newax][neway] = element;
9392   }
9393   else if (neway == ay || element == EL_EMC_DRIPPER)
9394   {
9395     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9396
9397     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9398   }
9399   else
9400   {
9401     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9402     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9403     Store[ax][ay] = EL_AMOEBA_DROP;
9404     ContinueMoving(ax, ay);
9405     return;
9406   }
9407
9408   TEST_DrawLevelField(newax, neway);
9409 }
9410
9411 static void Life(int ax, int ay)
9412 {
9413   int x1, y1, x2, y2;
9414   int life_time = 40;
9415   int element = Tile[ax][ay];
9416   int graphic = el2img(element);
9417   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9418                          level.biomaze);
9419   boolean changed = FALSE;
9420
9421   if (IS_ANIMATED(graphic))
9422     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9423
9424   if (Stop[ax][ay])
9425     return;
9426
9427   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9428     MovDelay[ax][ay] = life_time;
9429
9430   if (MovDelay[ax][ay])         // wait some time before next cycle
9431   {
9432     MovDelay[ax][ay]--;
9433     if (MovDelay[ax][ay])
9434       return;
9435   }
9436
9437   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9438   {
9439     int xx = ax+x1, yy = ay+y1;
9440     int old_element = Tile[xx][yy];
9441     int num_neighbours = 0;
9442
9443     if (!IN_LEV_FIELD(xx, yy))
9444       continue;
9445
9446     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9447     {
9448       int x = xx+x2, y = yy+y2;
9449
9450       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9451         continue;
9452
9453       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9454       boolean is_neighbour = FALSE;
9455
9456       if (level.use_life_bugs)
9457         is_neighbour =
9458           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9459            (IS_FREE(x, y)                             &&  Stop[x][y]));
9460       else
9461         is_neighbour =
9462           (Last[x][y] == element || is_player_cell);
9463
9464       if (is_neighbour)
9465         num_neighbours++;
9466     }
9467
9468     boolean is_free = FALSE;
9469
9470     if (level.use_life_bugs)
9471       is_free = (IS_FREE(xx, yy));
9472     else
9473       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9474
9475     if (xx == ax && yy == ay)           // field in the middle
9476     {
9477       if (num_neighbours < life_parameter[0] ||
9478           num_neighbours > life_parameter[1])
9479       {
9480         Tile[xx][yy] = EL_EMPTY;
9481         if (Tile[xx][yy] != old_element)
9482           TEST_DrawLevelField(xx, yy);
9483         Stop[xx][yy] = TRUE;
9484         changed = TRUE;
9485       }
9486     }
9487     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9488     {                                   // free border field
9489       if (num_neighbours >= life_parameter[2] &&
9490           num_neighbours <= life_parameter[3])
9491       {
9492         Tile[xx][yy] = element;
9493         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9494         if (Tile[xx][yy] != old_element)
9495           TEST_DrawLevelField(xx, yy);
9496         Stop[xx][yy] = TRUE;
9497         changed = TRUE;
9498       }
9499     }
9500   }
9501
9502   if (changed)
9503     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9504                    SND_GAME_OF_LIFE_GROWING);
9505 }
9506
9507 static void InitRobotWheel(int x, int y)
9508 {
9509   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9510 }
9511
9512 static void RunRobotWheel(int x, int y)
9513 {
9514   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9515 }
9516
9517 static void StopRobotWheel(int x, int y)
9518 {
9519   if (game.robot_wheel_x == x &&
9520       game.robot_wheel_y == y)
9521   {
9522     game.robot_wheel_x = -1;
9523     game.robot_wheel_y = -1;
9524     game.robot_wheel_active = FALSE;
9525   }
9526 }
9527
9528 static void InitTimegateWheel(int x, int y)
9529 {
9530   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9531 }
9532
9533 static void RunTimegateWheel(int x, int y)
9534 {
9535   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9536 }
9537
9538 static void InitMagicBallDelay(int x, int y)
9539 {
9540   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9541 }
9542
9543 static void ActivateMagicBall(int bx, int by)
9544 {
9545   int x, y;
9546
9547   if (level.ball_random)
9548   {
9549     int pos_border = RND(8);    // select one of the eight border elements
9550     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9551     int xx = pos_content % 3;
9552     int yy = pos_content / 3;
9553
9554     x = bx - 1 + xx;
9555     y = by - 1 + yy;
9556
9557     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9558       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9559   }
9560   else
9561   {
9562     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9563     {
9564       int xx = x - bx + 1;
9565       int yy = y - by + 1;
9566
9567       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9568         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9569     }
9570   }
9571
9572   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9573 }
9574
9575 static void CheckExit(int x, int y)
9576 {
9577   if (game.gems_still_needed > 0 ||
9578       game.sokoban_fields_still_needed > 0 ||
9579       game.sokoban_objects_still_needed > 0 ||
9580       game.lights_still_needed > 0)
9581   {
9582     int element = Tile[x][y];
9583     int graphic = el2img(element);
9584
9585     if (IS_ANIMATED(graphic))
9586       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9587
9588     return;
9589   }
9590
9591   // do not re-open exit door closed after last player
9592   if (game.all_players_gone)
9593     return;
9594
9595   Tile[x][y] = EL_EXIT_OPENING;
9596
9597   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9598 }
9599
9600 static void CheckExitEM(int x, int y)
9601 {
9602   if (game.gems_still_needed > 0 ||
9603       game.sokoban_fields_still_needed > 0 ||
9604       game.sokoban_objects_still_needed > 0 ||
9605       game.lights_still_needed > 0)
9606   {
9607     int element = Tile[x][y];
9608     int graphic = el2img(element);
9609
9610     if (IS_ANIMATED(graphic))
9611       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9612
9613     return;
9614   }
9615
9616   // do not re-open exit door closed after last player
9617   if (game.all_players_gone)
9618     return;
9619
9620   Tile[x][y] = EL_EM_EXIT_OPENING;
9621
9622   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9623 }
9624
9625 static void CheckExitSteel(int x, int y)
9626 {
9627   if (game.gems_still_needed > 0 ||
9628       game.sokoban_fields_still_needed > 0 ||
9629       game.sokoban_objects_still_needed > 0 ||
9630       game.lights_still_needed > 0)
9631   {
9632     int element = Tile[x][y];
9633     int graphic = el2img(element);
9634
9635     if (IS_ANIMATED(graphic))
9636       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9637
9638     return;
9639   }
9640
9641   // do not re-open exit door closed after last player
9642   if (game.all_players_gone)
9643     return;
9644
9645   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9646
9647   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9648 }
9649
9650 static void CheckExitSteelEM(int x, int y)
9651 {
9652   if (game.gems_still_needed > 0 ||
9653       game.sokoban_fields_still_needed > 0 ||
9654       game.sokoban_objects_still_needed > 0 ||
9655       game.lights_still_needed > 0)
9656   {
9657     int element = Tile[x][y];
9658     int graphic = el2img(element);
9659
9660     if (IS_ANIMATED(graphic))
9661       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9662
9663     return;
9664   }
9665
9666   // do not re-open exit door closed after last player
9667   if (game.all_players_gone)
9668     return;
9669
9670   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9671
9672   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9673 }
9674
9675 static void CheckExitSP(int x, int y)
9676 {
9677   if (game.gems_still_needed > 0)
9678   {
9679     int element = Tile[x][y];
9680     int graphic = el2img(element);
9681
9682     if (IS_ANIMATED(graphic))
9683       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9684
9685     return;
9686   }
9687
9688   // do not re-open exit door closed after last player
9689   if (game.all_players_gone)
9690     return;
9691
9692   Tile[x][y] = EL_SP_EXIT_OPENING;
9693
9694   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9695 }
9696
9697 static void CloseAllOpenTimegates(void)
9698 {
9699   int x, y;
9700
9701   SCAN_PLAYFIELD(x, y)
9702   {
9703     int element = Tile[x][y];
9704
9705     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9706     {
9707       Tile[x][y] = EL_TIMEGATE_CLOSING;
9708
9709       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9710     }
9711   }
9712 }
9713
9714 static void DrawTwinkleOnField(int x, int y)
9715 {
9716   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9717     return;
9718
9719   if (Tile[x][y] == EL_BD_DIAMOND)
9720     return;
9721
9722   if (MovDelay[x][y] == 0)      // next animation frame
9723     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9724
9725   if (MovDelay[x][y] != 0)      // wait some time before next frame
9726   {
9727     MovDelay[x][y]--;
9728
9729     DrawLevelElementAnimation(x, y, Tile[x][y]);
9730
9731     if (MovDelay[x][y] != 0)
9732     {
9733       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9734                                            10 - MovDelay[x][y]);
9735
9736       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9737     }
9738   }
9739 }
9740
9741 static void WallGrowing(int x, int y)
9742 {
9743   int delay = 6;
9744
9745   if (!MovDelay[x][y])          // next animation frame
9746     MovDelay[x][y] = 3 * delay;
9747
9748   if (MovDelay[x][y])           // wait some time before next frame
9749   {
9750     MovDelay[x][y]--;
9751
9752     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9753     {
9754       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9755       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9756
9757       DrawLevelGraphic(x, y, graphic, frame);
9758     }
9759
9760     if (!MovDelay[x][y])
9761     {
9762       if (MovDir[x][y] == MV_LEFT)
9763       {
9764         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9765           TEST_DrawLevelField(x - 1, y);
9766       }
9767       else if (MovDir[x][y] == MV_RIGHT)
9768       {
9769         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9770           TEST_DrawLevelField(x + 1, y);
9771       }
9772       else if (MovDir[x][y] == MV_UP)
9773       {
9774         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9775           TEST_DrawLevelField(x, y - 1);
9776       }
9777       else
9778       {
9779         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9780           TEST_DrawLevelField(x, y + 1);
9781       }
9782
9783       Tile[x][y] = Store[x][y];
9784       Store[x][y] = 0;
9785       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9786       TEST_DrawLevelField(x, y);
9787     }
9788   }
9789 }
9790
9791 static void CheckWallGrowing(int ax, int ay)
9792 {
9793   int element = Tile[ax][ay];
9794   int graphic = el2img(element);
9795   boolean free_top    = FALSE;
9796   boolean free_bottom = FALSE;
9797   boolean free_left   = FALSE;
9798   boolean free_right  = FALSE;
9799   boolean stop_top    = FALSE;
9800   boolean stop_bottom = FALSE;
9801   boolean stop_left   = FALSE;
9802   boolean stop_right  = FALSE;
9803   boolean new_wall    = FALSE;
9804
9805   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9806                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9807                            element == EL_EXPANDABLE_STEELWALL_ANY);
9808
9809   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9810                              element == EL_EXPANDABLE_WALL_ANY ||
9811                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9812                              element == EL_EXPANDABLE_STEELWALL_ANY);
9813
9814   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9815                              element == EL_EXPANDABLE_WALL_ANY ||
9816                              element == EL_EXPANDABLE_WALL ||
9817                              element == EL_BD_EXPANDABLE_WALL ||
9818                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9819                              element == EL_EXPANDABLE_STEELWALL_ANY);
9820
9821   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9822                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9823
9824   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9825                              element == EL_EXPANDABLE_WALL ||
9826                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9827
9828   int wall_growing = (is_steelwall ?
9829                       EL_EXPANDABLE_STEELWALL_GROWING :
9830                       EL_EXPANDABLE_WALL_GROWING);
9831
9832   int gfx_wall_growing_up    = (is_steelwall ?
9833                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9834                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9835   int gfx_wall_growing_down  = (is_steelwall ?
9836                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9837                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9838   int gfx_wall_growing_left  = (is_steelwall ?
9839                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9840                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9841   int gfx_wall_growing_right = (is_steelwall ?
9842                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9843                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9844
9845   if (IS_ANIMATED(graphic))
9846     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9847
9848   if (!MovDelay[ax][ay])        // start building new wall
9849     MovDelay[ax][ay] = 6;
9850
9851   if (MovDelay[ax][ay])         // wait some time before building new wall
9852   {
9853     MovDelay[ax][ay]--;
9854     if (MovDelay[ax][ay])
9855       return;
9856   }
9857
9858   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9859     free_top = TRUE;
9860   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9861     free_bottom = TRUE;
9862   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9863     free_left = TRUE;
9864   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9865     free_right = TRUE;
9866
9867   if (grow_vertical)
9868   {
9869     if (free_top)
9870     {
9871       Tile[ax][ay - 1] = wall_growing;
9872       Store[ax][ay - 1] = element;
9873       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9874
9875       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9876         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9877
9878       new_wall = TRUE;
9879     }
9880
9881     if (free_bottom)
9882     {
9883       Tile[ax][ay + 1] = wall_growing;
9884       Store[ax][ay + 1] = element;
9885       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9886
9887       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9888         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9889
9890       new_wall = TRUE;
9891     }
9892   }
9893
9894   if (grow_horizontal)
9895   {
9896     if (free_left)
9897     {
9898       Tile[ax - 1][ay] = wall_growing;
9899       Store[ax - 1][ay] = element;
9900       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9901
9902       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9903         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9904
9905       new_wall = TRUE;
9906     }
9907
9908     if (free_right)
9909     {
9910       Tile[ax + 1][ay] = wall_growing;
9911       Store[ax + 1][ay] = element;
9912       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9913
9914       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9915         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9916
9917       new_wall = TRUE;
9918     }
9919   }
9920
9921   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9922     TEST_DrawLevelField(ax, ay);
9923
9924   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9925     stop_top = TRUE;
9926   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9927     stop_bottom = TRUE;
9928   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9929     stop_left = TRUE;
9930   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9931     stop_right = TRUE;
9932
9933   if (((stop_top && stop_bottom) || stop_horizontal) &&
9934       ((stop_left && stop_right) || stop_vertical))
9935     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9936
9937   if (new_wall)
9938     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9939 }
9940
9941 static void CheckForDragon(int x, int y)
9942 {
9943   int i, j;
9944   boolean dragon_found = FALSE;
9945   struct XY *xy = xy_topdown;
9946
9947   for (i = 0; i < NUM_DIRECTIONS; i++)
9948   {
9949     for (j = 0; j < 4; j++)
9950     {
9951       int xx = x + j * xy[i].x;
9952       int yy = y + j * xy[i].y;
9953
9954       if (IN_LEV_FIELD(xx, yy) &&
9955           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9956       {
9957         if (Tile[xx][yy] == EL_DRAGON)
9958           dragon_found = TRUE;
9959       }
9960       else
9961         break;
9962     }
9963   }
9964
9965   if (!dragon_found)
9966   {
9967     for (i = 0; i < NUM_DIRECTIONS; i++)
9968     {
9969       for (j = 0; j < 3; j++)
9970       {
9971         int xx = x + j * xy[i].x;
9972         int yy = y + j * xy[i].y;
9973
9974         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9975         {
9976           Tile[xx][yy] = EL_EMPTY;
9977           TEST_DrawLevelField(xx, yy);
9978         }
9979         else
9980           break;
9981       }
9982     }
9983   }
9984 }
9985
9986 static void InitBuggyBase(int x, int y)
9987 {
9988   int element = Tile[x][y];
9989   int activating_delay = FRAMES_PER_SECOND / 4;
9990
9991   ChangeDelay[x][y] =
9992     (element == EL_SP_BUGGY_BASE ?
9993      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9994      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9995      activating_delay :
9996      element == EL_SP_BUGGY_BASE_ACTIVE ?
9997      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9998 }
9999
10000 static void WarnBuggyBase(int x, int y)
10001 {
10002   int i;
10003   struct XY *xy = xy_topdown;
10004
10005   for (i = 0; i < NUM_DIRECTIONS; i++)
10006   {
10007     int xx = x + xy[i].x;
10008     int yy = y + xy[i].y;
10009
10010     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10011     {
10012       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10013
10014       break;
10015     }
10016   }
10017 }
10018
10019 static void InitTrap(int x, int y)
10020 {
10021   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10022 }
10023
10024 static void ActivateTrap(int x, int y)
10025 {
10026   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10027 }
10028
10029 static void ChangeActiveTrap(int x, int y)
10030 {
10031   int graphic = IMG_TRAP_ACTIVE;
10032
10033   // if new animation frame was drawn, correct crumbled sand border
10034   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10035     TEST_DrawLevelFieldCrumbled(x, y);
10036 }
10037
10038 static int getSpecialActionElement(int element, int number, int base_element)
10039 {
10040   return (element != EL_EMPTY ? element :
10041           number != -1 ? base_element + number - 1 :
10042           EL_EMPTY);
10043 }
10044
10045 static int getModifiedActionNumber(int value_old, int operator, int operand,
10046                                    int value_min, int value_max)
10047 {
10048   int value_new = (operator == CA_MODE_SET      ? operand :
10049                    operator == CA_MODE_ADD      ? value_old + operand :
10050                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10051                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10052                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10053                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10054                    value_old);
10055
10056   return (value_new < value_min ? value_min :
10057           value_new > value_max ? value_max :
10058           value_new);
10059 }
10060
10061 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10062 {
10063   struct ElementInfo *ei = &element_info[element];
10064   struct ElementChangeInfo *change = &ei->change_page[page];
10065   int target_element = change->target_element;
10066   int action_type = change->action_type;
10067   int action_mode = change->action_mode;
10068   int action_arg = change->action_arg;
10069   int action_element = change->action_element;
10070   int i;
10071
10072   if (!change->has_action)
10073     return;
10074
10075   // ---------- determine action paramater values -----------------------------
10076
10077   int level_time_value =
10078     (level.time > 0 ? TimeLeft :
10079      TimePlayed);
10080
10081   int action_arg_element_raw =
10082     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10083      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10084      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10085      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10086      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10087      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10088      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10089      EL_EMPTY);
10090   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10091
10092   int action_arg_direction =
10093     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10094      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10095      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10096      change->actual_trigger_side :
10097      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10098      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10099      MV_NONE);
10100
10101   int action_arg_number_min =
10102     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10103      CA_ARG_MIN);
10104
10105   int action_arg_number_max =
10106     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10107      action_type == CA_SET_LEVEL_GEMS ? 999 :
10108      action_type == CA_SET_LEVEL_TIME ? 9999 :
10109      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10110      action_type == CA_SET_CE_VALUE ? 9999 :
10111      action_type == CA_SET_CE_SCORE ? 9999 :
10112      CA_ARG_MAX);
10113
10114   int action_arg_number_reset =
10115     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10116      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10117      action_type == CA_SET_LEVEL_TIME ? level.time :
10118      action_type == CA_SET_LEVEL_SCORE ? 0 :
10119      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10120      action_type == CA_SET_CE_SCORE ? 0 :
10121      0);
10122
10123   int action_arg_number =
10124     (action_arg <= CA_ARG_MAX ? action_arg :
10125      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10126      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10127      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10128      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10129      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10130      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10131      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10132      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10133      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10134      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10135      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10136      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10137      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10138      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10139      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10140      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10141      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10142      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10143      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10144      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10145      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10146      -1);
10147
10148   int action_arg_number_old =
10149     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10150      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10151      action_type == CA_SET_LEVEL_SCORE ? game.score :
10152      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10153      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10154      0);
10155
10156   int action_arg_number_new =
10157     getModifiedActionNumber(action_arg_number_old,
10158                             action_mode, action_arg_number,
10159                             action_arg_number_min, action_arg_number_max);
10160
10161   int trigger_player_bits =
10162     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10163      change->actual_trigger_player_bits : change->trigger_player);
10164
10165   int action_arg_player_bits =
10166     (action_arg >= CA_ARG_PLAYER_1 &&
10167      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10168      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10169      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10170      PLAYER_BITS_ANY);
10171
10172   // ---------- execute action  -----------------------------------------------
10173
10174   switch (action_type)
10175   {
10176     case CA_NO_ACTION:
10177     {
10178       return;
10179     }
10180
10181     // ---------- level actions  ----------------------------------------------
10182
10183     case CA_RESTART_LEVEL:
10184     {
10185       game.restart_level = TRUE;
10186
10187       break;
10188     }
10189
10190     case CA_SHOW_ENVELOPE:
10191     {
10192       int element = getSpecialActionElement(action_arg_element,
10193                                             action_arg_number, EL_ENVELOPE_1);
10194
10195       if (IS_ENVELOPE(element))
10196         local_player->show_envelope = element;
10197
10198       break;
10199     }
10200
10201     case CA_SET_LEVEL_TIME:
10202     {
10203       if (level.time > 0)       // only modify limited time value
10204       {
10205         TimeLeft = action_arg_number_new;
10206
10207         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10208
10209         DisplayGameControlValues();
10210
10211         if (!TimeLeft && game.time_limit)
10212           for (i = 0; i < MAX_PLAYERS; i++)
10213             KillPlayer(&stored_player[i]);
10214       }
10215
10216       break;
10217     }
10218
10219     case CA_SET_LEVEL_SCORE:
10220     {
10221       game.score = action_arg_number_new;
10222
10223       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10224
10225       DisplayGameControlValues();
10226
10227       break;
10228     }
10229
10230     case CA_SET_LEVEL_GEMS:
10231     {
10232       game.gems_still_needed = action_arg_number_new;
10233
10234       game.snapshot.collected_item = TRUE;
10235
10236       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10237
10238       DisplayGameControlValues();
10239
10240       break;
10241     }
10242
10243     case CA_SET_LEVEL_WIND:
10244     {
10245       game.wind_direction = action_arg_direction;
10246
10247       break;
10248     }
10249
10250     case CA_SET_LEVEL_RANDOM_SEED:
10251     {
10252       // ensure that setting a new random seed while playing is predictable
10253       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10254
10255       break;
10256     }
10257
10258     // ---------- player actions  ---------------------------------------------
10259
10260     case CA_MOVE_PLAYER:
10261     case CA_MOVE_PLAYER_NEW:
10262     {
10263       // automatically move to the next field in specified direction
10264       for (i = 0; i < MAX_PLAYERS; i++)
10265         if (trigger_player_bits & (1 << i))
10266           if (action_type == CA_MOVE_PLAYER ||
10267               stored_player[i].MovPos == 0)
10268             stored_player[i].programmed_action = action_arg_direction;
10269
10270       break;
10271     }
10272
10273     case CA_EXIT_PLAYER:
10274     {
10275       for (i = 0; i < MAX_PLAYERS; i++)
10276         if (action_arg_player_bits & (1 << i))
10277           ExitPlayer(&stored_player[i]);
10278
10279       if (game.players_still_needed == 0)
10280         LevelSolved();
10281
10282       break;
10283     }
10284
10285     case CA_KILL_PLAYER:
10286     {
10287       for (i = 0; i < MAX_PLAYERS; i++)
10288         if (action_arg_player_bits & (1 << i))
10289           KillPlayer(&stored_player[i]);
10290
10291       break;
10292     }
10293
10294     case CA_SET_PLAYER_KEYS:
10295     {
10296       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10297       int element = getSpecialActionElement(action_arg_element,
10298                                             action_arg_number, EL_KEY_1);
10299
10300       if (IS_KEY(element))
10301       {
10302         for (i = 0; i < MAX_PLAYERS; i++)
10303         {
10304           if (trigger_player_bits & (1 << i))
10305           {
10306             stored_player[i].key[KEY_NR(element)] = key_state;
10307
10308             DrawGameDoorValues();
10309           }
10310         }
10311       }
10312
10313       break;
10314     }
10315
10316     case CA_SET_PLAYER_SPEED:
10317     {
10318       for (i = 0; i < MAX_PLAYERS; i++)
10319       {
10320         if (trigger_player_bits & (1 << i))
10321         {
10322           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10323
10324           if (action_arg == CA_ARG_SPEED_FASTER &&
10325               stored_player[i].cannot_move)
10326           {
10327             action_arg_number = STEPSIZE_VERY_SLOW;
10328           }
10329           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10330                    action_arg == CA_ARG_SPEED_FASTER)
10331           {
10332             action_arg_number = 2;
10333             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10334                            CA_MODE_MULTIPLY);
10335           }
10336           else if (action_arg == CA_ARG_NUMBER_RESET)
10337           {
10338             action_arg_number = level.initial_player_stepsize[i];
10339           }
10340
10341           move_stepsize =
10342             getModifiedActionNumber(move_stepsize,
10343                                     action_mode,
10344                                     action_arg_number,
10345                                     action_arg_number_min,
10346                                     action_arg_number_max);
10347
10348           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10349         }
10350       }
10351
10352       break;
10353     }
10354
10355     case CA_SET_PLAYER_SHIELD:
10356     {
10357       for (i = 0; i < MAX_PLAYERS; i++)
10358       {
10359         if (trigger_player_bits & (1 << i))
10360         {
10361           if (action_arg == CA_ARG_SHIELD_OFF)
10362           {
10363             stored_player[i].shield_normal_time_left = 0;
10364             stored_player[i].shield_deadly_time_left = 0;
10365           }
10366           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10367           {
10368             stored_player[i].shield_normal_time_left = 999999;
10369           }
10370           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10371           {
10372             stored_player[i].shield_normal_time_left = 999999;
10373             stored_player[i].shield_deadly_time_left = 999999;
10374           }
10375         }
10376       }
10377
10378       break;
10379     }
10380
10381     case CA_SET_PLAYER_GRAVITY:
10382     {
10383       for (i = 0; i < MAX_PLAYERS; i++)
10384       {
10385         if (trigger_player_bits & (1 << i))
10386         {
10387           stored_player[i].gravity =
10388             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10389              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10390              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10391              stored_player[i].gravity);
10392         }
10393       }
10394
10395       break;
10396     }
10397
10398     case CA_SET_PLAYER_ARTWORK:
10399     {
10400       for (i = 0; i < MAX_PLAYERS; i++)
10401       {
10402         if (trigger_player_bits & (1 << i))
10403         {
10404           int artwork_element = action_arg_element;
10405
10406           if (action_arg == CA_ARG_ELEMENT_RESET)
10407             artwork_element =
10408               (level.use_artwork_element[i] ? level.artwork_element[i] :
10409                stored_player[i].element_nr);
10410
10411           if (stored_player[i].artwork_element != artwork_element)
10412             stored_player[i].Frame = 0;
10413
10414           stored_player[i].artwork_element = artwork_element;
10415
10416           SetPlayerWaiting(&stored_player[i], FALSE);
10417
10418           // set number of special actions for bored and sleeping animation
10419           stored_player[i].num_special_action_bored =
10420             get_num_special_action(artwork_element,
10421                                    ACTION_BORING_1, ACTION_BORING_LAST);
10422           stored_player[i].num_special_action_sleeping =
10423             get_num_special_action(artwork_element,
10424                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10425         }
10426       }
10427
10428       break;
10429     }
10430
10431     case CA_SET_PLAYER_INVENTORY:
10432     {
10433       for (i = 0; i < MAX_PLAYERS; i++)
10434       {
10435         struct PlayerInfo *player = &stored_player[i];
10436         int j, k;
10437
10438         if (trigger_player_bits & (1 << i))
10439         {
10440           int inventory_element = action_arg_element;
10441
10442           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10443               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10444               action_arg == CA_ARG_ELEMENT_ACTION)
10445           {
10446             int element = inventory_element;
10447             int collect_count = element_info[element].collect_count_initial;
10448
10449             if (!IS_CUSTOM_ELEMENT(element))
10450               collect_count = 1;
10451
10452             if (collect_count == 0)
10453               player->inventory_infinite_element = element;
10454             else
10455               for (k = 0; k < collect_count; k++)
10456                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10457                   player->inventory_element[player->inventory_size++] =
10458                     element;
10459           }
10460           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10461                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10462                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10463           {
10464             if (player->inventory_infinite_element != EL_UNDEFINED &&
10465                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10466                                      action_arg_element_raw))
10467               player->inventory_infinite_element = EL_UNDEFINED;
10468
10469             for (k = 0, j = 0; j < player->inventory_size; j++)
10470             {
10471               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10472                                         action_arg_element_raw))
10473                 player->inventory_element[k++] = player->inventory_element[j];
10474             }
10475
10476             player->inventory_size = k;
10477           }
10478           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10479           {
10480             if (player->inventory_size > 0)
10481             {
10482               for (j = 0; j < player->inventory_size - 1; j++)
10483                 player->inventory_element[j] = player->inventory_element[j + 1];
10484
10485               player->inventory_size--;
10486             }
10487           }
10488           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10489           {
10490             if (player->inventory_size > 0)
10491               player->inventory_size--;
10492           }
10493           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10494           {
10495             player->inventory_infinite_element = EL_UNDEFINED;
10496             player->inventory_size = 0;
10497           }
10498           else if (action_arg == CA_ARG_INVENTORY_RESET)
10499           {
10500             player->inventory_infinite_element = EL_UNDEFINED;
10501             player->inventory_size = 0;
10502
10503             if (level.use_initial_inventory[i])
10504             {
10505               for (j = 0; j < level.initial_inventory_size[i]; j++)
10506               {
10507                 int element = level.initial_inventory_content[i][j];
10508                 int collect_count = element_info[element].collect_count_initial;
10509
10510                 if (!IS_CUSTOM_ELEMENT(element))
10511                   collect_count = 1;
10512
10513                 if (collect_count == 0)
10514                   player->inventory_infinite_element = element;
10515                 else
10516                   for (k = 0; k < collect_count; k++)
10517                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10518                       player->inventory_element[player->inventory_size++] =
10519                         element;
10520               }
10521             }
10522           }
10523         }
10524       }
10525
10526       break;
10527     }
10528
10529     // ---------- CE actions  -------------------------------------------------
10530
10531     case CA_SET_CE_VALUE:
10532     {
10533       int last_ce_value = CustomValue[x][y];
10534
10535       CustomValue[x][y] = action_arg_number_new;
10536
10537       if (CustomValue[x][y] != last_ce_value)
10538       {
10539         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10540         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10541
10542         if (CustomValue[x][y] == 0)
10543         {
10544           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10545           ChangeCount[x][y] = 0;        // allow at least one more change
10546
10547           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10548           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10549         }
10550       }
10551
10552       break;
10553     }
10554
10555     case CA_SET_CE_SCORE:
10556     {
10557       int last_ce_score = ei->collect_score;
10558
10559       ei->collect_score = action_arg_number_new;
10560
10561       if (ei->collect_score != last_ce_score)
10562       {
10563         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10564         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10565
10566         if (ei->collect_score == 0)
10567         {
10568           int xx, yy;
10569
10570           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10571           ChangeCount[x][y] = 0;        // allow at least one more change
10572
10573           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10574           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10575
10576           /*
10577             This is a very special case that seems to be a mixture between
10578             CheckElementChange() and CheckTriggeredElementChange(): while
10579             the first one only affects single elements that are triggered
10580             directly, the second one affects multiple elements in the playfield
10581             that are triggered indirectly by another element. This is a third
10582             case: Changing the CE score always affects multiple identical CEs,
10583             so every affected CE must be checked, not only the single CE for
10584             which the CE score was changed in the first place (as every instance
10585             of that CE shares the same CE score, and therefore also can change)!
10586           */
10587           SCAN_PLAYFIELD(xx, yy)
10588           {
10589             if (Tile[xx][yy] == element)
10590               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10591                                  CE_SCORE_GETS_ZERO);
10592           }
10593         }
10594       }
10595
10596       break;
10597     }
10598
10599     case CA_SET_CE_ARTWORK:
10600     {
10601       int artwork_element = action_arg_element;
10602       boolean reset_frame = FALSE;
10603       int xx, yy;
10604
10605       if (action_arg == CA_ARG_ELEMENT_RESET)
10606         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10607                            element);
10608
10609       if (ei->gfx_element != artwork_element)
10610         reset_frame = TRUE;
10611
10612       ei->gfx_element = artwork_element;
10613
10614       SCAN_PLAYFIELD(xx, yy)
10615       {
10616         if (Tile[xx][yy] == element)
10617         {
10618           if (reset_frame)
10619           {
10620             ResetGfxAnimation(xx, yy);
10621             ResetRandomAnimationValue(xx, yy);
10622           }
10623
10624           TEST_DrawLevelField(xx, yy);
10625         }
10626       }
10627
10628       break;
10629     }
10630
10631     // ---------- engine actions  ---------------------------------------------
10632
10633     case CA_SET_ENGINE_SCAN_MODE:
10634     {
10635       InitPlayfieldScanMode(action_arg);
10636
10637       break;
10638     }
10639
10640     default:
10641       break;
10642   }
10643 }
10644
10645 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10646 {
10647   int old_element = Tile[x][y];
10648   int new_element = GetElementFromGroupElement(element);
10649   int previous_move_direction = MovDir[x][y];
10650   int last_ce_value = CustomValue[x][y];
10651   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10652   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10653   boolean add_player_onto_element = (new_element_is_player &&
10654                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10655                                      IS_WALKABLE(old_element));
10656
10657   if (!add_player_onto_element)
10658   {
10659     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10660       RemoveMovingField(x, y);
10661     else
10662       RemoveField(x, y);
10663
10664     Tile[x][y] = new_element;
10665
10666     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10667       MovDir[x][y] = previous_move_direction;
10668
10669     if (element_info[new_element].use_last_ce_value)
10670       CustomValue[x][y] = last_ce_value;
10671
10672     InitField_WithBug1(x, y, FALSE);
10673
10674     new_element = Tile[x][y];   // element may have changed
10675
10676     ResetGfxAnimation(x, y);
10677     ResetRandomAnimationValue(x, y);
10678
10679     TEST_DrawLevelField(x, y);
10680
10681     if (GFX_CRUMBLED(new_element))
10682       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10683
10684     if (old_element == EL_EXPLOSION)
10685     {
10686       Store[x][y] = Store2[x][y] = 0;
10687
10688       // check if new element replaces an exploding player, requiring cleanup
10689       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10690         StorePlayer[x][y] = 0;
10691     }
10692
10693     // check if element under the player changes from accessible to unaccessible
10694     // (needed for special case of dropping element which then changes)
10695     // (must be checked after creating new element for walkable group elements)
10696     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10697         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10698     {
10699       KillPlayer(PLAYERINFO(x, y));
10700
10701       return;
10702     }
10703   }
10704
10705   // "ChangeCount" not set yet to allow "entered by player" change one time
10706   if (new_element_is_player)
10707     RelocatePlayer(x, y, new_element);
10708
10709   if (is_change)
10710     ChangeCount[x][y]++;        // count number of changes in the same frame
10711
10712   TestIfBadThingTouchesPlayer(x, y);
10713   TestIfPlayerTouchesCustomElement(x, y);
10714   TestIfElementTouchesCustomElement(x, y);
10715 }
10716
10717 static void CreateField(int x, int y, int element)
10718 {
10719   CreateFieldExt(x, y, element, FALSE);
10720 }
10721
10722 static void CreateElementFromChange(int x, int y, int element)
10723 {
10724   element = GET_VALID_RUNTIME_ELEMENT(element);
10725
10726   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10727   {
10728     int old_element = Tile[x][y];
10729
10730     // prevent changed element from moving in same engine frame
10731     // unless both old and new element can either fall or move
10732     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10733         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10734       Stop[x][y] = TRUE;
10735   }
10736
10737   CreateFieldExt(x, y, element, TRUE);
10738 }
10739
10740 static boolean ChangeElement(int x, int y, int element, int page)
10741 {
10742   struct ElementInfo *ei = &element_info[element];
10743   struct ElementChangeInfo *change = &ei->change_page[page];
10744   int ce_value = CustomValue[x][y];
10745   int ce_score = ei->collect_score;
10746   int target_element;
10747   int old_element = Tile[x][y];
10748
10749   // always use default change event to prevent running into a loop
10750   if (ChangeEvent[x][y] == -1)
10751     ChangeEvent[x][y] = CE_DELAY;
10752
10753   if (ChangeEvent[x][y] == CE_DELAY)
10754   {
10755     // reset actual trigger element, trigger player and action element
10756     change->actual_trigger_element = EL_EMPTY;
10757     change->actual_trigger_player = EL_EMPTY;
10758     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10759     change->actual_trigger_side = CH_SIDE_NONE;
10760     change->actual_trigger_ce_value = 0;
10761     change->actual_trigger_ce_score = 0;
10762   }
10763
10764   // do not change elements more than a specified maximum number of changes
10765   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10766     return FALSE;
10767
10768   ChangeCount[x][y]++;          // count number of changes in the same frame
10769
10770   if (ei->has_anim_event)
10771     HandleGlobalAnimEventByElementChange(element, page, x, y);
10772
10773   if (change->explode)
10774   {
10775     Bang(x, y);
10776
10777     return TRUE;
10778   }
10779
10780   if (change->use_target_content)
10781   {
10782     boolean complete_replace = TRUE;
10783     boolean can_replace[3][3];
10784     int xx, yy;
10785
10786     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10787     {
10788       boolean is_empty;
10789       boolean is_walkable;
10790       boolean is_diggable;
10791       boolean is_collectible;
10792       boolean is_removable;
10793       boolean is_destructible;
10794       int ex = x + xx - 1;
10795       int ey = y + yy - 1;
10796       int content_element = change->target_content.e[xx][yy];
10797       int e;
10798
10799       can_replace[xx][yy] = TRUE;
10800
10801       if (ex == x && ey == y)   // do not check changing element itself
10802         continue;
10803
10804       if (content_element == EL_EMPTY_SPACE)
10805       {
10806         can_replace[xx][yy] = FALSE;    // do not replace border with space
10807
10808         continue;
10809       }
10810
10811       if (!IN_LEV_FIELD(ex, ey))
10812       {
10813         can_replace[xx][yy] = FALSE;
10814         complete_replace = FALSE;
10815
10816         continue;
10817       }
10818
10819       e = Tile[ex][ey];
10820
10821       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10822         e = MovingOrBlocked2Element(ex, ey);
10823
10824       is_empty = (IS_FREE(ex, ey) ||
10825                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10826
10827       is_walkable     = (is_empty || IS_WALKABLE(e));
10828       is_diggable     = (is_empty || IS_DIGGABLE(e));
10829       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10830       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10831       is_removable    = (is_diggable || is_collectible);
10832
10833       can_replace[xx][yy] =
10834         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10835           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10836           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10837           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10838           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10839           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10840          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10841
10842       if (!can_replace[xx][yy])
10843         complete_replace = FALSE;
10844     }
10845
10846     if (!change->only_if_complete || complete_replace)
10847     {
10848       boolean something_has_changed = FALSE;
10849
10850       if (change->only_if_complete && change->use_random_replace &&
10851           RND(100) < change->random_percentage)
10852         return FALSE;
10853
10854       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10855       {
10856         int ex = x + xx - 1;
10857         int ey = y + yy - 1;
10858         int content_element;
10859
10860         if (can_replace[xx][yy] && (!change->use_random_replace ||
10861                                     RND(100) < change->random_percentage))
10862         {
10863           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10864             RemoveMovingField(ex, ey);
10865
10866           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10867
10868           content_element = change->target_content.e[xx][yy];
10869           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10870                                               ce_value, ce_score);
10871
10872           CreateElementFromChange(ex, ey, target_element);
10873
10874           something_has_changed = TRUE;
10875
10876           // for symmetry reasons, freeze newly created border elements
10877           if (ex != x || ey != y)
10878             Stop[ex][ey] = TRUE;        // no more moving in this frame
10879         }
10880       }
10881
10882       if (something_has_changed)
10883       {
10884         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10885         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10886       }
10887     }
10888   }
10889   else
10890   {
10891     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10892                                         ce_value, ce_score);
10893
10894     if (element == EL_DIAGONAL_GROWING ||
10895         element == EL_DIAGONAL_SHRINKING)
10896     {
10897       target_element = Store[x][y];
10898
10899       Store[x][y] = EL_EMPTY;
10900     }
10901
10902     // special case: element changes to player (and may be kept if walkable)
10903     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10904       CreateElementFromChange(x, y, EL_EMPTY);
10905
10906     CreateElementFromChange(x, y, target_element);
10907
10908     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10909     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10910   }
10911
10912   // this uses direct change before indirect change
10913   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10914
10915   return TRUE;
10916 }
10917
10918 static void HandleElementChange(int x, int y, int page)
10919 {
10920   int element = MovingOrBlocked2Element(x, y);
10921   struct ElementInfo *ei = &element_info[element];
10922   struct ElementChangeInfo *change = &ei->change_page[page];
10923   boolean handle_action_before_change = FALSE;
10924
10925 #ifdef DEBUG
10926   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10927       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10928   {
10929     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10930           x, y, element, element_info[element].token_name);
10931     Debug("game:playing:HandleElementChange", "This should never happen!");
10932   }
10933 #endif
10934
10935   // this can happen with classic bombs on walkable, changing elements
10936   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10937   {
10938     return;
10939   }
10940
10941   if (ChangeDelay[x][y] == 0)           // initialize element change
10942   {
10943     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10944
10945     if (change->can_change)
10946     {
10947       // !!! not clear why graphic animation should be reset at all here !!!
10948       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10949       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10950
10951       /*
10952         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10953
10954         When using an animation frame delay of 1 (this only happens with
10955         "sp_zonk.moving.left/right" in the classic graphics), the default
10956         (non-moving) animation shows wrong animation frames (while the
10957         moving animation, like "sp_zonk.moving.left/right", is correct,
10958         so this graphical bug never shows up with the classic graphics).
10959         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10960         be drawn instead of the correct frames 0,1,2,3. This is caused by
10961         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10962         an element change: First when the change delay ("ChangeDelay[][]")
10963         counter has reached zero after decrementing, then a second time in
10964         the next frame (after "GfxFrame[][]" was already incremented) when
10965         "ChangeDelay[][]" is reset to the initial delay value again.
10966
10967         This causes frame 0 to be drawn twice, while the last frame won't
10968         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10969
10970         As some animations may already be cleverly designed around this bug
10971         (at least the "Snake Bite" snake tail animation does this), it cannot
10972         simply be fixed here without breaking such existing animations.
10973         Unfortunately, it cannot easily be detected if a graphics set was
10974         designed "before" or "after" the bug was fixed. As a workaround,
10975         a new graphics set option "game.graphics_engine_version" was added
10976         to be able to specify the game's major release version for which the
10977         graphics set was designed, which can then be used to decide if the
10978         bugfix should be used (version 4 and above) or not (version 3 or
10979         below, or if no version was specified at all, as with old sets).
10980
10981         (The wrong/fixed animation frames can be tested with the test level set
10982         "test_gfxframe" and level "000", which contains a specially prepared
10983         custom element at level position (x/y) == (11/9) which uses the zonk
10984         animation mentioned above. Using "game.graphics_engine_version: 4"
10985         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10986         This can also be seen from the debug output for this test element.)
10987       */
10988
10989       // when a custom element is about to change (for example by change delay),
10990       // do not reset graphic animation when the custom element is moving
10991       if (game.graphics_engine_version < 4 &&
10992           !IS_MOVING(x, y))
10993       {
10994         ResetGfxAnimation(x, y);
10995         ResetRandomAnimationValue(x, y);
10996       }
10997
10998       if (change->pre_change_function)
10999         change->pre_change_function(x, y);
11000     }
11001   }
11002
11003   ChangeDelay[x][y]--;
11004
11005   if (ChangeDelay[x][y] != 0)           // continue element change
11006   {
11007     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11008
11009     // also needed if CE can not change, but has CE delay with CE action
11010     if (IS_ANIMATED(graphic))
11011       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11012
11013     if (change->can_change)
11014     {
11015       if (change->change_function)
11016         change->change_function(x, y);
11017     }
11018   }
11019   else                                  // finish element change
11020   {
11021     if (ChangePage[x][y] != -1)         // remember page from delayed change
11022     {
11023       page = ChangePage[x][y];
11024       ChangePage[x][y] = -1;
11025
11026       change = &ei->change_page[page];
11027     }
11028
11029     if (IS_MOVING(x, y))                // never change a running system ;-)
11030     {
11031       ChangeDelay[x][y] = 1;            // try change after next move step
11032       ChangePage[x][y] = page;          // remember page to use for change
11033
11034       return;
11035     }
11036
11037     // special case: set new level random seed before changing element
11038     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11039       handle_action_before_change = TRUE;
11040
11041     if (change->has_action && handle_action_before_change)
11042       ExecuteCustomElementAction(x, y, element, page);
11043
11044     if (change->can_change)
11045     {
11046       if (ChangeElement(x, y, element, page))
11047       {
11048         if (change->post_change_function)
11049           change->post_change_function(x, y);
11050       }
11051     }
11052
11053     if (change->has_action && !handle_action_before_change)
11054       ExecuteCustomElementAction(x, y, element, page);
11055   }
11056 }
11057
11058 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11059                                               int trigger_element,
11060                                               int trigger_event,
11061                                               int trigger_player,
11062                                               int trigger_side,
11063                                               int trigger_page)
11064 {
11065   boolean change_done_any = FALSE;
11066   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11067   int i;
11068
11069   if (!(trigger_events[trigger_element][trigger_event]))
11070     return FALSE;
11071
11072   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11073
11074   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11075   {
11076     int element = EL_CUSTOM_START + i;
11077     boolean change_done = FALSE;
11078     int p;
11079
11080     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11081         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11082       continue;
11083
11084     for (p = 0; p < element_info[element].num_change_pages; p++)
11085     {
11086       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11087
11088       if (change->can_change_or_has_action &&
11089           change->has_event[trigger_event] &&
11090           change->trigger_side & trigger_side &&
11091           change->trigger_player & trigger_player &&
11092           change->trigger_page & trigger_page_bits &&
11093           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11094       {
11095         change->actual_trigger_element = trigger_element;
11096         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11097         change->actual_trigger_player_bits = trigger_player;
11098         change->actual_trigger_side = trigger_side;
11099         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11100         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11101
11102         if ((change->can_change && !change_done) || change->has_action)
11103         {
11104           int x, y;
11105
11106           SCAN_PLAYFIELD(x, y)
11107           {
11108             if (Tile[x][y] == element)
11109             {
11110               if (change->can_change && !change_done)
11111               {
11112                 // if element already changed in this frame, not only prevent
11113                 // another element change (checked in ChangeElement()), but
11114                 // also prevent additional element actions for this element
11115
11116                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11117                     !level.use_action_after_change_bug)
11118                   continue;
11119
11120                 ChangeDelay[x][y] = 1;
11121                 ChangeEvent[x][y] = trigger_event;
11122
11123                 HandleElementChange(x, y, p);
11124               }
11125               else if (change->has_action)
11126               {
11127                 // if element already changed in this frame, not only prevent
11128                 // another element change (checked in ChangeElement()), but
11129                 // also prevent additional element actions for this element
11130
11131                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11132                     !level.use_action_after_change_bug)
11133                   continue;
11134
11135                 ExecuteCustomElementAction(x, y, element, p);
11136                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11137               }
11138             }
11139           }
11140
11141           if (change->can_change)
11142           {
11143             change_done = TRUE;
11144             change_done_any = TRUE;
11145           }
11146         }
11147       }
11148     }
11149   }
11150
11151   RECURSION_LOOP_DETECTION_END();
11152
11153   return change_done_any;
11154 }
11155
11156 static boolean CheckElementChangeExt(int x, int y,
11157                                      int element,
11158                                      int trigger_element,
11159                                      int trigger_event,
11160                                      int trigger_player,
11161                                      int trigger_side)
11162 {
11163   boolean change_done = FALSE;
11164   int p;
11165
11166   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11167       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11168     return FALSE;
11169
11170   if (Tile[x][y] == EL_BLOCKED)
11171   {
11172     Blocked2Moving(x, y, &x, &y);
11173     element = Tile[x][y];
11174   }
11175
11176   // check if element has already changed or is about to change after moving
11177   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11178        Tile[x][y] != element) ||
11179
11180       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11181        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11182         ChangePage[x][y] != -1)))
11183     return FALSE;
11184
11185   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11186
11187   for (p = 0; p < element_info[element].num_change_pages; p++)
11188   {
11189     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11190
11191     /* check trigger element for all events where the element that is checked
11192        for changing interacts with a directly adjacent element -- this is
11193        different to element changes that affect other elements to change on the
11194        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11195     boolean check_trigger_element =
11196       (trigger_event == CE_NEXT_TO_X ||
11197        trigger_event == CE_TOUCHING_X ||
11198        trigger_event == CE_HITTING_X ||
11199        trigger_event == CE_HIT_BY_X ||
11200        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11201
11202     if (change->can_change_or_has_action &&
11203         change->has_event[trigger_event] &&
11204         change->trigger_side & trigger_side &&
11205         change->trigger_player & trigger_player &&
11206         (!check_trigger_element ||
11207          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11208     {
11209       change->actual_trigger_element = trigger_element;
11210       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11211       change->actual_trigger_player_bits = trigger_player;
11212       change->actual_trigger_side = trigger_side;
11213       change->actual_trigger_ce_value = CustomValue[x][y];
11214       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11215
11216       // special case: trigger element not at (x,y) position for some events
11217       if (check_trigger_element)
11218       {
11219         static struct
11220         {
11221           int dx, dy;
11222         } move_xy[] =
11223           {
11224             {  0,  0 },
11225             { -1,  0 },
11226             { +1,  0 },
11227             {  0,  0 },
11228             {  0, -1 },
11229             {  0,  0 }, { 0, 0 }, { 0, 0 },
11230             {  0, +1 }
11231           };
11232
11233         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11234         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11235
11236         change->actual_trigger_ce_value = CustomValue[xx][yy];
11237         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11238       }
11239
11240       if (change->can_change && !change_done)
11241       {
11242         ChangeDelay[x][y] = 1;
11243         ChangeEvent[x][y] = trigger_event;
11244
11245         HandleElementChange(x, y, p);
11246
11247         change_done = TRUE;
11248       }
11249       else if (change->has_action)
11250       {
11251         ExecuteCustomElementAction(x, y, element, p);
11252         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11253       }
11254     }
11255   }
11256
11257   RECURSION_LOOP_DETECTION_END();
11258
11259   return change_done;
11260 }
11261
11262 static void PlayPlayerSound(struct PlayerInfo *player)
11263 {
11264   int jx = player->jx, jy = player->jy;
11265   int sound_element = player->artwork_element;
11266   int last_action = player->last_action_waiting;
11267   int action = player->action_waiting;
11268
11269   if (player->is_waiting)
11270   {
11271     if (action != last_action)
11272       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11273     else
11274       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11275   }
11276   else
11277   {
11278     if (action != last_action)
11279       StopSound(element_info[sound_element].sound[last_action]);
11280
11281     if (last_action == ACTION_SLEEPING)
11282       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11283   }
11284 }
11285
11286 static void PlayAllPlayersSound(void)
11287 {
11288   int i;
11289
11290   for (i = 0; i < MAX_PLAYERS; i++)
11291     if (stored_player[i].active)
11292       PlayPlayerSound(&stored_player[i]);
11293 }
11294
11295 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11296 {
11297   boolean last_waiting = player->is_waiting;
11298   int move_dir = player->MovDir;
11299
11300   player->dir_waiting = move_dir;
11301   player->last_action_waiting = player->action_waiting;
11302
11303   if (is_waiting)
11304   {
11305     if (!last_waiting)          // not waiting -> waiting
11306     {
11307       player->is_waiting = TRUE;
11308
11309       player->frame_counter_bored =
11310         FrameCounter +
11311         game.player_boring_delay_fixed +
11312         GetSimpleRandom(game.player_boring_delay_random);
11313       player->frame_counter_sleeping =
11314         FrameCounter +
11315         game.player_sleeping_delay_fixed +
11316         GetSimpleRandom(game.player_sleeping_delay_random);
11317
11318       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11319     }
11320
11321     if (game.player_sleeping_delay_fixed +
11322         game.player_sleeping_delay_random > 0 &&
11323         player->anim_delay_counter == 0 &&
11324         player->post_delay_counter == 0 &&
11325         FrameCounter >= player->frame_counter_sleeping)
11326       player->is_sleeping = TRUE;
11327     else if (game.player_boring_delay_fixed +
11328              game.player_boring_delay_random > 0 &&
11329              FrameCounter >= player->frame_counter_bored)
11330       player->is_bored = TRUE;
11331
11332     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11333                               player->is_bored ? ACTION_BORING :
11334                               ACTION_WAITING);
11335
11336     if (player->is_sleeping && player->use_murphy)
11337     {
11338       // special case for sleeping Murphy when leaning against non-free tile
11339
11340       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11341           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11342            !IS_MOVING(player->jx - 1, player->jy)))
11343         move_dir = MV_LEFT;
11344       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11345                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11346                 !IS_MOVING(player->jx + 1, player->jy)))
11347         move_dir = MV_RIGHT;
11348       else
11349         player->is_sleeping = FALSE;
11350
11351       player->dir_waiting = move_dir;
11352     }
11353
11354     if (player->is_sleeping)
11355     {
11356       if (player->num_special_action_sleeping > 0)
11357       {
11358         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11359         {
11360           int last_special_action = player->special_action_sleeping;
11361           int num_special_action = player->num_special_action_sleeping;
11362           int special_action =
11363             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11364              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11365              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11366              last_special_action + 1 : ACTION_SLEEPING);
11367           int special_graphic =
11368             el_act_dir2img(player->artwork_element, special_action, move_dir);
11369
11370           player->anim_delay_counter =
11371             graphic_info[special_graphic].anim_delay_fixed +
11372             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11373           player->post_delay_counter =
11374             graphic_info[special_graphic].post_delay_fixed +
11375             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11376
11377           player->special_action_sleeping = special_action;
11378         }
11379
11380         if (player->anim_delay_counter > 0)
11381         {
11382           player->action_waiting = player->special_action_sleeping;
11383           player->anim_delay_counter--;
11384         }
11385         else if (player->post_delay_counter > 0)
11386         {
11387           player->post_delay_counter--;
11388         }
11389       }
11390     }
11391     else if (player->is_bored)
11392     {
11393       if (player->num_special_action_bored > 0)
11394       {
11395         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11396         {
11397           int special_action =
11398             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11399           int special_graphic =
11400             el_act_dir2img(player->artwork_element, special_action, move_dir);
11401
11402           player->anim_delay_counter =
11403             graphic_info[special_graphic].anim_delay_fixed +
11404             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11405           player->post_delay_counter =
11406             graphic_info[special_graphic].post_delay_fixed +
11407             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11408
11409           player->special_action_bored = special_action;
11410         }
11411
11412         if (player->anim_delay_counter > 0)
11413         {
11414           player->action_waiting = player->special_action_bored;
11415           player->anim_delay_counter--;
11416         }
11417         else if (player->post_delay_counter > 0)
11418         {
11419           player->post_delay_counter--;
11420         }
11421       }
11422     }
11423   }
11424   else if (last_waiting)        // waiting -> not waiting
11425   {
11426     player->is_waiting = FALSE;
11427     player->is_bored = FALSE;
11428     player->is_sleeping = FALSE;
11429
11430     player->frame_counter_bored = -1;
11431     player->frame_counter_sleeping = -1;
11432
11433     player->anim_delay_counter = 0;
11434     player->post_delay_counter = 0;
11435
11436     player->dir_waiting = player->MovDir;
11437     player->action_waiting = ACTION_DEFAULT;
11438
11439     player->special_action_bored = ACTION_DEFAULT;
11440     player->special_action_sleeping = ACTION_DEFAULT;
11441   }
11442 }
11443
11444 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11445 {
11446   if ((!player->is_moving  && player->was_moving) ||
11447       (player->MovPos == 0 && player->was_moving) ||
11448       (player->is_snapping && !player->was_snapping) ||
11449       (player->is_dropping && !player->was_dropping))
11450   {
11451     if (!CheckSaveEngineSnapshotToList())
11452       return;
11453
11454     player->was_moving = FALSE;
11455     player->was_snapping = TRUE;
11456     player->was_dropping = TRUE;
11457   }
11458   else
11459   {
11460     if (player->is_moving)
11461       player->was_moving = TRUE;
11462
11463     if (!player->is_snapping)
11464       player->was_snapping = FALSE;
11465
11466     if (!player->is_dropping)
11467       player->was_dropping = FALSE;
11468   }
11469
11470   static struct MouseActionInfo mouse_action_last = { 0 };
11471   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11472   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11473
11474   if (new_released)
11475     CheckSaveEngineSnapshotToList();
11476
11477   mouse_action_last = mouse_action;
11478 }
11479
11480 static void CheckSingleStepMode(struct PlayerInfo *player)
11481 {
11482   if (tape.single_step && tape.recording && !tape.pausing)
11483   {
11484     // as it is called "single step mode", just return to pause mode when the
11485     // player stopped moving after one tile (or never starts moving at all)
11486     // (reverse logic needed here in case single step mode used in team mode)
11487     if (player->is_moving ||
11488         player->is_pushing ||
11489         player->is_dropping_pressed ||
11490         player->effective_mouse_action.button)
11491       game.enter_single_step_mode = FALSE;
11492   }
11493
11494   CheckSaveEngineSnapshot(player);
11495 }
11496
11497 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11498 {
11499   int left      = player_action & JOY_LEFT;
11500   int right     = player_action & JOY_RIGHT;
11501   int up        = player_action & JOY_UP;
11502   int down      = player_action & JOY_DOWN;
11503   int button1   = player_action & JOY_BUTTON_1;
11504   int button2   = player_action & JOY_BUTTON_2;
11505   int dx        = (left ? -1 : right ? 1 : 0);
11506   int dy        = (up   ? -1 : down  ? 1 : 0);
11507
11508   if (!player->active || tape.pausing)
11509     return 0;
11510
11511   if (player_action)
11512   {
11513     if (button1)
11514       SnapField(player, dx, dy);
11515     else
11516     {
11517       if (button2)
11518         DropElement(player);
11519
11520       MovePlayer(player, dx, dy);
11521     }
11522
11523     CheckSingleStepMode(player);
11524
11525     SetPlayerWaiting(player, FALSE);
11526
11527     return player_action;
11528   }
11529   else
11530   {
11531     // no actions for this player (no input at player's configured device)
11532
11533     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11534     SnapField(player, 0, 0);
11535     CheckGravityMovementWhenNotMoving(player);
11536
11537     if (player->MovPos == 0)
11538       SetPlayerWaiting(player, TRUE);
11539
11540     if (player->MovPos == 0)    // needed for tape.playing
11541       player->is_moving = FALSE;
11542
11543     player->is_dropping = FALSE;
11544     player->is_dropping_pressed = FALSE;
11545     player->drop_pressed_delay = 0;
11546
11547     CheckSingleStepMode(player);
11548
11549     return 0;
11550   }
11551 }
11552
11553 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11554                                          byte *tape_action)
11555 {
11556   if (!tape.use_mouse_actions)
11557     return;
11558
11559   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11560   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11561   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11562 }
11563
11564 static void SetTapeActionFromMouseAction(byte *tape_action,
11565                                          struct MouseActionInfo *mouse_action)
11566 {
11567   if (!tape.use_mouse_actions)
11568     return;
11569
11570   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11571   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11572   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11573 }
11574
11575 static void CheckLevelSolved(void)
11576 {
11577   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11578   {
11579     if (game_em.level_solved &&
11580         !game_em.game_over)                             // game won
11581     {
11582       LevelSolved();
11583
11584       game_em.game_over = TRUE;
11585
11586       game.all_players_gone = TRUE;
11587     }
11588
11589     if (game_em.game_over)                              // game lost
11590       game.all_players_gone = TRUE;
11591   }
11592   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11593   {
11594     if (game_sp.level_solved &&
11595         !game_sp.game_over)                             // game won
11596     {
11597       LevelSolved();
11598
11599       game_sp.game_over = TRUE;
11600
11601       game.all_players_gone = TRUE;
11602     }
11603
11604     if (game_sp.game_over)                              // game lost
11605       game.all_players_gone = TRUE;
11606   }
11607   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11608   {
11609     if (game_mm.level_solved &&
11610         !game_mm.game_over)                             // game won
11611     {
11612       LevelSolved();
11613
11614       game_mm.game_over = TRUE;
11615
11616       game.all_players_gone = TRUE;
11617     }
11618
11619     if (game_mm.game_over)                              // game lost
11620       game.all_players_gone = TRUE;
11621   }
11622 }
11623
11624 static void CheckLevelTime_StepCounter(void)
11625 {
11626   int i;
11627
11628   TimePlayed++;
11629
11630   if (TimeLeft > 0)
11631   {
11632     TimeLeft--;
11633
11634     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11635       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11636
11637     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11638
11639     DisplayGameControlValues();
11640
11641     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11642       for (i = 0; i < MAX_PLAYERS; i++)
11643         KillPlayer(&stored_player[i]);
11644   }
11645   else if (game.no_level_time_limit && !game.all_players_gone)
11646   {
11647     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11648
11649     DisplayGameControlValues();
11650   }
11651 }
11652
11653 static void CheckLevelTime(void)
11654 {
11655   int i;
11656
11657   if (TimeFrames >= FRAMES_PER_SECOND)
11658   {
11659     TimeFrames = 0;
11660     TapeTime++;
11661
11662     for (i = 0; i < MAX_PLAYERS; i++)
11663     {
11664       struct PlayerInfo *player = &stored_player[i];
11665
11666       if (SHIELD_ON(player))
11667       {
11668         player->shield_normal_time_left--;
11669
11670         if (player->shield_deadly_time_left > 0)
11671           player->shield_deadly_time_left--;
11672       }
11673     }
11674
11675     if (!game.LevelSolved && !level.use_step_counter)
11676     {
11677       TimePlayed++;
11678
11679       if (TimeLeft > 0)
11680       {
11681         TimeLeft--;
11682
11683         if (TimeLeft <= 10 && game.time_limit)
11684           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11685
11686         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11687            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11688
11689         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11690
11691         if (!TimeLeft && game.time_limit)
11692         {
11693           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11694             game_em.lev->killed_out_of_time = TRUE;
11695           else
11696             for (i = 0; i < MAX_PLAYERS; i++)
11697               KillPlayer(&stored_player[i]);
11698         }
11699       }
11700       else if (game.no_level_time_limit && !game.all_players_gone)
11701       {
11702         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11703       }
11704
11705       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11706     }
11707
11708     if (tape.recording || tape.playing)
11709       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11710   }
11711
11712   if (tape.recording || tape.playing)
11713     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11714
11715   UpdateAndDisplayGameControlValues();
11716 }
11717
11718 void AdvanceFrameAndPlayerCounters(int player_nr)
11719 {
11720   int i;
11721
11722   // advance frame counters (global frame counter and time frame counter)
11723   FrameCounter++;
11724   TimeFrames++;
11725
11726   // advance player counters (counters for move delay, move animation etc.)
11727   for (i = 0; i < MAX_PLAYERS; i++)
11728   {
11729     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11730     int move_delay_value = stored_player[i].move_delay_value;
11731     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11732
11733     if (!advance_player_counters)       // not all players may be affected
11734       continue;
11735
11736     if (move_frames == 0)       // less than one move per game frame
11737     {
11738       int stepsize = TILEX / move_delay_value;
11739       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11740       int count = (stored_player[i].is_moving ?
11741                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11742
11743       if (count % delay == 0)
11744         move_frames = 1;
11745     }
11746
11747     stored_player[i].Frame += move_frames;
11748
11749     if (stored_player[i].MovPos != 0)
11750       stored_player[i].StepFrame += move_frames;
11751
11752     if (stored_player[i].move_delay > 0)
11753       stored_player[i].move_delay--;
11754
11755     // due to bugs in previous versions, counter must count up, not down
11756     if (stored_player[i].push_delay != -1)
11757       stored_player[i].push_delay++;
11758
11759     if (stored_player[i].drop_delay > 0)
11760       stored_player[i].drop_delay--;
11761
11762     if (stored_player[i].is_dropping_pressed)
11763       stored_player[i].drop_pressed_delay++;
11764   }
11765 }
11766
11767 void AdvanceFrameCounter(void)
11768 {
11769   FrameCounter++;
11770 }
11771
11772 void AdvanceGfxFrame(void)
11773 {
11774   int x, y;
11775
11776   SCAN_PLAYFIELD(x, y)
11777   {
11778     GfxFrame[x][y]++;
11779   }
11780 }
11781
11782 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11783                               struct MouseActionInfo *mouse_action_last)
11784 {
11785   if (mouse_action->button)
11786   {
11787     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11788     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11789     int x = mouse_action->lx;
11790     int y = mouse_action->ly;
11791     int element = Tile[x][y];
11792
11793     if (new_button)
11794     {
11795       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11796       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11797                                          ch_button);
11798     }
11799
11800     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11801     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11802                                        ch_button);
11803
11804     if (level.use_step_counter)
11805     {
11806       boolean counted_click = FALSE;
11807
11808       // element clicked that can change when clicked/pressed
11809       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11810           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11811            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11812         counted_click = TRUE;
11813
11814       // element clicked that can trigger change when clicked/pressed
11815       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11816           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11817         counted_click = TRUE;
11818
11819       if (new_button && counted_click)
11820         CheckLevelTime_StepCounter();
11821     }
11822   }
11823 }
11824
11825 void StartGameActions(boolean init_network_game, boolean record_tape,
11826                       int random_seed)
11827 {
11828   unsigned int new_random_seed = InitRND(random_seed);
11829
11830   if (record_tape)
11831     TapeStartRecording(new_random_seed);
11832
11833   if (setup.auto_pause_on_start && !tape.pausing)
11834     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11835
11836   if (init_network_game)
11837   {
11838     SendToServer_LevelFile();
11839     SendToServer_StartPlaying();
11840
11841     return;
11842   }
11843
11844   InitGame();
11845 }
11846
11847 static void GameActionsExt(void)
11848 {
11849 #if 0
11850   static unsigned int game_frame_delay = 0;
11851 #endif
11852   unsigned int game_frame_delay_value;
11853   byte *recorded_player_action;
11854   byte summarized_player_action = 0;
11855   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11856   int i;
11857
11858   // detect endless loops, caused by custom element programming
11859   if (recursion_loop_detected && recursion_loop_depth == 0)
11860   {
11861     char *message = getStringCat3("Internal Error! Element ",
11862                                   EL_NAME(recursion_loop_element),
11863                                   " caused endless loop! Quit the game?");
11864
11865     Warn("element '%s' caused endless loop in game engine",
11866          EL_NAME(recursion_loop_element));
11867
11868     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11869
11870     recursion_loop_detected = FALSE;    // if game should be continued
11871
11872     free(message);
11873
11874     return;
11875   }
11876
11877   if (game.restart_level)
11878     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11879
11880   CheckLevelSolved();
11881
11882   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11883     GameWon();
11884
11885   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11886     TapeStop();
11887
11888   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11889     return;
11890
11891   game_frame_delay_value =
11892     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11893
11894   if (tape.playing && tape.warp_forward && !tape.pausing)
11895     game_frame_delay_value = 0;
11896
11897   SetVideoFrameDelay(game_frame_delay_value);
11898
11899   // (de)activate virtual buttons depending on current game status
11900   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11901   {
11902     if (game.all_players_gone)  // if no players there to be controlled anymore
11903       SetOverlayActive(FALSE);
11904     else if (!tape.playing)     // if game continues after tape stopped playing
11905       SetOverlayActive(TRUE);
11906   }
11907
11908 #if 0
11909 #if 0
11910   // ---------- main game synchronization point ----------
11911
11912   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11913
11914   Debug("game:playing:skip", "skip == %d", skip);
11915
11916 #else
11917   // ---------- main game synchronization point ----------
11918
11919   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11920 #endif
11921 #endif
11922
11923   if (network_playing && !network_player_action_received)
11924   {
11925     // try to get network player actions in time
11926
11927     // last chance to get network player actions without main loop delay
11928     HandleNetworking();
11929
11930     // game was quit by network peer
11931     if (game_status != GAME_MODE_PLAYING)
11932       return;
11933
11934     // check if network player actions still missing and game still running
11935     if (!network_player_action_received && !checkGameEnded())
11936       return;           // failed to get network player actions in time
11937
11938     // do not yet reset "network_player_action_received" (for tape.pausing)
11939   }
11940
11941   if (tape.pausing)
11942     return;
11943
11944   // at this point we know that we really continue executing the game
11945
11946   network_player_action_received = FALSE;
11947
11948   // when playing tape, read previously recorded player input from tape data
11949   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11950
11951   local_player->effective_mouse_action = local_player->mouse_action;
11952
11953   if (recorded_player_action != NULL)
11954     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11955                                  recorded_player_action);
11956
11957   // TapePlayAction() may return NULL when toggling to "pause before death"
11958   if (tape.pausing)
11959     return;
11960
11961   if (tape.set_centered_player)
11962   {
11963     game.centered_player_nr_next = tape.centered_player_nr_next;
11964     game.set_centered_player = TRUE;
11965   }
11966
11967   for (i = 0; i < MAX_PLAYERS; i++)
11968   {
11969     summarized_player_action |= stored_player[i].action;
11970
11971     if (!network_playing && (game.team_mode || tape.playing))
11972       stored_player[i].effective_action = stored_player[i].action;
11973   }
11974
11975   if (network_playing && !checkGameEnded())
11976     SendToServer_MovePlayer(summarized_player_action);
11977
11978   // summarize all actions at local players mapped input device position
11979   // (this allows using different input devices in single player mode)
11980   if (!network.enabled && !game.team_mode)
11981     stored_player[map_player_action[local_player->index_nr]].effective_action =
11982       summarized_player_action;
11983
11984   // summarize all actions at centered player in local team mode
11985   if (tape.recording &&
11986       setup.team_mode && !network.enabled &&
11987       setup.input_on_focus &&
11988       game.centered_player_nr != -1)
11989   {
11990     for (i = 0; i < MAX_PLAYERS; i++)
11991       stored_player[map_player_action[i]].effective_action =
11992         (i == game.centered_player_nr ? summarized_player_action : 0);
11993   }
11994
11995   if (recorded_player_action != NULL)
11996     for (i = 0; i < MAX_PLAYERS; i++)
11997       stored_player[i].effective_action = recorded_player_action[i];
11998
11999   for (i = 0; i < MAX_PLAYERS; i++)
12000   {
12001     tape_action[i] = stored_player[i].effective_action;
12002
12003     /* (this may happen in the RND game engine if a player was not present on
12004        the playfield on level start, but appeared later from a custom element */
12005     if (setup.team_mode &&
12006         tape.recording &&
12007         tape_action[i] &&
12008         !tape.player_participates[i])
12009       tape.player_participates[i] = TRUE;
12010   }
12011
12012   SetTapeActionFromMouseAction(tape_action,
12013                                &local_player->effective_mouse_action);
12014
12015   // only record actions from input devices, but not programmed actions
12016   if (tape.recording)
12017     TapeRecordAction(tape_action);
12018
12019   // remember if game was played (especially after tape stopped playing)
12020   if (!tape.playing && summarized_player_action && !checkGameFailed())
12021     game.GamePlayed = TRUE;
12022
12023 #if USE_NEW_PLAYER_ASSIGNMENTS
12024   // !!! also map player actions in single player mode !!!
12025   // if (game.team_mode)
12026   if (1)
12027   {
12028     byte mapped_action[MAX_PLAYERS];
12029
12030 #if DEBUG_PLAYER_ACTIONS
12031     for (i = 0; i < MAX_PLAYERS; i++)
12032       DebugContinued("", "%d, ", stored_player[i].effective_action);
12033 #endif
12034
12035     for (i = 0; i < MAX_PLAYERS; i++)
12036       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12037
12038     for (i = 0; i < MAX_PLAYERS; i++)
12039       stored_player[i].effective_action = mapped_action[i];
12040
12041 #if DEBUG_PLAYER_ACTIONS
12042     DebugContinued("", "=> ");
12043     for (i = 0; i < MAX_PLAYERS; i++)
12044       DebugContinued("", "%d, ", stored_player[i].effective_action);
12045     DebugContinued("game:playing:player", "\n");
12046 #endif
12047   }
12048 #if DEBUG_PLAYER_ACTIONS
12049   else
12050   {
12051     for (i = 0; i < MAX_PLAYERS; i++)
12052       DebugContinued("", "%d, ", stored_player[i].effective_action);
12053     DebugContinued("game:playing:player", "\n");
12054   }
12055 #endif
12056 #endif
12057
12058   for (i = 0; i < MAX_PLAYERS; i++)
12059   {
12060     // allow engine snapshot in case of changed movement attempt
12061     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12062         (stored_player[i].effective_action & KEY_MOTION))
12063       game.snapshot.changed_action = TRUE;
12064
12065     // allow engine snapshot in case of snapping/dropping attempt
12066     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12067         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12068       game.snapshot.changed_action = TRUE;
12069
12070     game.snapshot.last_action[i] = stored_player[i].effective_action;
12071   }
12072
12073   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12074   {
12075     GameActions_EM_Main();
12076   }
12077   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12078   {
12079     GameActions_SP_Main();
12080   }
12081   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12082   {
12083     GameActions_MM_Main();
12084   }
12085   else
12086   {
12087     GameActions_RND_Main();
12088   }
12089
12090   BlitScreenToBitmap(backbuffer);
12091
12092   CheckLevelSolved();
12093   CheckLevelTime();
12094
12095   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12096
12097   if (global.show_frames_per_second)
12098   {
12099     static unsigned int fps_counter = 0;
12100     static int fps_frames = 0;
12101     unsigned int fps_delay_ms = Counter() - fps_counter;
12102
12103     fps_frames++;
12104
12105     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12106     {
12107       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12108
12109       fps_frames = 0;
12110       fps_counter = Counter();
12111
12112       // always draw FPS to screen after FPS value was updated
12113       redraw_mask |= REDRAW_FPS;
12114     }
12115
12116     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12117     if (GetDrawDeactivationMask() == REDRAW_NONE)
12118       redraw_mask |= REDRAW_FPS;
12119   }
12120 }
12121
12122 static void GameActions_CheckSaveEngineSnapshot(void)
12123 {
12124   if (!game.snapshot.save_snapshot)
12125     return;
12126
12127   // clear flag for saving snapshot _before_ saving snapshot
12128   game.snapshot.save_snapshot = FALSE;
12129
12130   SaveEngineSnapshotToList();
12131 }
12132
12133 void GameActions(void)
12134 {
12135   GameActionsExt();
12136
12137   GameActions_CheckSaveEngineSnapshot();
12138 }
12139
12140 void GameActions_EM_Main(void)
12141 {
12142   byte effective_action[MAX_PLAYERS];
12143   int i;
12144
12145   for (i = 0; i < MAX_PLAYERS; i++)
12146     effective_action[i] = stored_player[i].effective_action;
12147
12148   GameActions_EM(effective_action);
12149 }
12150
12151 void GameActions_SP_Main(void)
12152 {
12153   byte effective_action[MAX_PLAYERS];
12154   int i;
12155
12156   for (i = 0; i < MAX_PLAYERS; i++)
12157     effective_action[i] = stored_player[i].effective_action;
12158
12159   GameActions_SP(effective_action);
12160
12161   for (i = 0; i < MAX_PLAYERS; i++)
12162   {
12163     if (stored_player[i].force_dropping)
12164       stored_player[i].action |= KEY_BUTTON_DROP;
12165
12166     stored_player[i].force_dropping = FALSE;
12167   }
12168 }
12169
12170 void GameActions_MM_Main(void)
12171 {
12172   AdvanceGfxFrame();
12173
12174   GameActions_MM(local_player->effective_mouse_action);
12175 }
12176
12177 void GameActions_RND_Main(void)
12178 {
12179   GameActions_RND();
12180 }
12181
12182 void GameActions_RND(void)
12183 {
12184   static struct MouseActionInfo mouse_action_last = { 0 };
12185   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12186   int magic_wall_x = 0, magic_wall_y = 0;
12187   int i, x, y, element, graphic, last_gfx_frame;
12188
12189   InitPlayfieldScanModeVars();
12190
12191   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12192   {
12193     SCAN_PLAYFIELD(x, y)
12194     {
12195       ChangeCount[x][y] = 0;
12196       ChangeEvent[x][y] = -1;
12197     }
12198   }
12199
12200   if (game.set_centered_player)
12201   {
12202     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12203
12204     // switching to "all players" only possible if all players fit to screen
12205     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12206     {
12207       game.centered_player_nr_next = game.centered_player_nr;
12208       game.set_centered_player = FALSE;
12209     }
12210
12211     // do not switch focus to non-existing (or non-active) player
12212     if (game.centered_player_nr_next >= 0 &&
12213         !stored_player[game.centered_player_nr_next].active)
12214     {
12215       game.centered_player_nr_next = game.centered_player_nr;
12216       game.set_centered_player = FALSE;
12217     }
12218   }
12219
12220   if (game.set_centered_player &&
12221       ScreenMovPos == 0)        // screen currently aligned at tile position
12222   {
12223     int sx, sy;
12224
12225     if (game.centered_player_nr_next == -1)
12226     {
12227       setScreenCenteredToAllPlayers(&sx, &sy);
12228     }
12229     else
12230     {
12231       sx = stored_player[game.centered_player_nr_next].jx;
12232       sy = stored_player[game.centered_player_nr_next].jy;
12233     }
12234
12235     game.centered_player_nr = game.centered_player_nr_next;
12236     game.set_centered_player = FALSE;
12237
12238     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12239     DrawGameDoorValues();
12240   }
12241
12242   // check single step mode (set flag and clear again if any player is active)
12243   game.enter_single_step_mode =
12244     (tape.single_step && tape.recording && !tape.pausing);
12245
12246   for (i = 0; i < MAX_PLAYERS; i++)
12247   {
12248     int actual_player_action = stored_player[i].effective_action;
12249
12250 #if 1
12251     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12252        - rnd_equinox_tetrachloride 048
12253        - rnd_equinox_tetrachloride_ii 096
12254        - rnd_emanuel_schmieg 002
12255        - doctor_sloan_ww 001, 020
12256     */
12257     if (stored_player[i].MovPos == 0)
12258       CheckGravityMovement(&stored_player[i]);
12259 #endif
12260
12261     // overwrite programmed action with tape action
12262     if (stored_player[i].programmed_action)
12263       actual_player_action = stored_player[i].programmed_action;
12264
12265     PlayerActions(&stored_player[i], actual_player_action);
12266
12267     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12268   }
12269
12270   // single step pause mode may already have been toggled by "ScrollPlayer()"
12271   if (game.enter_single_step_mode && !tape.pausing)
12272     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12273
12274   ScrollScreen(NULL, SCROLL_GO_ON);
12275
12276   /* for backwards compatibility, the following code emulates a fixed bug that
12277      occured when pushing elements (causing elements that just made their last
12278      pushing step to already (if possible) make their first falling step in the
12279      same game frame, which is bad); this code is also needed to use the famous
12280      "spring push bug" which is used in older levels and might be wanted to be
12281      used also in newer levels, but in this case the buggy pushing code is only
12282      affecting the "spring" element and no other elements */
12283
12284   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12285   {
12286     for (i = 0; i < MAX_PLAYERS; i++)
12287     {
12288       struct PlayerInfo *player = &stored_player[i];
12289       int x = player->jx;
12290       int y = player->jy;
12291
12292       if (player->active && player->is_pushing && player->is_moving &&
12293           IS_MOVING(x, y) &&
12294           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12295            Tile[x][y] == EL_SPRING))
12296       {
12297         ContinueMoving(x, y);
12298
12299         // continue moving after pushing (this is actually a bug)
12300         if (!IS_MOVING(x, y))
12301           Stop[x][y] = FALSE;
12302       }
12303     }
12304   }
12305
12306   SCAN_PLAYFIELD(x, y)
12307   {
12308     Last[x][y] = Tile[x][y];
12309
12310     ChangeCount[x][y] = 0;
12311     ChangeEvent[x][y] = -1;
12312
12313     // this must be handled before main playfield loop
12314     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12315     {
12316       MovDelay[x][y]--;
12317       if (MovDelay[x][y] <= 0)
12318         RemoveField(x, y);
12319     }
12320
12321     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12322     {
12323       MovDelay[x][y]--;
12324       if (MovDelay[x][y] <= 0)
12325       {
12326         int element = Store[x][y];
12327         int move_direction = MovDir[x][y];
12328         int player_index_bit = Store2[x][y];
12329
12330         Store[x][y] = 0;
12331         Store2[x][y] = 0;
12332
12333         RemoveField(x, y);
12334         TEST_DrawLevelField(x, y);
12335
12336         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12337
12338         if (IS_ENVELOPE(element))
12339           local_player->show_envelope = element;
12340       }
12341     }
12342
12343 #if DEBUG
12344     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12345     {
12346       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12347             x, y);
12348       Debug("game:playing:GameActions_RND", "This should never happen!");
12349
12350       ChangePage[x][y] = -1;
12351     }
12352 #endif
12353
12354     Stop[x][y] = FALSE;
12355     if (WasJustMoving[x][y] > 0)
12356       WasJustMoving[x][y]--;
12357     if (WasJustFalling[x][y] > 0)
12358       WasJustFalling[x][y]--;
12359     if (CheckCollision[x][y] > 0)
12360       CheckCollision[x][y]--;
12361     if (CheckImpact[x][y] > 0)
12362       CheckImpact[x][y]--;
12363
12364     GfxFrame[x][y]++;
12365
12366     /* reset finished pushing action (not done in ContinueMoving() to allow
12367        continuous pushing animation for elements with zero push delay) */
12368     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12369     {
12370       ResetGfxAnimation(x, y);
12371       TEST_DrawLevelField(x, y);
12372     }
12373
12374 #if DEBUG
12375     if (IS_BLOCKED(x, y))
12376     {
12377       int oldx, oldy;
12378
12379       Blocked2Moving(x, y, &oldx, &oldy);
12380       if (!IS_MOVING(oldx, oldy))
12381       {
12382         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12383         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12384         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12385         Debug("game:playing:GameActions_RND", "This should never happen!");
12386       }
12387     }
12388 #endif
12389   }
12390
12391   HandleMouseAction(&mouse_action, &mouse_action_last);
12392
12393   SCAN_PLAYFIELD(x, y)
12394   {
12395     element = Tile[x][y];
12396     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12397     last_gfx_frame = GfxFrame[x][y];
12398
12399     if (element == EL_EMPTY)
12400       graphic = el2img(GfxElementEmpty[x][y]);
12401
12402     ResetGfxFrame(x, y);
12403
12404     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12405       DrawLevelGraphicAnimation(x, y, graphic);
12406
12407     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12408         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12409       ResetRandomAnimationValue(x, y);
12410
12411     SetRandomAnimationValue(x, y);
12412
12413     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12414
12415     if (IS_INACTIVE(element))
12416     {
12417       if (IS_ANIMATED(graphic))
12418         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12419
12420       continue;
12421     }
12422
12423     // this may take place after moving, so 'element' may have changed
12424     if (IS_CHANGING(x, y) &&
12425         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12426     {
12427       int page = element_info[element].event_page_nr[CE_DELAY];
12428
12429       HandleElementChange(x, y, page);
12430
12431       element = Tile[x][y];
12432       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12433     }
12434
12435     CheckNextToConditions(x, y);
12436
12437     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12438     {
12439       StartMoving(x, y);
12440
12441       element = Tile[x][y];
12442       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12443
12444       if (IS_ANIMATED(graphic) &&
12445           !IS_MOVING(x, y) &&
12446           !Stop[x][y])
12447         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12448
12449       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12450         TEST_DrawTwinkleOnField(x, y);
12451     }
12452     else if (element == EL_ACID)
12453     {
12454       if (!Stop[x][y])
12455         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12456     }
12457     else if ((element == EL_EXIT_OPEN ||
12458               element == EL_EM_EXIT_OPEN ||
12459               element == EL_SP_EXIT_OPEN ||
12460               element == EL_STEEL_EXIT_OPEN ||
12461               element == EL_EM_STEEL_EXIT_OPEN ||
12462               element == EL_SP_TERMINAL ||
12463               element == EL_SP_TERMINAL_ACTIVE ||
12464               element == EL_EXTRA_TIME ||
12465               element == EL_SHIELD_NORMAL ||
12466               element == EL_SHIELD_DEADLY) &&
12467              IS_ANIMATED(graphic))
12468       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12469     else if (IS_MOVING(x, y))
12470       ContinueMoving(x, y);
12471     else if (IS_ACTIVE_BOMB(element))
12472       CheckDynamite(x, y);
12473     else if (element == EL_AMOEBA_GROWING)
12474       AmoebaGrowing(x, y);
12475     else if (element == EL_AMOEBA_SHRINKING)
12476       AmoebaShrinking(x, y);
12477
12478 #if !USE_NEW_AMOEBA_CODE
12479     else if (IS_AMOEBALIVE(element))
12480       AmoebaReproduce(x, y);
12481 #endif
12482
12483     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12484       Life(x, y);
12485     else if (element == EL_EXIT_CLOSED)
12486       CheckExit(x, y);
12487     else if (element == EL_EM_EXIT_CLOSED)
12488       CheckExitEM(x, y);
12489     else if (element == EL_STEEL_EXIT_CLOSED)
12490       CheckExitSteel(x, y);
12491     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12492       CheckExitSteelEM(x, y);
12493     else if (element == EL_SP_EXIT_CLOSED)
12494       CheckExitSP(x, y);
12495     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12496              element == EL_EXPANDABLE_STEELWALL_GROWING)
12497       WallGrowing(x, y);
12498     else if (element == EL_EXPANDABLE_WALL ||
12499              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12500              element == EL_EXPANDABLE_WALL_VERTICAL ||
12501              element == EL_EXPANDABLE_WALL_ANY ||
12502              element == EL_BD_EXPANDABLE_WALL ||
12503              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12504              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12505              element == EL_EXPANDABLE_STEELWALL_ANY)
12506       CheckWallGrowing(x, y);
12507     else if (element == EL_FLAMES)
12508       CheckForDragon(x, y);
12509     else if (element == EL_EXPLOSION)
12510       ; // drawing of correct explosion animation is handled separately
12511     else if (element == EL_ELEMENT_SNAPPING ||
12512              element == EL_DIAGONAL_SHRINKING ||
12513              element == EL_DIAGONAL_GROWING)
12514     {
12515       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12516
12517       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12518     }
12519     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12520       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12521
12522     if (IS_BELT_ACTIVE(element))
12523       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12524
12525     if (game.magic_wall_active)
12526     {
12527       int jx = local_player->jx, jy = local_player->jy;
12528
12529       // play the element sound at the position nearest to the player
12530       if ((element == EL_MAGIC_WALL_FULL ||
12531            element == EL_MAGIC_WALL_ACTIVE ||
12532            element == EL_MAGIC_WALL_EMPTYING ||
12533            element == EL_BD_MAGIC_WALL_FULL ||
12534            element == EL_BD_MAGIC_WALL_ACTIVE ||
12535            element == EL_BD_MAGIC_WALL_EMPTYING ||
12536            element == EL_DC_MAGIC_WALL_FULL ||
12537            element == EL_DC_MAGIC_WALL_ACTIVE ||
12538            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12539           ABS(x - jx) + ABS(y - jy) <
12540           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12541       {
12542         magic_wall_x = x;
12543         magic_wall_y = y;
12544       }
12545     }
12546   }
12547
12548 #if USE_NEW_AMOEBA_CODE
12549   // new experimental amoeba growth stuff
12550   if (!(FrameCounter % 8))
12551   {
12552     static unsigned int random = 1684108901;
12553
12554     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12555     {
12556       x = RND(lev_fieldx);
12557       y = RND(lev_fieldy);
12558       element = Tile[x][y];
12559
12560       if (!IS_PLAYER(x, y) &&
12561           (element == EL_EMPTY ||
12562            CAN_GROW_INTO(element) ||
12563            element == EL_QUICKSAND_EMPTY ||
12564            element == EL_QUICKSAND_FAST_EMPTY ||
12565            element == EL_ACID_SPLASH_LEFT ||
12566            element == EL_ACID_SPLASH_RIGHT))
12567       {
12568         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12569             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12570             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12571             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12572           Tile[x][y] = EL_AMOEBA_DROP;
12573       }
12574
12575       random = random * 129 + 1;
12576     }
12577   }
12578 #endif
12579
12580   game.explosions_delayed = FALSE;
12581
12582   SCAN_PLAYFIELD(x, y)
12583   {
12584     element = Tile[x][y];
12585
12586     if (ExplodeField[x][y])
12587       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12588     else if (element == EL_EXPLOSION)
12589       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12590
12591     ExplodeField[x][y] = EX_TYPE_NONE;
12592   }
12593
12594   game.explosions_delayed = TRUE;
12595
12596   if (game.magic_wall_active)
12597   {
12598     if (!(game.magic_wall_time_left % 4))
12599     {
12600       int element = Tile[magic_wall_x][magic_wall_y];
12601
12602       if (element == EL_BD_MAGIC_WALL_FULL ||
12603           element == EL_BD_MAGIC_WALL_ACTIVE ||
12604           element == EL_BD_MAGIC_WALL_EMPTYING)
12605         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12606       else if (element == EL_DC_MAGIC_WALL_FULL ||
12607                element == EL_DC_MAGIC_WALL_ACTIVE ||
12608                element == EL_DC_MAGIC_WALL_EMPTYING)
12609         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12610       else
12611         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12612     }
12613
12614     if (game.magic_wall_time_left > 0)
12615     {
12616       game.magic_wall_time_left--;
12617
12618       if (!game.magic_wall_time_left)
12619       {
12620         SCAN_PLAYFIELD(x, y)
12621         {
12622           element = Tile[x][y];
12623
12624           if (element == EL_MAGIC_WALL_ACTIVE ||
12625               element == EL_MAGIC_WALL_FULL)
12626           {
12627             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12628             TEST_DrawLevelField(x, y);
12629           }
12630           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12631                    element == EL_BD_MAGIC_WALL_FULL)
12632           {
12633             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12634             TEST_DrawLevelField(x, y);
12635           }
12636           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12637                    element == EL_DC_MAGIC_WALL_FULL)
12638           {
12639             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12640             TEST_DrawLevelField(x, y);
12641           }
12642         }
12643
12644         game.magic_wall_active = FALSE;
12645       }
12646     }
12647   }
12648
12649   if (game.light_time_left > 0)
12650   {
12651     game.light_time_left--;
12652
12653     if (game.light_time_left == 0)
12654       RedrawAllLightSwitchesAndInvisibleElements();
12655   }
12656
12657   if (game.timegate_time_left > 0)
12658   {
12659     game.timegate_time_left--;
12660
12661     if (game.timegate_time_left == 0)
12662       CloseAllOpenTimegates();
12663   }
12664
12665   if (game.lenses_time_left > 0)
12666   {
12667     game.lenses_time_left--;
12668
12669     if (game.lenses_time_left == 0)
12670       RedrawAllInvisibleElementsForLenses();
12671   }
12672
12673   if (game.magnify_time_left > 0)
12674   {
12675     game.magnify_time_left--;
12676
12677     if (game.magnify_time_left == 0)
12678       RedrawAllInvisibleElementsForMagnifier();
12679   }
12680
12681   for (i = 0; i < MAX_PLAYERS; i++)
12682   {
12683     struct PlayerInfo *player = &stored_player[i];
12684
12685     if (SHIELD_ON(player))
12686     {
12687       if (player->shield_deadly_time_left)
12688         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12689       else if (player->shield_normal_time_left)
12690         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12691     }
12692   }
12693
12694 #if USE_DELAYED_GFX_REDRAW
12695   SCAN_PLAYFIELD(x, y)
12696   {
12697     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12698     {
12699       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12700          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12701
12702       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12703         DrawLevelField(x, y);
12704
12705       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12706         DrawLevelFieldCrumbled(x, y);
12707
12708       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12709         DrawLevelFieldCrumbledNeighbours(x, y);
12710
12711       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12712         DrawTwinkleOnField(x, y);
12713     }
12714
12715     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12716   }
12717 #endif
12718
12719   DrawAllPlayers();
12720   PlayAllPlayersSound();
12721
12722   for (i = 0; i < MAX_PLAYERS; i++)
12723   {
12724     struct PlayerInfo *player = &stored_player[i];
12725
12726     if (player->show_envelope != 0 && (!player->active ||
12727                                        player->MovPos == 0))
12728     {
12729       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12730
12731       player->show_envelope = 0;
12732     }
12733   }
12734
12735   // use random number generator in every frame to make it less predictable
12736   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12737     RND(1);
12738
12739   mouse_action_last = mouse_action;
12740 }
12741
12742 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12743 {
12744   int min_x = x, min_y = y, max_x = x, max_y = y;
12745   int scr_fieldx = getScreenFieldSizeX();
12746   int scr_fieldy = getScreenFieldSizeY();
12747   int i;
12748
12749   for (i = 0; i < MAX_PLAYERS; i++)
12750   {
12751     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12752
12753     if (!stored_player[i].active || &stored_player[i] == player)
12754       continue;
12755
12756     min_x = MIN(min_x, jx);
12757     min_y = MIN(min_y, jy);
12758     max_x = MAX(max_x, jx);
12759     max_y = MAX(max_y, jy);
12760   }
12761
12762   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12763 }
12764
12765 static boolean AllPlayersInVisibleScreen(void)
12766 {
12767   int i;
12768
12769   for (i = 0; i < MAX_PLAYERS; i++)
12770   {
12771     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12772
12773     if (!stored_player[i].active)
12774       continue;
12775
12776     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12777       return FALSE;
12778   }
12779
12780   return TRUE;
12781 }
12782
12783 void ScrollLevel(int dx, int dy)
12784 {
12785   int scroll_offset = 2 * TILEX_VAR;
12786   int x, y;
12787
12788   BlitBitmap(drawto_field, drawto_field,
12789              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12790              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12791              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12792              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12793              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12794              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12795
12796   if (dx != 0)
12797   {
12798     x = (dx == 1 ? BX1 : BX2);
12799     for (y = BY1; y <= BY2; y++)
12800       DrawScreenField(x, y);
12801   }
12802
12803   if (dy != 0)
12804   {
12805     y = (dy == 1 ? BY1 : BY2);
12806     for (x = BX1; x <= BX2; x++)
12807       DrawScreenField(x, y);
12808   }
12809
12810   redraw_mask |= REDRAW_FIELD;
12811 }
12812
12813 static boolean canFallDown(struct PlayerInfo *player)
12814 {
12815   int jx = player->jx, jy = player->jy;
12816
12817   return (IN_LEV_FIELD(jx, jy + 1) &&
12818           (IS_FREE(jx, jy + 1) ||
12819            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12820           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12821           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12822 }
12823
12824 static boolean canPassField(int x, int y, int move_dir)
12825 {
12826   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12827   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12828   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12829   int nextx = x + dx;
12830   int nexty = y + dy;
12831   int element = Tile[x][y];
12832
12833   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12834           !CAN_MOVE(element) &&
12835           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12836           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12837           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12838 }
12839
12840 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12841 {
12842   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12843   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12844   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12845   int newx = x + dx;
12846   int newy = y + dy;
12847
12848   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12849           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12850           (IS_DIGGABLE(Tile[newx][newy]) ||
12851            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12852            canPassField(newx, newy, move_dir)));
12853 }
12854
12855 static void CheckGravityMovement(struct PlayerInfo *player)
12856 {
12857   if (player->gravity && !player->programmed_action)
12858   {
12859     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12860     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12861     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12862     int jx = player->jx, jy = player->jy;
12863     boolean player_is_moving_to_valid_field =
12864       (!player_is_snapping &&
12865        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12866         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12867     boolean player_can_fall_down = canFallDown(player);
12868
12869     if (player_can_fall_down &&
12870         !player_is_moving_to_valid_field)
12871       player->programmed_action = MV_DOWN;
12872   }
12873 }
12874
12875 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12876 {
12877   return CheckGravityMovement(player);
12878
12879   if (player->gravity && !player->programmed_action)
12880   {
12881     int jx = player->jx, jy = player->jy;
12882     boolean field_under_player_is_free =
12883       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12884     boolean player_is_standing_on_valid_field =
12885       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12886        (IS_WALKABLE(Tile[jx][jy]) &&
12887         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12888
12889     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12890       player->programmed_action = MV_DOWN;
12891   }
12892 }
12893
12894 /*
12895   MovePlayerOneStep()
12896   -----------------------------------------------------------------------------
12897   dx, dy:               direction (non-diagonal) to try to move the player to
12898   real_dx, real_dy:     direction as read from input device (can be diagonal)
12899 */
12900
12901 boolean MovePlayerOneStep(struct PlayerInfo *player,
12902                           int dx, int dy, int real_dx, int real_dy)
12903 {
12904   int jx = player->jx, jy = player->jy;
12905   int new_jx = jx + dx, new_jy = jy + dy;
12906   int can_move;
12907   boolean player_can_move = !player->cannot_move;
12908
12909   if (!player->active || (!dx && !dy))
12910     return MP_NO_ACTION;
12911
12912   player->MovDir = (dx < 0 ? MV_LEFT :
12913                     dx > 0 ? MV_RIGHT :
12914                     dy < 0 ? MV_UP :
12915                     dy > 0 ? MV_DOWN :  MV_NONE);
12916
12917   if (!IN_LEV_FIELD(new_jx, new_jy))
12918     return MP_NO_ACTION;
12919
12920   if (!player_can_move)
12921   {
12922     if (player->MovPos == 0)
12923     {
12924       player->is_moving = FALSE;
12925       player->is_digging = FALSE;
12926       player->is_collecting = FALSE;
12927       player->is_snapping = FALSE;
12928       player->is_pushing = FALSE;
12929     }
12930   }
12931
12932   if (!network.enabled && game.centered_player_nr == -1 &&
12933       !AllPlayersInSight(player, new_jx, new_jy))
12934     return MP_NO_ACTION;
12935
12936   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12937   if (can_move != MP_MOVING)
12938     return can_move;
12939
12940   // check if DigField() has caused relocation of the player
12941   if (player->jx != jx || player->jy != jy)
12942     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12943
12944   StorePlayer[jx][jy] = 0;
12945   player->last_jx = jx;
12946   player->last_jy = jy;
12947   player->jx = new_jx;
12948   player->jy = new_jy;
12949   StorePlayer[new_jx][new_jy] = player->element_nr;
12950
12951   if (player->move_delay_value_next != -1)
12952   {
12953     player->move_delay_value = player->move_delay_value_next;
12954     player->move_delay_value_next = -1;
12955   }
12956
12957   player->MovPos =
12958     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12959
12960   player->step_counter++;
12961
12962   PlayerVisit[jx][jy] = FrameCounter;
12963
12964   player->is_moving = TRUE;
12965
12966 #if 1
12967   // should better be called in MovePlayer(), but this breaks some tapes
12968   ScrollPlayer(player, SCROLL_INIT);
12969 #endif
12970
12971   return MP_MOVING;
12972 }
12973
12974 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12975 {
12976   int jx = player->jx, jy = player->jy;
12977   int old_jx = jx, old_jy = jy;
12978   int moved = MP_NO_ACTION;
12979
12980   if (!player->active)
12981     return FALSE;
12982
12983   if (!dx && !dy)
12984   {
12985     if (player->MovPos == 0)
12986     {
12987       player->is_moving = FALSE;
12988       player->is_digging = FALSE;
12989       player->is_collecting = FALSE;
12990       player->is_snapping = FALSE;
12991       player->is_pushing = FALSE;
12992     }
12993
12994     return FALSE;
12995   }
12996
12997   if (player->move_delay > 0)
12998     return FALSE;
12999
13000   player->move_delay = -1;              // set to "uninitialized" value
13001
13002   // store if player is automatically moved to next field
13003   player->is_auto_moving = (player->programmed_action != MV_NONE);
13004
13005   // remove the last programmed player action
13006   player->programmed_action = 0;
13007
13008   if (player->MovPos)
13009   {
13010     // should only happen if pre-1.2 tape recordings are played
13011     // this is only for backward compatibility
13012
13013     int original_move_delay_value = player->move_delay_value;
13014
13015 #if DEBUG
13016     Debug("game:playing:MovePlayer",
13017           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13018           tape.counter);
13019 #endif
13020
13021     // scroll remaining steps with finest movement resolution
13022     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13023
13024     while (player->MovPos)
13025     {
13026       ScrollPlayer(player, SCROLL_GO_ON);
13027       ScrollScreen(NULL, SCROLL_GO_ON);
13028
13029       AdvanceFrameAndPlayerCounters(player->index_nr);
13030
13031       DrawAllPlayers();
13032       BackToFront_WithFrameDelay(0);
13033     }
13034
13035     player->move_delay_value = original_move_delay_value;
13036   }
13037
13038   player->is_active = FALSE;
13039
13040   if (player->last_move_dir & MV_HORIZONTAL)
13041   {
13042     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13043       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13044   }
13045   else
13046   {
13047     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13048       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13049   }
13050
13051   if (!moved && !player->is_active)
13052   {
13053     player->is_moving = FALSE;
13054     player->is_digging = FALSE;
13055     player->is_collecting = FALSE;
13056     player->is_snapping = FALSE;
13057     player->is_pushing = FALSE;
13058   }
13059
13060   jx = player->jx;
13061   jy = player->jy;
13062
13063   if (moved & MP_MOVING && !ScreenMovPos &&
13064       (player->index_nr == game.centered_player_nr ||
13065        game.centered_player_nr == -1))
13066   {
13067     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13068
13069     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13070     {
13071       // actual player has left the screen -- scroll in that direction
13072       if (jx != old_jx)         // player has moved horizontally
13073         scroll_x += (jx - old_jx);
13074       else                      // player has moved vertically
13075         scroll_y += (jy - old_jy);
13076     }
13077     else
13078     {
13079       int offset_raw = game.scroll_delay_value;
13080
13081       if (jx != old_jx)         // player has moved horizontally
13082       {
13083         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13084         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13085         int new_scroll_x = jx - MIDPOSX + offset_x;
13086
13087         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13088             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13089           scroll_x = new_scroll_x;
13090
13091         // don't scroll over playfield boundaries
13092         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13093
13094         // don't scroll more than one field at a time
13095         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13096
13097         // don't scroll against the player's moving direction
13098         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13099             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13100           scroll_x = old_scroll_x;
13101       }
13102       else                      // player has moved vertically
13103       {
13104         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13105         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13106         int new_scroll_y = jy - MIDPOSY + offset_y;
13107
13108         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13109             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13110           scroll_y = new_scroll_y;
13111
13112         // don't scroll over playfield boundaries
13113         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13114
13115         // don't scroll more than one field at a time
13116         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13117
13118         // don't scroll against the player's moving direction
13119         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13120             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13121           scroll_y = old_scroll_y;
13122       }
13123     }
13124
13125     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13126     {
13127       if (!network.enabled && game.centered_player_nr == -1 &&
13128           !AllPlayersInVisibleScreen())
13129       {
13130         scroll_x = old_scroll_x;
13131         scroll_y = old_scroll_y;
13132       }
13133       else
13134       {
13135         ScrollScreen(player, SCROLL_INIT);
13136         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13137       }
13138     }
13139   }
13140
13141   player->StepFrame = 0;
13142
13143   if (moved & MP_MOVING)
13144   {
13145     if (old_jx != jx && old_jy == jy)
13146       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13147     else if (old_jx == jx && old_jy != jy)
13148       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13149
13150     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13151
13152     player->last_move_dir = player->MovDir;
13153     player->is_moving = TRUE;
13154     player->is_snapping = FALSE;
13155     player->is_switching = FALSE;
13156     player->is_dropping = FALSE;
13157     player->is_dropping_pressed = FALSE;
13158     player->drop_pressed_delay = 0;
13159
13160 #if 0
13161     // should better be called here than above, but this breaks some tapes
13162     ScrollPlayer(player, SCROLL_INIT);
13163 #endif
13164   }
13165   else
13166   {
13167     CheckGravityMovementWhenNotMoving(player);
13168
13169     player->is_moving = FALSE;
13170
13171     /* at this point, the player is allowed to move, but cannot move right now
13172        (e.g. because of something blocking the way) -- ensure that the player
13173        is also allowed to move in the next frame (in old versions before 3.1.1,
13174        the player was forced to wait again for eight frames before next try) */
13175
13176     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13177       player->move_delay = 0;   // allow direct movement in the next frame
13178   }
13179
13180   if (player->move_delay == -1)         // not yet initialized by DigField()
13181     player->move_delay = player->move_delay_value;
13182
13183   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13184   {
13185     TestIfPlayerTouchesBadThing(jx, jy);
13186     TestIfPlayerTouchesCustomElement(jx, jy);
13187   }
13188
13189   if (!player->active)
13190     RemovePlayer(player);
13191
13192   return moved;
13193 }
13194
13195 void ScrollPlayer(struct PlayerInfo *player, int mode)
13196 {
13197   int jx = player->jx, jy = player->jy;
13198   int last_jx = player->last_jx, last_jy = player->last_jy;
13199   int move_stepsize = TILEX / player->move_delay_value;
13200
13201   if (!player->active)
13202     return;
13203
13204   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13205     return;
13206
13207   if (mode == SCROLL_INIT)
13208   {
13209     player->actual_frame_counter.count = FrameCounter;
13210     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13211
13212     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13213         Tile[last_jx][last_jy] == EL_EMPTY)
13214     {
13215       int last_field_block_delay = 0;   // start with no blocking at all
13216       int block_delay_adjustment = player->block_delay_adjustment;
13217
13218       // if player blocks last field, add delay for exactly one move
13219       if (player->block_last_field)
13220       {
13221         last_field_block_delay += player->move_delay_value;
13222
13223         // when blocking enabled, prevent moving up despite gravity
13224         if (player->gravity && player->MovDir == MV_UP)
13225           block_delay_adjustment = -1;
13226       }
13227
13228       // add block delay adjustment (also possible when not blocking)
13229       last_field_block_delay += block_delay_adjustment;
13230
13231       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13232       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13233     }
13234
13235     if (player->MovPos != 0)    // player has not yet reached destination
13236       return;
13237   }
13238   else if (!FrameReached(&player->actual_frame_counter))
13239     return;
13240
13241   if (player->MovPos != 0)
13242   {
13243     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13244     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13245
13246     // before DrawPlayer() to draw correct player graphic for this case
13247     if (player->MovPos == 0)
13248       CheckGravityMovement(player);
13249   }
13250
13251   if (player->MovPos == 0)      // player reached destination field
13252   {
13253     if (player->move_delay_reset_counter > 0)
13254     {
13255       player->move_delay_reset_counter--;
13256
13257       if (player->move_delay_reset_counter == 0)
13258       {
13259         // continue with normal speed after quickly moving through gate
13260         HALVE_PLAYER_SPEED(player);
13261
13262         // be able to make the next move without delay
13263         player->move_delay = 0;
13264       }
13265     }
13266
13267     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13268         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13269         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13270         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13271         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13272         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13273         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13274         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13275     {
13276       ExitPlayer(player);
13277
13278       if (game.players_still_needed == 0 &&
13279           (game.friends_still_needed == 0 ||
13280            IS_SP_ELEMENT(Tile[jx][jy])))
13281         LevelSolved();
13282     }
13283
13284     player->last_jx = jx;
13285     player->last_jy = jy;
13286
13287     // this breaks one level: "machine", level 000
13288     {
13289       int move_direction = player->MovDir;
13290       int enter_side = MV_DIR_OPPOSITE(move_direction);
13291       int leave_side = move_direction;
13292       int old_jx = last_jx;
13293       int old_jy = last_jy;
13294       int old_element = Tile[old_jx][old_jy];
13295       int new_element = Tile[jx][jy];
13296
13297       if (IS_CUSTOM_ELEMENT(old_element))
13298         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13299                                    CE_LEFT_BY_PLAYER,
13300                                    player->index_bit, leave_side);
13301
13302       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13303                                           CE_PLAYER_LEAVES_X,
13304                                           player->index_bit, leave_side);
13305
13306       // needed because pushed element has not yet reached its destination,
13307       // so it would trigger a change event at its previous field location
13308       if (!player->is_pushing)
13309       {
13310         if (IS_CUSTOM_ELEMENT(new_element))
13311           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13312                                      player->index_bit, enter_side);
13313
13314         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13315                                             CE_PLAYER_ENTERS_X,
13316                                             player->index_bit, enter_side);
13317       }
13318
13319       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13320                                         CE_MOVE_OF_X, move_direction);
13321     }
13322
13323     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13324     {
13325       TestIfPlayerTouchesBadThing(jx, jy);
13326       TestIfPlayerTouchesCustomElement(jx, jy);
13327
13328       // needed because pushed element has not yet reached its destination,
13329       // so it would trigger a change event at its previous field location
13330       if (!player->is_pushing)
13331         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13332
13333       if (level.finish_dig_collect &&
13334           (player->is_digging || player->is_collecting))
13335       {
13336         int last_element = player->last_removed_element;
13337         int move_direction = player->MovDir;
13338         int enter_side = MV_DIR_OPPOSITE(move_direction);
13339         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13340                             CE_PLAYER_COLLECTS_X);
13341
13342         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13343                                             player->index_bit, enter_side);
13344
13345         player->last_removed_element = EL_UNDEFINED;
13346       }
13347
13348       if (!player->active)
13349         RemovePlayer(player);
13350     }
13351
13352     if (level.use_step_counter)
13353       CheckLevelTime_StepCounter();
13354
13355     if (tape.single_step && tape.recording && !tape.pausing &&
13356         !player->programmed_action)
13357       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13358
13359     if (!player->programmed_action)
13360       CheckSaveEngineSnapshot(player);
13361   }
13362 }
13363
13364 void ScrollScreen(struct PlayerInfo *player, int mode)
13365 {
13366   static DelayCounter screen_frame_counter = { 0 };
13367
13368   if (mode == SCROLL_INIT)
13369   {
13370     // set scrolling step size according to actual player's moving speed
13371     ScrollStepSize = TILEX / player->move_delay_value;
13372
13373     screen_frame_counter.count = FrameCounter;
13374     screen_frame_counter.value = 1;
13375
13376     ScreenMovDir = player->MovDir;
13377     ScreenMovPos = player->MovPos;
13378     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13379     return;
13380   }
13381   else if (!FrameReached(&screen_frame_counter))
13382     return;
13383
13384   if (ScreenMovPos)
13385   {
13386     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13387     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13388     redraw_mask |= REDRAW_FIELD;
13389   }
13390   else
13391     ScreenMovDir = MV_NONE;
13392 }
13393
13394 void CheckNextToConditions(int x, int y)
13395 {
13396   int element = Tile[x][y];
13397
13398   if (IS_PLAYER(x, y))
13399     TestIfPlayerNextToCustomElement(x, y);
13400
13401   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13402       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13403     TestIfElementNextToCustomElement(x, y);
13404 }
13405
13406 void TestIfPlayerNextToCustomElement(int x, int y)
13407 {
13408   struct XY *xy = xy_topdown;
13409   static int trigger_sides[4][2] =
13410   {
13411     // center side       border side
13412     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13413     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13414     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13415     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13416   };
13417   int i;
13418
13419   if (!IS_PLAYER(x, y))
13420     return;
13421
13422   struct PlayerInfo *player = PLAYERINFO(x, y);
13423
13424   if (player->is_moving)
13425     return;
13426
13427   for (i = 0; i < NUM_DIRECTIONS; i++)
13428   {
13429     int xx = x + xy[i].x;
13430     int yy = y + xy[i].y;
13431     int border_side = trigger_sides[i][1];
13432     int border_element;
13433
13434     if (!IN_LEV_FIELD(xx, yy))
13435       continue;
13436
13437     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13438       continue;         // center and border element not connected
13439
13440     border_element = Tile[xx][yy];
13441
13442     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13443                                player->index_bit, border_side);
13444     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13445                                         CE_PLAYER_NEXT_TO_X,
13446                                         player->index_bit, border_side);
13447
13448     /* use player element that is initially defined in the level playfield,
13449        not the player element that corresponds to the runtime player number
13450        (example: a level that contains EL_PLAYER_3 as the only player would
13451        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13452
13453     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13454                              CE_NEXT_TO_X, border_side);
13455   }
13456 }
13457
13458 void TestIfPlayerTouchesCustomElement(int x, int y)
13459 {
13460   struct XY *xy = xy_topdown;
13461   static int trigger_sides[4][2] =
13462   {
13463     // center side       border side
13464     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13465     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13466     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13467     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13468   };
13469   static int touch_dir[4] =
13470   {
13471     MV_LEFT | MV_RIGHT,
13472     MV_UP   | MV_DOWN,
13473     MV_UP   | MV_DOWN,
13474     MV_LEFT | MV_RIGHT
13475   };
13476   int center_element = Tile[x][y];      // should always be non-moving!
13477   int i;
13478
13479   for (i = 0; i < NUM_DIRECTIONS; i++)
13480   {
13481     int xx = x + xy[i].x;
13482     int yy = y + xy[i].y;
13483     int center_side = trigger_sides[i][0];
13484     int border_side = trigger_sides[i][1];
13485     int border_element;
13486
13487     if (!IN_LEV_FIELD(xx, yy))
13488       continue;
13489
13490     if (IS_PLAYER(x, y))                // player found at center element
13491     {
13492       struct PlayerInfo *player = PLAYERINFO(x, y);
13493
13494       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13495         border_element = Tile[xx][yy];          // may be moving!
13496       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13497         border_element = Tile[xx][yy];
13498       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13499         border_element = MovingOrBlocked2Element(xx, yy);
13500       else
13501         continue;               // center and border element do not touch
13502
13503       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13504                                  player->index_bit, border_side);
13505       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13506                                           CE_PLAYER_TOUCHES_X,
13507                                           player->index_bit, border_side);
13508
13509       {
13510         /* use player element that is initially defined in the level playfield,
13511            not the player element that corresponds to the runtime player number
13512            (example: a level that contains EL_PLAYER_3 as the only player would
13513            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13514         int player_element = PLAYERINFO(x, y)->initial_element;
13515
13516         // as element "X" is the player here, check opposite (center) side
13517         CheckElementChangeBySide(xx, yy, border_element, player_element,
13518                                  CE_TOUCHING_X, center_side);
13519       }
13520     }
13521     else if (IS_PLAYER(xx, yy))         // player found at border element
13522     {
13523       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13524
13525       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13526       {
13527         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13528           continue;             // center and border element do not touch
13529       }
13530
13531       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13532                                  player->index_bit, center_side);
13533       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13534                                           CE_PLAYER_TOUCHES_X,
13535                                           player->index_bit, center_side);
13536
13537       {
13538         /* use player element that is initially defined in the level playfield,
13539            not the player element that corresponds to the runtime player number
13540            (example: a level that contains EL_PLAYER_3 as the only player would
13541            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13542         int player_element = PLAYERINFO(xx, yy)->initial_element;
13543
13544         // as element "X" is the player here, check opposite (border) side
13545         CheckElementChangeBySide(x, y, center_element, player_element,
13546                                  CE_TOUCHING_X, border_side);
13547       }
13548
13549       break;
13550     }
13551   }
13552 }
13553
13554 void TestIfElementNextToCustomElement(int x, int y)
13555 {
13556   struct XY *xy = xy_topdown;
13557   static int trigger_sides[4][2] =
13558   {
13559     // center side      border side
13560     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13561     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13562     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13563     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13564   };
13565   int center_element = Tile[x][y];      // should always be non-moving!
13566   int i;
13567
13568   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13569     return;
13570
13571   for (i = 0; i < NUM_DIRECTIONS; i++)
13572   {
13573     int xx = x + xy[i].x;
13574     int yy = y + xy[i].y;
13575     int border_side = trigger_sides[i][1];
13576     int border_element;
13577
13578     if (!IN_LEV_FIELD(xx, yy))
13579       continue;
13580
13581     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13582       continue;                 // center and border element not connected
13583
13584     border_element = Tile[xx][yy];
13585
13586     // check for change of center element (but change it only once)
13587     if (CheckElementChangeBySide(x, y, center_element, border_element,
13588                                  CE_NEXT_TO_X, border_side))
13589       break;
13590   }
13591 }
13592
13593 void TestIfElementTouchesCustomElement(int x, int y)
13594 {
13595   struct XY *xy = xy_topdown;
13596   static int trigger_sides[4][2] =
13597   {
13598     // center side      border side
13599     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13600     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13601     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13602     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13603   };
13604   static int touch_dir[4] =
13605   {
13606     MV_LEFT | MV_RIGHT,
13607     MV_UP   | MV_DOWN,
13608     MV_UP   | MV_DOWN,
13609     MV_LEFT | MV_RIGHT
13610   };
13611   boolean change_center_element = FALSE;
13612   int center_element = Tile[x][y];      // should always be non-moving!
13613   int border_element_old[NUM_DIRECTIONS];
13614   int i;
13615
13616   for (i = 0; i < NUM_DIRECTIONS; i++)
13617   {
13618     int xx = x + xy[i].x;
13619     int yy = y + xy[i].y;
13620     int border_element;
13621
13622     border_element_old[i] = -1;
13623
13624     if (!IN_LEV_FIELD(xx, yy))
13625       continue;
13626
13627     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13628       border_element = Tile[xx][yy];    // may be moving!
13629     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13630       border_element = Tile[xx][yy];
13631     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13632       border_element = MovingOrBlocked2Element(xx, yy);
13633     else
13634       continue;                 // center and border element do not touch
13635
13636     border_element_old[i] = border_element;
13637   }
13638
13639   for (i = 0; i < NUM_DIRECTIONS; i++)
13640   {
13641     int xx = x + xy[i].x;
13642     int yy = y + xy[i].y;
13643     int center_side = trigger_sides[i][0];
13644     int border_element = border_element_old[i];
13645
13646     if (border_element == -1)
13647       continue;
13648
13649     // check for change of border element
13650     CheckElementChangeBySide(xx, yy, border_element, center_element,
13651                              CE_TOUCHING_X, center_side);
13652
13653     // (center element cannot be player, so we don't have to check this here)
13654   }
13655
13656   for (i = 0; i < NUM_DIRECTIONS; i++)
13657   {
13658     int xx = x + xy[i].x;
13659     int yy = y + xy[i].y;
13660     int border_side = trigger_sides[i][1];
13661     int border_element = border_element_old[i];
13662
13663     if (border_element == -1)
13664       continue;
13665
13666     // check for change of center element (but change it only once)
13667     if (!change_center_element)
13668       change_center_element =
13669         CheckElementChangeBySide(x, y, center_element, border_element,
13670                                  CE_TOUCHING_X, border_side);
13671
13672     if (IS_PLAYER(xx, yy))
13673     {
13674       /* use player element that is initially defined in the level playfield,
13675          not the player element that corresponds to the runtime player number
13676          (example: a level that contains EL_PLAYER_3 as the only player would
13677          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13678       int player_element = PLAYERINFO(xx, yy)->initial_element;
13679
13680       // as element "X" is the player here, check opposite (border) side
13681       CheckElementChangeBySide(x, y, center_element, player_element,
13682                                CE_TOUCHING_X, border_side);
13683     }
13684   }
13685 }
13686
13687 void TestIfElementHitsCustomElement(int x, int y, int direction)
13688 {
13689   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13690   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13691   int hitx = x + dx, hity = y + dy;
13692   int hitting_element = Tile[x][y];
13693   int touched_element;
13694
13695   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13696     return;
13697
13698   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13699                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13700
13701   if (IN_LEV_FIELD(hitx, hity))
13702   {
13703     int opposite_direction = MV_DIR_OPPOSITE(direction);
13704     int hitting_side = direction;
13705     int touched_side = opposite_direction;
13706     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13707                           MovDir[hitx][hity] != direction ||
13708                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13709
13710     object_hit = TRUE;
13711
13712     if (object_hit)
13713     {
13714       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13715                                CE_HITTING_X, touched_side);
13716
13717       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13718                                CE_HIT_BY_X, hitting_side);
13719
13720       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13721                                CE_HIT_BY_SOMETHING, opposite_direction);
13722
13723       if (IS_PLAYER(hitx, hity))
13724       {
13725         /* use player element that is initially defined in the level playfield,
13726            not the player element that corresponds to the runtime player number
13727            (example: a level that contains EL_PLAYER_3 as the only player would
13728            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13729         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13730
13731         CheckElementChangeBySide(x, y, hitting_element, player_element,
13732                                  CE_HITTING_X, touched_side);
13733       }
13734     }
13735   }
13736
13737   // "hitting something" is also true when hitting the playfield border
13738   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13739                            CE_HITTING_SOMETHING, direction);
13740 }
13741
13742 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13743 {
13744   int i, kill_x = -1, kill_y = -1;
13745
13746   int bad_element = -1;
13747   struct XY *test_xy = xy_topdown;
13748   static int test_dir[4] =
13749   {
13750     MV_UP,
13751     MV_LEFT,
13752     MV_RIGHT,
13753     MV_DOWN
13754   };
13755
13756   for (i = 0; i < NUM_DIRECTIONS; i++)
13757   {
13758     int test_x, test_y, test_move_dir, test_element;
13759
13760     test_x = good_x + test_xy[i].x;
13761     test_y = good_y + test_xy[i].y;
13762
13763     if (!IN_LEV_FIELD(test_x, test_y))
13764       continue;
13765
13766     test_move_dir =
13767       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13768
13769     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13770
13771     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13772        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13773     */
13774     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13775         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13776     {
13777       kill_x = test_x;
13778       kill_y = test_y;
13779       bad_element = test_element;
13780
13781       break;
13782     }
13783   }
13784
13785   if (kill_x != -1 || kill_y != -1)
13786   {
13787     if (IS_PLAYER(good_x, good_y))
13788     {
13789       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13790
13791       if (player->shield_deadly_time_left > 0 &&
13792           !IS_INDESTRUCTIBLE(bad_element))
13793         Bang(kill_x, kill_y);
13794       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13795         KillPlayer(player);
13796     }
13797     else
13798       Bang(good_x, good_y);
13799   }
13800 }
13801
13802 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13803 {
13804   int i, kill_x = -1, kill_y = -1;
13805   int bad_element = Tile[bad_x][bad_y];
13806   struct XY *test_xy = xy_topdown;
13807   static int touch_dir[4] =
13808   {
13809     MV_LEFT | MV_RIGHT,
13810     MV_UP   | MV_DOWN,
13811     MV_UP   | MV_DOWN,
13812     MV_LEFT | MV_RIGHT
13813   };
13814   static int test_dir[4] =
13815   {
13816     MV_UP,
13817     MV_LEFT,
13818     MV_RIGHT,
13819     MV_DOWN
13820   };
13821
13822   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13823     return;
13824
13825   for (i = 0; i < NUM_DIRECTIONS; i++)
13826   {
13827     int test_x, test_y, test_move_dir, test_element;
13828
13829     test_x = bad_x + test_xy[i].x;
13830     test_y = bad_y + test_xy[i].y;
13831
13832     if (!IN_LEV_FIELD(test_x, test_y))
13833       continue;
13834
13835     test_move_dir =
13836       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13837
13838     test_element = Tile[test_x][test_y];
13839
13840     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13841        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13842     */
13843     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13844         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13845     {
13846       // good thing is player or penguin that does not move away
13847       if (IS_PLAYER(test_x, test_y))
13848       {
13849         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13850
13851         if (bad_element == EL_ROBOT && player->is_moving)
13852           continue;     // robot does not kill player if he is moving
13853
13854         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13855         {
13856           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13857             continue;           // center and border element do not touch
13858         }
13859
13860         kill_x = test_x;
13861         kill_y = test_y;
13862
13863         break;
13864       }
13865       else if (test_element == EL_PENGUIN)
13866       {
13867         kill_x = test_x;
13868         kill_y = test_y;
13869
13870         break;
13871       }
13872     }
13873   }
13874
13875   if (kill_x != -1 || kill_y != -1)
13876   {
13877     if (IS_PLAYER(kill_x, kill_y))
13878     {
13879       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13880
13881       if (player->shield_deadly_time_left > 0 &&
13882           !IS_INDESTRUCTIBLE(bad_element))
13883         Bang(bad_x, bad_y);
13884       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13885         KillPlayer(player);
13886     }
13887     else
13888       Bang(kill_x, kill_y);
13889   }
13890 }
13891
13892 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13893 {
13894   int bad_element = Tile[bad_x][bad_y];
13895   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13896   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13897   int test_x = bad_x + dx, test_y = bad_y + dy;
13898   int test_move_dir, test_element;
13899   int kill_x = -1, kill_y = -1;
13900
13901   if (!IN_LEV_FIELD(test_x, test_y))
13902     return;
13903
13904   test_move_dir =
13905     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13906
13907   test_element = Tile[test_x][test_y];
13908
13909   if (test_move_dir != bad_move_dir)
13910   {
13911     // good thing can be player or penguin that does not move away
13912     if (IS_PLAYER(test_x, test_y))
13913     {
13914       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13915
13916       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13917          player as being hit when he is moving towards the bad thing, because
13918          the "get hit by" condition would be lost after the player stops) */
13919       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13920         return;         // player moves away from bad thing
13921
13922       kill_x = test_x;
13923       kill_y = test_y;
13924     }
13925     else if (test_element == EL_PENGUIN)
13926     {
13927       kill_x = test_x;
13928       kill_y = test_y;
13929     }
13930   }
13931
13932   if (kill_x != -1 || kill_y != -1)
13933   {
13934     if (IS_PLAYER(kill_x, kill_y))
13935     {
13936       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13937
13938       if (player->shield_deadly_time_left > 0 &&
13939           !IS_INDESTRUCTIBLE(bad_element))
13940         Bang(bad_x, bad_y);
13941       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13942         KillPlayer(player);
13943     }
13944     else
13945       Bang(kill_x, kill_y);
13946   }
13947 }
13948
13949 void TestIfPlayerTouchesBadThing(int x, int y)
13950 {
13951   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13952 }
13953
13954 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13955 {
13956   TestIfGoodThingHitsBadThing(x, y, move_dir);
13957 }
13958
13959 void TestIfBadThingTouchesPlayer(int x, int y)
13960 {
13961   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13962 }
13963
13964 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13965 {
13966   TestIfBadThingHitsGoodThing(x, y, move_dir);
13967 }
13968
13969 void TestIfFriendTouchesBadThing(int x, int y)
13970 {
13971   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13972 }
13973
13974 void TestIfBadThingTouchesFriend(int x, int y)
13975 {
13976   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13977 }
13978
13979 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13980 {
13981   int i, kill_x = bad_x, kill_y = bad_y;
13982   struct XY *xy = xy_topdown;
13983
13984   for (i = 0; i < NUM_DIRECTIONS; i++)
13985   {
13986     int x, y, element;
13987
13988     x = bad_x + xy[i].x;
13989     y = bad_y + xy[i].y;
13990     if (!IN_LEV_FIELD(x, y))
13991       continue;
13992
13993     element = Tile[x][y];
13994     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13995         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13996     {
13997       kill_x = x;
13998       kill_y = y;
13999       break;
14000     }
14001   }
14002
14003   if (kill_x != bad_x || kill_y != bad_y)
14004     Bang(bad_x, bad_y);
14005 }
14006
14007 void KillPlayer(struct PlayerInfo *player)
14008 {
14009   int jx = player->jx, jy = player->jy;
14010
14011   if (!player->active)
14012     return;
14013
14014 #if 0
14015   Debug("game:playing:KillPlayer",
14016         "0: killed == %d, active == %d, reanimated == %d",
14017         player->killed, player->active, player->reanimated);
14018 #endif
14019
14020   /* the following code was introduced to prevent an infinite loop when calling
14021      -> Bang()
14022      -> CheckTriggeredElementChangeExt()
14023      -> ExecuteCustomElementAction()
14024      -> KillPlayer()
14025      -> (infinitely repeating the above sequence of function calls)
14026      which occurs when killing the player while having a CE with the setting
14027      "kill player X when explosion of <player X>"; the solution using a new
14028      field "player->killed" was chosen for backwards compatibility, although
14029      clever use of the fields "player->active" etc. would probably also work */
14030 #if 1
14031   if (player->killed)
14032     return;
14033 #endif
14034
14035   player->killed = TRUE;
14036
14037   // remove accessible field at the player's position
14038   RemoveField(jx, jy);
14039
14040   // deactivate shield (else Bang()/Explode() would not work right)
14041   player->shield_normal_time_left = 0;
14042   player->shield_deadly_time_left = 0;
14043
14044 #if 0
14045   Debug("game:playing:KillPlayer",
14046         "1: killed == %d, active == %d, reanimated == %d",
14047         player->killed, player->active, player->reanimated);
14048 #endif
14049
14050   Bang(jx, jy);
14051
14052 #if 0
14053   Debug("game:playing:KillPlayer",
14054         "2: killed == %d, active == %d, reanimated == %d",
14055         player->killed, player->active, player->reanimated);
14056 #endif
14057
14058   if (player->reanimated)       // killed player may have been reanimated
14059     player->killed = player->reanimated = FALSE;
14060   else
14061     BuryPlayer(player);
14062 }
14063
14064 static void KillPlayerUnlessEnemyProtected(int x, int y)
14065 {
14066   if (!PLAYER_ENEMY_PROTECTED(x, y))
14067     KillPlayer(PLAYERINFO(x, y));
14068 }
14069
14070 static void KillPlayerUnlessExplosionProtected(int x, int y)
14071 {
14072   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14073     KillPlayer(PLAYERINFO(x, y));
14074 }
14075
14076 void BuryPlayer(struct PlayerInfo *player)
14077 {
14078   int jx = player->jx, jy = player->jy;
14079
14080   if (!player->active)
14081     return;
14082
14083   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14084
14085   RemovePlayer(player);
14086
14087   player->buried = TRUE;
14088
14089   if (game.all_players_gone)
14090     game.GameOver = TRUE;
14091 }
14092
14093 void RemovePlayer(struct PlayerInfo *player)
14094 {
14095   int jx = player->jx, jy = player->jy;
14096   int i, found = FALSE;
14097
14098   player->present = FALSE;
14099   player->active = FALSE;
14100
14101   // required for some CE actions (even if the player is not active anymore)
14102   player->MovPos = 0;
14103
14104   if (!ExplodeField[jx][jy])
14105     StorePlayer[jx][jy] = 0;
14106
14107   if (player->is_moving)
14108     TEST_DrawLevelField(player->last_jx, player->last_jy);
14109
14110   for (i = 0; i < MAX_PLAYERS; i++)
14111     if (stored_player[i].active)
14112       found = TRUE;
14113
14114   if (!found)
14115   {
14116     game.all_players_gone = TRUE;
14117     game.GameOver = TRUE;
14118   }
14119
14120   game.exit_x = game.robot_wheel_x = jx;
14121   game.exit_y = game.robot_wheel_y = jy;
14122 }
14123
14124 void ExitPlayer(struct PlayerInfo *player)
14125 {
14126   DrawPlayer(player);   // needed here only to cleanup last field
14127   RemovePlayer(player);
14128
14129   if (game.players_still_needed > 0)
14130     game.players_still_needed--;
14131 }
14132
14133 static void SetFieldForSnapping(int x, int y, int element, int direction,
14134                                 int player_index_bit)
14135 {
14136   struct ElementInfo *ei = &element_info[element];
14137   int direction_bit = MV_DIR_TO_BIT(direction);
14138   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14139   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14140                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14141
14142   Tile[x][y] = EL_ELEMENT_SNAPPING;
14143   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14144   MovDir[x][y] = direction;
14145   Store[x][y] = element;
14146   Store2[x][y] = player_index_bit;
14147
14148   ResetGfxAnimation(x, y);
14149
14150   GfxElement[x][y] = element;
14151   GfxAction[x][y] = action;
14152   GfxDir[x][y] = direction;
14153   GfxFrame[x][y] = -1;
14154 }
14155
14156 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14157                                    int player_index_bit)
14158 {
14159   TestIfElementTouchesCustomElement(x, y);      // for empty space
14160
14161   if (level.finish_dig_collect)
14162   {
14163     int dig_side = MV_DIR_OPPOSITE(direction);
14164     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14165                         CE_PLAYER_COLLECTS_X);
14166
14167     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14168                                         player_index_bit, dig_side);
14169     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14170                                         player_index_bit, dig_side);
14171   }
14172 }
14173
14174 /*
14175   =============================================================================
14176   checkDiagonalPushing()
14177   -----------------------------------------------------------------------------
14178   check if diagonal input device direction results in pushing of object
14179   (by checking if the alternative direction is walkable, diggable, ...)
14180   =============================================================================
14181 */
14182
14183 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14184                                     int x, int y, int real_dx, int real_dy)
14185 {
14186   int jx, jy, dx, dy, xx, yy;
14187
14188   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14189     return TRUE;
14190
14191   // diagonal direction: check alternative direction
14192   jx = player->jx;
14193   jy = player->jy;
14194   dx = x - jx;
14195   dy = y - jy;
14196   xx = jx + (dx == 0 ? real_dx : 0);
14197   yy = jy + (dy == 0 ? real_dy : 0);
14198
14199   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14200 }
14201
14202 /*
14203   =============================================================================
14204   DigField()
14205   -----------------------------------------------------------------------------
14206   x, y:                 field next to player (non-diagonal) to try to dig to
14207   real_dx, real_dy:     direction as read from input device (can be diagonal)
14208   =============================================================================
14209 */
14210
14211 static int DigField(struct PlayerInfo *player,
14212                     int oldx, int oldy, int x, int y,
14213                     int real_dx, int real_dy, int mode)
14214 {
14215   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14216   boolean player_was_pushing = player->is_pushing;
14217   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14218   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14219   int jx = oldx, jy = oldy;
14220   int dx = x - jx, dy = y - jy;
14221   int nextx = x + dx, nexty = y + dy;
14222   int move_direction = (dx == -1 ? MV_LEFT  :
14223                         dx == +1 ? MV_RIGHT :
14224                         dy == -1 ? MV_UP    :
14225                         dy == +1 ? MV_DOWN  : MV_NONE);
14226   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14227   int dig_side = MV_DIR_OPPOSITE(move_direction);
14228   int old_element = Tile[jx][jy];
14229   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14230   int collect_count;
14231
14232   if (is_player)                // function can also be called by EL_PENGUIN
14233   {
14234     if (player->MovPos == 0)
14235     {
14236       player->is_digging = FALSE;
14237       player->is_collecting = FALSE;
14238     }
14239
14240     if (player->MovPos == 0)    // last pushing move finished
14241       player->is_pushing = FALSE;
14242
14243     if (mode == DF_NO_PUSH)     // player just stopped pushing
14244     {
14245       player->is_switching = FALSE;
14246       player->push_delay = -1;
14247
14248       return MP_NO_ACTION;
14249     }
14250   }
14251   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14252     old_element = Back[jx][jy];
14253
14254   // in case of element dropped at player position, check background
14255   else if (Back[jx][jy] != EL_EMPTY &&
14256            game.engine_version >= VERSION_IDENT(2,2,0,0))
14257     old_element = Back[jx][jy];
14258
14259   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14260     return MP_NO_ACTION;        // field has no opening in this direction
14261
14262   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14263     return MP_NO_ACTION;        // field has no opening in this direction
14264
14265   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14266   {
14267     SplashAcid(x, y);
14268
14269     Tile[jx][jy] = player->artwork_element;
14270     InitMovingField(jx, jy, MV_DOWN);
14271     Store[jx][jy] = EL_ACID;
14272     ContinueMoving(jx, jy);
14273     BuryPlayer(player);
14274
14275     return MP_DONT_RUN_INTO;
14276   }
14277
14278   if (player_can_move && DONT_RUN_INTO(element))
14279   {
14280     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14281
14282     return MP_DONT_RUN_INTO;
14283   }
14284
14285   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14286     return MP_NO_ACTION;
14287
14288   collect_count = element_info[element].collect_count_initial;
14289
14290   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14291     return MP_NO_ACTION;
14292
14293   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14294     player_can_move = player_can_move_or_snap;
14295
14296   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14297       game.engine_version >= VERSION_IDENT(2,2,0,0))
14298   {
14299     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14300                                player->index_bit, dig_side);
14301     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14302                                         player->index_bit, dig_side);
14303
14304     if (element == EL_DC_LANDMINE)
14305       Bang(x, y);
14306
14307     if (Tile[x][y] != element)          // field changed by snapping
14308       return MP_ACTION;
14309
14310     return MP_NO_ACTION;
14311   }
14312
14313   if (player->gravity && is_player && !player->is_auto_moving &&
14314       canFallDown(player) && move_direction != MV_DOWN &&
14315       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14316     return MP_NO_ACTION;        // player cannot walk here due to gravity
14317
14318   if (player_can_move &&
14319       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14320   {
14321     int sound_element = SND_ELEMENT(element);
14322     int sound_action = ACTION_WALKING;
14323
14324     if (IS_RND_GATE(element))
14325     {
14326       if (!player->key[RND_GATE_NR(element)])
14327         return MP_NO_ACTION;
14328     }
14329     else if (IS_RND_GATE_GRAY(element))
14330     {
14331       if (!player->key[RND_GATE_GRAY_NR(element)])
14332         return MP_NO_ACTION;
14333     }
14334     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14335     {
14336       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14337         return MP_NO_ACTION;
14338     }
14339     else if (element == EL_EXIT_OPEN ||
14340              element == EL_EM_EXIT_OPEN ||
14341              element == EL_EM_EXIT_OPENING ||
14342              element == EL_STEEL_EXIT_OPEN ||
14343              element == EL_EM_STEEL_EXIT_OPEN ||
14344              element == EL_EM_STEEL_EXIT_OPENING ||
14345              element == EL_SP_EXIT_OPEN ||
14346              element == EL_SP_EXIT_OPENING)
14347     {
14348       sound_action = ACTION_PASSING;    // player is passing exit
14349     }
14350     else if (element == EL_EMPTY)
14351     {
14352       sound_action = ACTION_MOVING;             // nothing to walk on
14353     }
14354
14355     // play sound from background or player, whatever is available
14356     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14357       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14358     else
14359       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14360   }
14361   else if (player_can_move &&
14362            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14363   {
14364     if (!ACCESS_FROM(element, opposite_direction))
14365       return MP_NO_ACTION;      // field not accessible from this direction
14366
14367     if (CAN_MOVE(element))      // only fixed elements can be passed!
14368       return MP_NO_ACTION;
14369
14370     if (IS_EM_GATE(element))
14371     {
14372       if (!player->key[EM_GATE_NR(element)])
14373         return MP_NO_ACTION;
14374     }
14375     else if (IS_EM_GATE_GRAY(element))
14376     {
14377       if (!player->key[EM_GATE_GRAY_NR(element)])
14378         return MP_NO_ACTION;
14379     }
14380     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14381     {
14382       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14383         return MP_NO_ACTION;
14384     }
14385     else if (IS_EMC_GATE(element))
14386     {
14387       if (!player->key[EMC_GATE_NR(element)])
14388         return MP_NO_ACTION;
14389     }
14390     else if (IS_EMC_GATE_GRAY(element))
14391     {
14392       if (!player->key[EMC_GATE_GRAY_NR(element)])
14393         return MP_NO_ACTION;
14394     }
14395     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14396     {
14397       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14398         return MP_NO_ACTION;
14399     }
14400     else if (element == EL_DC_GATE_WHITE ||
14401              element == EL_DC_GATE_WHITE_GRAY ||
14402              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14403     {
14404       if (player->num_white_keys == 0)
14405         return MP_NO_ACTION;
14406
14407       player->num_white_keys--;
14408     }
14409     else if (IS_SP_PORT(element))
14410     {
14411       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14412           element == EL_SP_GRAVITY_PORT_RIGHT ||
14413           element == EL_SP_GRAVITY_PORT_UP ||
14414           element == EL_SP_GRAVITY_PORT_DOWN)
14415         player->gravity = !player->gravity;
14416       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14417                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14418                element == EL_SP_GRAVITY_ON_PORT_UP ||
14419                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14420         player->gravity = TRUE;
14421       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14422                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14423                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14424                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14425         player->gravity = FALSE;
14426     }
14427
14428     // automatically move to the next field with double speed
14429     player->programmed_action = move_direction;
14430
14431     if (player->move_delay_reset_counter == 0)
14432     {
14433       player->move_delay_reset_counter = 2;     // two double speed steps
14434
14435       DOUBLE_PLAYER_SPEED(player);
14436     }
14437
14438     PlayLevelSoundAction(x, y, ACTION_PASSING);
14439   }
14440   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14441   {
14442     RemoveField(x, y);
14443
14444     if (mode != DF_SNAP)
14445     {
14446       GfxElement[x][y] = GFX_ELEMENT(element);
14447       player->is_digging = TRUE;
14448     }
14449
14450     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14451
14452     // use old behaviour for old levels (digging)
14453     if (!level.finish_dig_collect)
14454     {
14455       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14456                                           player->index_bit, dig_side);
14457
14458       // if digging triggered player relocation, finish digging tile
14459       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14460         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14461     }
14462
14463     if (mode == DF_SNAP)
14464     {
14465       if (level.block_snap_field)
14466         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14467       else
14468         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14469
14470       // use old behaviour for old levels (snapping)
14471       if (!level.finish_dig_collect)
14472         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14473                                             player->index_bit, dig_side);
14474     }
14475   }
14476   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14477   {
14478     RemoveField(x, y);
14479
14480     if (is_player && mode != DF_SNAP)
14481     {
14482       GfxElement[x][y] = element;
14483       player->is_collecting = TRUE;
14484     }
14485
14486     if (element == EL_SPEED_PILL)
14487     {
14488       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14489     }
14490     else if (element == EL_EXTRA_TIME && level.time > 0)
14491     {
14492       TimeLeft += level.extra_time;
14493
14494       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14495
14496       DisplayGameControlValues();
14497     }
14498     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14499     {
14500       int shield_time = (element == EL_SHIELD_DEADLY ?
14501                          level.shield_deadly_time :
14502                          level.shield_normal_time);
14503
14504       player->shield_normal_time_left += shield_time;
14505       if (element == EL_SHIELD_DEADLY)
14506         player->shield_deadly_time_left += shield_time;
14507     }
14508     else if (element == EL_DYNAMITE ||
14509              element == EL_EM_DYNAMITE ||
14510              element == EL_SP_DISK_RED)
14511     {
14512       if (player->inventory_size < MAX_INVENTORY_SIZE)
14513         player->inventory_element[player->inventory_size++] = element;
14514
14515       DrawGameDoorValues();
14516     }
14517     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14518     {
14519       player->dynabomb_count++;
14520       player->dynabombs_left++;
14521     }
14522     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14523     {
14524       player->dynabomb_size++;
14525     }
14526     else if (element == EL_DYNABOMB_INCREASE_POWER)
14527     {
14528       player->dynabomb_xl = TRUE;
14529     }
14530     else if (IS_KEY(element))
14531     {
14532       player->key[KEY_NR(element)] = TRUE;
14533
14534       DrawGameDoorValues();
14535     }
14536     else if (element == EL_DC_KEY_WHITE)
14537     {
14538       player->num_white_keys++;
14539
14540       // display white keys?
14541       // DrawGameDoorValues();
14542     }
14543     else if (IS_ENVELOPE(element))
14544     {
14545       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14546
14547       if (!wait_for_snapping)
14548         player->show_envelope = element;
14549     }
14550     else if (element == EL_EMC_LENSES)
14551     {
14552       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14553
14554       RedrawAllInvisibleElementsForLenses();
14555     }
14556     else if (element == EL_EMC_MAGNIFIER)
14557     {
14558       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14559
14560       RedrawAllInvisibleElementsForMagnifier();
14561     }
14562     else if (IS_DROPPABLE(element) ||
14563              IS_THROWABLE(element))     // can be collected and dropped
14564     {
14565       int i;
14566
14567       if (collect_count == 0)
14568         player->inventory_infinite_element = element;
14569       else
14570         for (i = 0; i < collect_count; i++)
14571           if (player->inventory_size < MAX_INVENTORY_SIZE)
14572             player->inventory_element[player->inventory_size++] = element;
14573
14574       DrawGameDoorValues();
14575     }
14576     else if (collect_count > 0)
14577     {
14578       game.gems_still_needed -= collect_count;
14579       if (game.gems_still_needed < 0)
14580         game.gems_still_needed = 0;
14581
14582       game.snapshot.collected_item = TRUE;
14583
14584       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14585
14586       DisplayGameControlValues();
14587     }
14588
14589     RaiseScoreElement(element);
14590     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14591
14592     // use old behaviour for old levels (collecting)
14593     if (!level.finish_dig_collect && is_player)
14594     {
14595       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14596                                           player->index_bit, dig_side);
14597
14598       // if collecting triggered player relocation, finish collecting tile
14599       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14600         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14601     }
14602
14603     if (mode == DF_SNAP)
14604     {
14605       if (level.block_snap_field)
14606         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14607       else
14608         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14609
14610       // use old behaviour for old levels (snapping)
14611       if (!level.finish_dig_collect)
14612         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14613                                             player->index_bit, dig_side);
14614     }
14615   }
14616   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14617   {
14618     if (mode == DF_SNAP && element != EL_BD_ROCK)
14619       return MP_NO_ACTION;
14620
14621     if (CAN_FALL(element) && dy)
14622       return MP_NO_ACTION;
14623
14624     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14625         !(element == EL_SPRING && level.use_spring_bug))
14626       return MP_NO_ACTION;
14627
14628     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14629         ((move_direction & MV_VERTICAL &&
14630           ((element_info[element].move_pattern & MV_LEFT &&
14631             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14632            (element_info[element].move_pattern & MV_RIGHT &&
14633             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14634          (move_direction & MV_HORIZONTAL &&
14635           ((element_info[element].move_pattern & MV_UP &&
14636             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14637            (element_info[element].move_pattern & MV_DOWN &&
14638             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14639       return MP_NO_ACTION;
14640
14641     // do not push elements already moving away faster than player
14642     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14643         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14644       return MP_NO_ACTION;
14645
14646     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14647     {
14648       if (player->push_delay_value == -1 || !player_was_pushing)
14649         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14650     }
14651     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14652     {
14653       if (player->push_delay_value == -1)
14654         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14655     }
14656     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14657     {
14658       if (!player->is_pushing)
14659         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14660     }
14661
14662     player->is_pushing = TRUE;
14663     player->is_active = TRUE;
14664
14665     if (!(IN_LEV_FIELD(nextx, nexty) &&
14666           (IS_FREE(nextx, nexty) ||
14667            (IS_SB_ELEMENT(element) &&
14668             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14669            (IS_CUSTOM_ELEMENT(element) &&
14670             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14671       return MP_NO_ACTION;
14672
14673     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14674       return MP_NO_ACTION;
14675
14676     if (player->push_delay == -1)       // new pushing; restart delay
14677       player->push_delay = 0;
14678
14679     if (player->push_delay < player->push_delay_value &&
14680         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14681         element != EL_SPRING && element != EL_BALLOON)
14682     {
14683       // make sure that there is no move delay before next try to push
14684       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14685         player->move_delay = 0;
14686
14687       return MP_NO_ACTION;
14688     }
14689
14690     if (IS_CUSTOM_ELEMENT(element) &&
14691         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14692     {
14693       if (!DigFieldByCE(nextx, nexty, element))
14694         return MP_NO_ACTION;
14695     }
14696
14697     if (IS_SB_ELEMENT(element))
14698     {
14699       boolean sokoban_task_solved = FALSE;
14700
14701       if (element == EL_SOKOBAN_FIELD_FULL)
14702       {
14703         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14704
14705         IncrementSokobanFieldsNeeded();
14706         IncrementSokobanObjectsNeeded();
14707       }
14708
14709       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14710       {
14711         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14712
14713         DecrementSokobanFieldsNeeded();
14714         DecrementSokobanObjectsNeeded();
14715
14716         // sokoban object was pushed from empty field to sokoban field
14717         if (Back[x][y] == EL_EMPTY)
14718           sokoban_task_solved = TRUE;
14719       }
14720
14721       Tile[x][y] = EL_SOKOBAN_OBJECT;
14722
14723       if (Back[x][y] == Back[nextx][nexty])
14724         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14725       else if (Back[x][y] != 0)
14726         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14727                                     ACTION_EMPTYING);
14728       else
14729         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14730                                     ACTION_FILLING);
14731
14732       if (sokoban_task_solved &&
14733           game.sokoban_fields_still_needed == 0 &&
14734           game.sokoban_objects_still_needed == 0 &&
14735           level.auto_exit_sokoban)
14736       {
14737         game.players_still_needed = 0;
14738
14739         LevelSolved();
14740
14741         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14742       }
14743     }
14744     else
14745       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14746
14747     InitMovingField(x, y, move_direction);
14748     GfxAction[x][y] = ACTION_PUSHING;
14749
14750     if (mode == DF_SNAP)
14751       ContinueMoving(x, y);
14752     else
14753       MovPos[x][y] = (dx != 0 ? dx : dy);
14754
14755     Pushed[x][y] = TRUE;
14756     Pushed[nextx][nexty] = TRUE;
14757
14758     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14759       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14760     else
14761       player->push_delay_value = -1;    // get new value later
14762
14763     // check for element change _after_ element has been pushed
14764     if (game.use_change_when_pushing_bug)
14765     {
14766       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14767                                  player->index_bit, dig_side);
14768       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14769                                           player->index_bit, dig_side);
14770     }
14771   }
14772   else if (IS_SWITCHABLE(element))
14773   {
14774     if (PLAYER_SWITCHING(player, x, y))
14775     {
14776       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14777                                           player->index_bit, dig_side);
14778
14779       return MP_ACTION;
14780     }
14781
14782     player->is_switching = TRUE;
14783     player->switch_x = x;
14784     player->switch_y = y;
14785
14786     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14787
14788     if (element == EL_ROBOT_WHEEL)
14789     {
14790       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14791
14792       game.robot_wheel_x = x;
14793       game.robot_wheel_y = y;
14794       game.robot_wheel_active = TRUE;
14795
14796       TEST_DrawLevelField(x, y);
14797     }
14798     else if (element == EL_SP_TERMINAL)
14799     {
14800       int xx, yy;
14801
14802       SCAN_PLAYFIELD(xx, yy)
14803       {
14804         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14805         {
14806           Bang(xx, yy);
14807         }
14808         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14809         {
14810           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14811
14812           ResetGfxAnimation(xx, yy);
14813           TEST_DrawLevelField(xx, yy);
14814         }
14815       }
14816     }
14817     else if (IS_BELT_SWITCH(element))
14818     {
14819       ToggleBeltSwitch(x, y);
14820     }
14821     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14822              element == EL_SWITCHGATE_SWITCH_DOWN ||
14823              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14824              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14825     {
14826       ToggleSwitchgateSwitch();
14827     }
14828     else if (element == EL_LIGHT_SWITCH ||
14829              element == EL_LIGHT_SWITCH_ACTIVE)
14830     {
14831       ToggleLightSwitch(x, y);
14832     }
14833     else if (element == EL_TIMEGATE_SWITCH ||
14834              element == EL_DC_TIMEGATE_SWITCH)
14835     {
14836       ActivateTimegateSwitch(x, y);
14837     }
14838     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14839              element == EL_BALLOON_SWITCH_RIGHT ||
14840              element == EL_BALLOON_SWITCH_UP    ||
14841              element == EL_BALLOON_SWITCH_DOWN  ||
14842              element == EL_BALLOON_SWITCH_NONE  ||
14843              element == EL_BALLOON_SWITCH_ANY)
14844     {
14845       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14846                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14847                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14848                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14849                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14850                              move_direction);
14851     }
14852     else if (element == EL_LAMP)
14853     {
14854       Tile[x][y] = EL_LAMP_ACTIVE;
14855       game.lights_still_needed--;
14856
14857       ResetGfxAnimation(x, y);
14858       TEST_DrawLevelField(x, y);
14859     }
14860     else if (element == EL_TIME_ORB_FULL)
14861     {
14862       Tile[x][y] = EL_TIME_ORB_EMPTY;
14863
14864       if (level.time > 0 || level.use_time_orb_bug)
14865       {
14866         TimeLeft += level.time_orb_time;
14867         game.no_level_time_limit = FALSE;
14868
14869         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14870
14871         DisplayGameControlValues();
14872       }
14873
14874       ResetGfxAnimation(x, y);
14875       TEST_DrawLevelField(x, y);
14876     }
14877     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14878              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14879     {
14880       int xx, yy;
14881
14882       game.ball_active = !game.ball_active;
14883
14884       SCAN_PLAYFIELD(xx, yy)
14885       {
14886         int e = Tile[xx][yy];
14887
14888         if (game.ball_active)
14889         {
14890           if (e == EL_EMC_MAGIC_BALL)
14891             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14892           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14893             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14894         }
14895         else
14896         {
14897           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14898             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14899           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14900             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14901         }
14902       }
14903     }
14904
14905     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14906                                         player->index_bit, dig_side);
14907
14908     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14909                                         player->index_bit, dig_side);
14910
14911     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14912                                         player->index_bit, dig_side);
14913
14914     return MP_ACTION;
14915   }
14916   else
14917   {
14918     if (!PLAYER_SWITCHING(player, x, y))
14919     {
14920       player->is_switching = TRUE;
14921       player->switch_x = x;
14922       player->switch_y = y;
14923
14924       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14925                                  player->index_bit, dig_side);
14926       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14927                                           player->index_bit, dig_side);
14928
14929       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14930                                  player->index_bit, dig_side);
14931       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14932                                           player->index_bit, dig_side);
14933     }
14934
14935     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14936                                player->index_bit, dig_side);
14937     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14938                                         player->index_bit, dig_side);
14939
14940     return MP_NO_ACTION;
14941   }
14942
14943   player->push_delay = -1;
14944
14945   if (is_player)                // function can also be called by EL_PENGUIN
14946   {
14947     if (Tile[x][y] != element)          // really digged/collected something
14948     {
14949       player->is_collecting = !player->is_digging;
14950       player->is_active = TRUE;
14951
14952       player->last_removed_element = element;
14953     }
14954   }
14955
14956   return MP_MOVING;
14957 }
14958
14959 static boolean DigFieldByCE(int x, int y, int digging_element)
14960 {
14961   int element = Tile[x][y];
14962
14963   if (!IS_FREE(x, y))
14964   {
14965     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14966                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14967                   ACTION_BREAKING);
14968
14969     // no element can dig solid indestructible elements
14970     if (IS_INDESTRUCTIBLE(element) &&
14971         !IS_DIGGABLE(element) &&
14972         !IS_COLLECTIBLE(element))
14973       return FALSE;
14974
14975     if (AmoebaNr[x][y] &&
14976         (element == EL_AMOEBA_FULL ||
14977          element == EL_BD_AMOEBA ||
14978          element == EL_AMOEBA_GROWING))
14979     {
14980       AmoebaCnt[AmoebaNr[x][y]]--;
14981       AmoebaCnt2[AmoebaNr[x][y]]--;
14982     }
14983
14984     if (IS_MOVING(x, y))
14985       RemoveMovingField(x, y);
14986     else
14987     {
14988       RemoveField(x, y);
14989       TEST_DrawLevelField(x, y);
14990     }
14991
14992     // if digged element was about to explode, prevent the explosion
14993     ExplodeField[x][y] = EX_TYPE_NONE;
14994
14995     PlayLevelSoundAction(x, y, action);
14996   }
14997
14998   Store[x][y] = EL_EMPTY;
14999
15000   // this makes it possible to leave the removed element again
15001   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15002     Store[x][y] = element;
15003
15004   return TRUE;
15005 }
15006
15007 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15008 {
15009   int jx = player->jx, jy = player->jy;
15010   int x = jx + dx, y = jy + dy;
15011   int snap_direction = (dx == -1 ? MV_LEFT  :
15012                         dx == +1 ? MV_RIGHT :
15013                         dy == -1 ? MV_UP    :
15014                         dy == +1 ? MV_DOWN  : MV_NONE);
15015   boolean can_continue_snapping = (level.continuous_snapping &&
15016                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15017
15018   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15019     return FALSE;
15020
15021   if (!player->active || !IN_LEV_FIELD(x, y))
15022     return FALSE;
15023
15024   if (dx && dy)
15025     return FALSE;
15026
15027   if (!dx && !dy)
15028   {
15029     if (player->MovPos == 0)
15030       player->is_pushing = FALSE;
15031
15032     player->is_snapping = FALSE;
15033
15034     if (player->MovPos == 0)
15035     {
15036       player->is_moving = FALSE;
15037       player->is_digging = FALSE;
15038       player->is_collecting = FALSE;
15039     }
15040
15041     return FALSE;
15042   }
15043
15044   // prevent snapping with already pressed snap key when not allowed
15045   if (player->is_snapping && !can_continue_snapping)
15046     return FALSE;
15047
15048   player->MovDir = snap_direction;
15049
15050   if (player->MovPos == 0)
15051   {
15052     player->is_moving = FALSE;
15053     player->is_digging = FALSE;
15054     player->is_collecting = FALSE;
15055   }
15056
15057   player->is_dropping = FALSE;
15058   player->is_dropping_pressed = FALSE;
15059   player->drop_pressed_delay = 0;
15060
15061   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15062     return FALSE;
15063
15064   player->is_snapping = TRUE;
15065   player->is_active = TRUE;
15066
15067   if (player->MovPos == 0)
15068   {
15069     player->is_moving = FALSE;
15070     player->is_digging = FALSE;
15071     player->is_collecting = FALSE;
15072   }
15073
15074   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15075     TEST_DrawLevelField(player->last_jx, player->last_jy);
15076
15077   TEST_DrawLevelField(x, y);
15078
15079   return TRUE;
15080 }
15081
15082 static boolean DropElement(struct PlayerInfo *player)
15083 {
15084   int old_element, new_element;
15085   int dropx = player->jx, dropy = player->jy;
15086   int drop_direction = player->MovDir;
15087   int drop_side = drop_direction;
15088   int drop_element = get_next_dropped_element(player);
15089
15090   /* do not drop an element on top of another element; when holding drop key
15091      pressed without moving, dropped element must move away before the next
15092      element can be dropped (this is especially important if the next element
15093      is dynamite, which can be placed on background for historical reasons) */
15094   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15095     return MP_ACTION;
15096
15097   if (IS_THROWABLE(drop_element))
15098   {
15099     dropx += GET_DX_FROM_DIR(drop_direction);
15100     dropy += GET_DY_FROM_DIR(drop_direction);
15101
15102     if (!IN_LEV_FIELD(dropx, dropy))
15103       return FALSE;
15104   }
15105
15106   old_element = Tile[dropx][dropy];     // old element at dropping position
15107   new_element = drop_element;           // default: no change when dropping
15108
15109   // check if player is active, not moving and ready to drop
15110   if (!player->active || player->MovPos || player->drop_delay > 0)
15111     return FALSE;
15112
15113   // check if player has anything that can be dropped
15114   if (new_element == EL_UNDEFINED)
15115     return FALSE;
15116
15117   // only set if player has anything that can be dropped
15118   player->is_dropping_pressed = TRUE;
15119
15120   // check if drop key was pressed long enough for EM style dynamite
15121   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15122     return FALSE;
15123
15124   // check if anything can be dropped at the current position
15125   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15126     return FALSE;
15127
15128   // collected custom elements can only be dropped on empty fields
15129   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15130     return FALSE;
15131
15132   if (old_element != EL_EMPTY)
15133     Back[dropx][dropy] = old_element;   // store old element on this field
15134
15135   ResetGfxAnimation(dropx, dropy);
15136   ResetRandomAnimationValue(dropx, dropy);
15137
15138   if (player->inventory_size > 0 ||
15139       player->inventory_infinite_element != EL_UNDEFINED)
15140   {
15141     if (player->inventory_size > 0)
15142     {
15143       player->inventory_size--;
15144
15145       DrawGameDoorValues();
15146
15147       if (new_element == EL_DYNAMITE)
15148         new_element = EL_DYNAMITE_ACTIVE;
15149       else if (new_element == EL_EM_DYNAMITE)
15150         new_element = EL_EM_DYNAMITE_ACTIVE;
15151       else if (new_element == EL_SP_DISK_RED)
15152         new_element = EL_SP_DISK_RED_ACTIVE;
15153     }
15154
15155     Tile[dropx][dropy] = new_element;
15156
15157     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15158       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15159                           el2img(Tile[dropx][dropy]), 0);
15160
15161     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15162
15163     // needed if previous element just changed to "empty" in the last frame
15164     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15165
15166     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15167                                player->index_bit, drop_side);
15168     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15169                                         CE_PLAYER_DROPS_X,
15170                                         player->index_bit, drop_side);
15171
15172     TestIfElementTouchesCustomElement(dropx, dropy);
15173   }
15174   else          // player is dropping a dyna bomb
15175   {
15176     player->dynabombs_left--;
15177
15178     Tile[dropx][dropy] = new_element;
15179
15180     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15181       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15182                           el2img(Tile[dropx][dropy]), 0);
15183
15184     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15185   }
15186
15187   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15188     InitField_WithBug1(dropx, dropy, FALSE);
15189
15190   new_element = Tile[dropx][dropy];     // element might have changed
15191
15192   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15193       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15194   {
15195     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15196       MovDir[dropx][dropy] = drop_direction;
15197
15198     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15199
15200     // do not cause impact style collision by dropping elements that can fall
15201     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15202   }
15203
15204   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15205   player->is_dropping = TRUE;
15206
15207   player->drop_pressed_delay = 0;
15208   player->is_dropping_pressed = FALSE;
15209
15210   player->drop_x = dropx;
15211   player->drop_y = dropy;
15212
15213   return TRUE;
15214 }
15215
15216 // ----------------------------------------------------------------------------
15217 // game sound playing functions
15218 // ----------------------------------------------------------------------------
15219
15220 static int *loop_sound_frame = NULL;
15221 static int *loop_sound_volume = NULL;
15222
15223 void InitPlayLevelSound(void)
15224 {
15225   int num_sounds = getSoundListSize();
15226
15227   checked_free(loop_sound_frame);
15228   checked_free(loop_sound_volume);
15229
15230   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15231   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15232 }
15233
15234 static void PlayLevelSound(int x, int y, int nr)
15235 {
15236   int sx = SCREENX(x), sy = SCREENY(y);
15237   int volume, stereo_position;
15238   int max_distance = 8;
15239   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15240
15241   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15242       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15243     return;
15244
15245   if (!IN_LEV_FIELD(x, y) ||
15246       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15247       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15248     return;
15249
15250   volume = SOUND_MAX_VOLUME;
15251
15252   if (!IN_SCR_FIELD(sx, sy))
15253   {
15254     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15255     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15256
15257     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15258   }
15259
15260   stereo_position = (SOUND_MAX_LEFT +
15261                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15262                      (SCR_FIELDX + 2 * max_distance));
15263
15264   if (IS_LOOP_SOUND(nr))
15265   {
15266     /* This assures that quieter loop sounds do not overwrite louder ones,
15267        while restarting sound volume comparison with each new game frame. */
15268
15269     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15270       return;
15271
15272     loop_sound_volume[nr] = volume;
15273     loop_sound_frame[nr] = FrameCounter;
15274   }
15275
15276   PlaySoundExt(nr, volume, stereo_position, type);
15277 }
15278
15279 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15280 {
15281   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15282                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15283                  y < LEVELY(BY1) ? LEVELY(BY1) :
15284                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15285                  sound_action);
15286 }
15287
15288 static void PlayLevelSoundAction(int x, int y, int action)
15289 {
15290   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15291 }
15292
15293 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15294 {
15295   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15296
15297   if (sound_effect != SND_UNDEFINED)
15298     PlayLevelSound(x, y, sound_effect);
15299 }
15300
15301 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15302                                               int action)
15303 {
15304   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15305
15306   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15307     PlayLevelSound(x, y, sound_effect);
15308 }
15309
15310 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15311 {
15312   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15313
15314   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15315     PlayLevelSound(x, y, sound_effect);
15316 }
15317
15318 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15319 {
15320   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15321
15322   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15323     StopSound(sound_effect);
15324 }
15325
15326 static int getLevelMusicNr(void)
15327 {
15328   int level_pos = level_nr - leveldir_current->first_level;
15329
15330   if (levelset.music[level_nr] != MUS_UNDEFINED)
15331     return levelset.music[level_nr];            // from config file
15332   else
15333     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15334 }
15335
15336 static void FadeLevelSounds(void)
15337 {
15338   FadeSounds();
15339 }
15340
15341 static void FadeLevelMusic(void)
15342 {
15343   int music_nr = getLevelMusicNr();
15344   char *curr_music = getCurrentlyPlayingMusicFilename();
15345   char *next_music = getMusicInfoEntryFilename(music_nr);
15346
15347   if (!strEqual(curr_music, next_music))
15348     FadeMusic();
15349 }
15350
15351 void FadeLevelSoundsAndMusic(void)
15352 {
15353   FadeLevelSounds();
15354   FadeLevelMusic();
15355 }
15356
15357 static void PlayLevelMusic(void)
15358 {
15359   int music_nr = getLevelMusicNr();
15360   char *curr_music = getCurrentlyPlayingMusicFilename();
15361   char *next_music = getMusicInfoEntryFilename(music_nr);
15362
15363   if (!strEqual(curr_music, next_music))
15364     PlayMusicLoop(music_nr);
15365 }
15366
15367 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15368 {
15369   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15370   int offset = 0;
15371   int x = xx - offset;
15372   int y = yy - offset;
15373
15374   switch (sample)
15375   {
15376     case SOUND_blank:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15378       break;
15379
15380     case SOUND_roll:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15382       break;
15383
15384     case SOUND_stone:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15386       break;
15387
15388     case SOUND_nut:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15390       break;
15391
15392     case SOUND_crack:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15394       break;
15395
15396     case SOUND_bug:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15398       break;
15399
15400     case SOUND_tank:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15402       break;
15403
15404     case SOUND_android_clone:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15406       break;
15407
15408     case SOUND_android_move:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15410       break;
15411
15412     case SOUND_spring:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15414       break;
15415
15416     case SOUND_slurp:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15418       break;
15419
15420     case SOUND_eater:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15422       break;
15423
15424     case SOUND_eater_eat:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15426       break;
15427
15428     case SOUND_alien:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15430       break;
15431
15432     case SOUND_collect:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15434       break;
15435
15436     case SOUND_diamond:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15438       break;
15439
15440     case SOUND_squash:
15441       // !!! CHECK THIS !!!
15442 #if 1
15443       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15444 #else
15445       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15446 #endif
15447       break;
15448
15449     case SOUND_wonderfall:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15451       break;
15452
15453     case SOUND_drip:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15455       break;
15456
15457     case SOUND_push:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15459       break;
15460
15461     case SOUND_dirt:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15463       break;
15464
15465     case SOUND_acid:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15467       break;
15468
15469     case SOUND_ball:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15471       break;
15472
15473     case SOUND_slide:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15475       break;
15476
15477     case SOUND_wonder:
15478       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15479       break;
15480
15481     case SOUND_door:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15483       break;
15484
15485     case SOUND_exit_open:
15486       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15487       break;
15488
15489     case SOUND_exit_leave:
15490       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15491       break;
15492
15493     case SOUND_dynamite:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15495       break;
15496
15497     case SOUND_tick:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15499       break;
15500
15501     case SOUND_press:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15503       break;
15504
15505     case SOUND_wheel:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15507       break;
15508
15509     case SOUND_boom:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15511       break;
15512
15513     case SOUND_die:
15514       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15515       break;
15516
15517     case SOUND_time:
15518       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15519       break;
15520
15521     default:
15522       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15523       break;
15524   }
15525 }
15526
15527 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15528 {
15529   int element = map_element_SP_to_RND(element_sp);
15530   int action = map_action_SP_to_RND(action_sp);
15531   int offset = (setup.sp_show_border_elements ? 0 : 1);
15532   int x = xx - offset;
15533   int y = yy - offset;
15534
15535   PlayLevelSoundElementAction(x, y, element, action);
15536 }
15537
15538 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15539 {
15540   int element = map_element_MM_to_RND(element_mm);
15541   int action = map_action_MM_to_RND(action_mm);
15542   int offset = 0;
15543   int x = xx - offset;
15544   int y = yy - offset;
15545
15546   if (!IS_MM_ELEMENT(element))
15547     element = EL_MM_DEFAULT;
15548
15549   PlayLevelSoundElementAction(x, y, element, action);
15550 }
15551
15552 void PlaySound_MM(int sound_mm)
15553 {
15554   int sound = map_sound_MM_to_RND(sound_mm);
15555
15556   if (sound == SND_UNDEFINED)
15557     return;
15558
15559   PlaySound(sound);
15560 }
15561
15562 void PlaySoundLoop_MM(int sound_mm)
15563 {
15564   int sound = map_sound_MM_to_RND(sound_mm);
15565
15566   if (sound == SND_UNDEFINED)
15567     return;
15568
15569   PlaySoundLoop(sound);
15570 }
15571
15572 void StopSound_MM(int sound_mm)
15573 {
15574   int sound = map_sound_MM_to_RND(sound_mm);
15575
15576   if (sound == SND_UNDEFINED)
15577     return;
15578
15579   StopSound(sound);
15580 }
15581
15582 void RaiseScore(int value)
15583 {
15584   game.score += value;
15585
15586   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15587
15588   DisplayGameControlValues();
15589 }
15590
15591 void RaiseScoreElement(int element)
15592 {
15593   switch (element)
15594   {
15595     case EL_EMERALD:
15596     case EL_BD_DIAMOND:
15597     case EL_EMERALD_YELLOW:
15598     case EL_EMERALD_RED:
15599     case EL_EMERALD_PURPLE:
15600     case EL_SP_INFOTRON:
15601       RaiseScore(level.score[SC_EMERALD]);
15602       break;
15603     case EL_DIAMOND:
15604       RaiseScore(level.score[SC_DIAMOND]);
15605       break;
15606     case EL_CRYSTAL:
15607       RaiseScore(level.score[SC_CRYSTAL]);
15608       break;
15609     case EL_PEARL:
15610       RaiseScore(level.score[SC_PEARL]);
15611       break;
15612     case EL_BUG:
15613     case EL_BD_BUTTERFLY:
15614     case EL_SP_ELECTRON:
15615       RaiseScore(level.score[SC_BUG]);
15616       break;
15617     case EL_SPACESHIP:
15618     case EL_BD_FIREFLY:
15619     case EL_SP_SNIKSNAK:
15620       RaiseScore(level.score[SC_SPACESHIP]);
15621       break;
15622     case EL_YAMYAM:
15623     case EL_DARK_YAMYAM:
15624       RaiseScore(level.score[SC_YAMYAM]);
15625       break;
15626     case EL_ROBOT:
15627       RaiseScore(level.score[SC_ROBOT]);
15628       break;
15629     case EL_PACMAN:
15630       RaiseScore(level.score[SC_PACMAN]);
15631       break;
15632     case EL_NUT:
15633       RaiseScore(level.score[SC_NUT]);
15634       break;
15635     case EL_DYNAMITE:
15636     case EL_EM_DYNAMITE:
15637     case EL_SP_DISK_RED:
15638     case EL_DYNABOMB_INCREASE_NUMBER:
15639     case EL_DYNABOMB_INCREASE_SIZE:
15640     case EL_DYNABOMB_INCREASE_POWER:
15641       RaiseScore(level.score[SC_DYNAMITE]);
15642       break;
15643     case EL_SHIELD_NORMAL:
15644     case EL_SHIELD_DEADLY:
15645       RaiseScore(level.score[SC_SHIELD]);
15646       break;
15647     case EL_EXTRA_TIME:
15648       RaiseScore(level.extra_time_score);
15649       break;
15650     case EL_KEY_1:
15651     case EL_KEY_2:
15652     case EL_KEY_3:
15653     case EL_KEY_4:
15654     case EL_EM_KEY_1:
15655     case EL_EM_KEY_2:
15656     case EL_EM_KEY_3:
15657     case EL_EM_KEY_4:
15658     case EL_EMC_KEY_5:
15659     case EL_EMC_KEY_6:
15660     case EL_EMC_KEY_7:
15661     case EL_EMC_KEY_8:
15662     case EL_DC_KEY_WHITE:
15663       RaiseScore(level.score[SC_KEY]);
15664       break;
15665     default:
15666       RaiseScore(element_info[element].collect_score);
15667       break;
15668   }
15669 }
15670
15671 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15672 {
15673   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15674   {
15675     if (!quick_quit)
15676     {
15677       // prevent short reactivation of overlay buttons while closing door
15678       SetOverlayActive(FALSE);
15679       UnmapGameButtons();
15680
15681       // door may still be open due to skipped or envelope style request
15682       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15683     }
15684
15685     if (network.enabled)
15686       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15687     else
15688     {
15689       if (quick_quit)
15690         FadeSkipNextFadeIn();
15691
15692       SetGameStatus(GAME_MODE_MAIN);
15693
15694       DrawMainMenu();
15695     }
15696   }
15697   else          // continue playing the game
15698   {
15699     if (tape.playing && tape.deactivate_display)
15700       TapeDeactivateDisplayOff(TRUE);
15701
15702     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15703
15704     if (tape.playing && tape.deactivate_display)
15705       TapeDeactivateDisplayOn();
15706   }
15707 }
15708
15709 void RequestQuitGame(boolean escape_key_pressed)
15710 {
15711   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15712   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15713                         level_editor_test_game);
15714   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15715                           quick_quit || score_info_tape_play);
15716
15717   RequestQuitGameExt(skip_request, quick_quit,
15718                      "Do you really want to quit the game?");
15719 }
15720
15721 static char *getRestartGameMessage(void)
15722 {
15723   boolean play_again = hasStartedNetworkGame();
15724   static char message[MAX_OUTPUT_LINESIZE];
15725   char *game_over_text = "Game over!";
15726   char *play_again_text = " Play it again?";
15727
15728   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15729       game_mm.game_over_message != NULL)
15730     game_over_text = game_mm.game_over_message;
15731
15732   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15733            (play_again ? play_again_text : ""));
15734
15735   return message;
15736 }
15737
15738 static void RequestRestartGame(void)
15739 {
15740   char *message = getRestartGameMessage();
15741   boolean has_started_game = hasStartedNetworkGame();
15742   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15743   int door_state = DOOR_CLOSE_1;
15744
15745   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15746   {
15747     CloseDoor(door_state);
15748
15749     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15750   }
15751   else
15752   {
15753     // if game was invoked from level editor, also close tape recorder door
15754     if (level_editor_test_game)
15755       door_state = DOOR_CLOSE_ALL;
15756
15757     CloseDoor(door_state);
15758
15759     SetGameStatus(GAME_MODE_MAIN);
15760
15761     DrawMainMenu();
15762   }
15763 }
15764
15765 boolean CheckRestartGame(void)
15766 {
15767   static int game_over_delay = 0;
15768   int game_over_delay_value = 50;
15769   boolean game_over = checkGameFailed();
15770
15771   if (!game_over)
15772   {
15773     game_over_delay = game_over_delay_value;
15774
15775     return FALSE;
15776   }
15777
15778   if (game_over_delay > 0)
15779   {
15780     if (game_over_delay == game_over_delay_value / 2)
15781       PlaySound(SND_GAME_LOSING);
15782
15783     game_over_delay--;
15784
15785     return FALSE;
15786   }
15787
15788   // do not handle game over if request dialog is already active
15789   if (game.request_active)
15790     return FALSE;
15791
15792   // do not ask to play again if game was never actually played
15793   if (!game.GamePlayed)
15794     return FALSE;
15795
15796   // do not ask to play again if this was disabled in setup menu
15797   if (!setup.ask_on_game_over)
15798     return FALSE;
15799
15800   RequestRestartGame();
15801
15802   return TRUE;
15803 }
15804
15805 boolean checkGameSolved(void)
15806 {
15807   // set for all game engines if level was solved
15808   return game.LevelSolved_GameEnd;
15809 }
15810
15811 boolean checkGameFailed(void)
15812 {
15813   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15814     return (game_em.game_over && !game_em.level_solved);
15815   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15816     return (game_sp.game_over && !game_sp.level_solved);
15817   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15818     return (game_mm.game_over && !game_mm.level_solved);
15819   else                          // GAME_ENGINE_TYPE_RND
15820     return (game.GameOver && !game.LevelSolved);
15821 }
15822
15823 boolean checkGameEnded(void)
15824 {
15825   return (checkGameSolved() || checkGameFailed());
15826 }
15827
15828
15829 // ----------------------------------------------------------------------------
15830 // random generator functions
15831 // ----------------------------------------------------------------------------
15832
15833 unsigned int InitEngineRandom_RND(int seed)
15834 {
15835   game.num_random_calls = 0;
15836
15837   return InitEngineRandom(seed);
15838 }
15839
15840 unsigned int RND(int max)
15841 {
15842   if (max > 0)
15843   {
15844     game.num_random_calls++;
15845
15846     return GetEngineRandom(max);
15847   }
15848
15849   return 0;
15850 }
15851
15852
15853 // ----------------------------------------------------------------------------
15854 // game engine snapshot handling functions
15855 // ----------------------------------------------------------------------------
15856
15857 struct EngineSnapshotInfo
15858 {
15859   // runtime values for custom element collect score
15860   int collect_score[NUM_CUSTOM_ELEMENTS];
15861
15862   // runtime values for group element choice position
15863   int choice_pos[NUM_GROUP_ELEMENTS];
15864
15865   // runtime values for belt position animations
15866   int belt_graphic[4][NUM_BELT_PARTS];
15867   int belt_anim_mode[4][NUM_BELT_PARTS];
15868 };
15869
15870 static struct EngineSnapshotInfo engine_snapshot_rnd;
15871 static char *snapshot_level_identifier = NULL;
15872 static int snapshot_level_nr = -1;
15873
15874 static void SaveEngineSnapshotValues_RND(void)
15875 {
15876   static int belt_base_active_element[4] =
15877   {
15878     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15879     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15880     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15881     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15882   };
15883   int i, j;
15884
15885   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15886   {
15887     int element = EL_CUSTOM_START + i;
15888
15889     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15890   }
15891
15892   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15893   {
15894     int element = EL_GROUP_START + i;
15895
15896     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15897   }
15898
15899   for (i = 0; i < 4; i++)
15900   {
15901     for (j = 0; j < NUM_BELT_PARTS; j++)
15902     {
15903       int element = belt_base_active_element[i] + j;
15904       int graphic = el2img(element);
15905       int anim_mode = graphic_info[graphic].anim_mode;
15906
15907       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15908       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15909     }
15910   }
15911 }
15912
15913 static void LoadEngineSnapshotValues_RND(void)
15914 {
15915   unsigned int num_random_calls = game.num_random_calls;
15916   int i, j;
15917
15918   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15919   {
15920     int element = EL_CUSTOM_START + i;
15921
15922     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15923   }
15924
15925   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15926   {
15927     int element = EL_GROUP_START + i;
15928
15929     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15930   }
15931
15932   for (i = 0; i < 4; i++)
15933   {
15934     for (j = 0; j < NUM_BELT_PARTS; j++)
15935     {
15936       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15937       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15938
15939       graphic_info[graphic].anim_mode = anim_mode;
15940     }
15941   }
15942
15943   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15944   {
15945     InitRND(tape.random_seed);
15946     for (i = 0; i < num_random_calls; i++)
15947       RND(1);
15948   }
15949
15950   if (game.num_random_calls != num_random_calls)
15951   {
15952     Error("number of random calls out of sync");
15953     Error("number of random calls should be %d", num_random_calls);
15954     Error("number of random calls is %d", game.num_random_calls);
15955
15956     Fail("this should not happen -- please debug");
15957   }
15958 }
15959
15960 void FreeEngineSnapshotSingle(void)
15961 {
15962   FreeSnapshotSingle();
15963
15964   setString(&snapshot_level_identifier, NULL);
15965   snapshot_level_nr = -1;
15966 }
15967
15968 void FreeEngineSnapshotList(void)
15969 {
15970   FreeSnapshotList();
15971 }
15972
15973 static ListNode *SaveEngineSnapshotBuffers(void)
15974 {
15975   ListNode *buffers = NULL;
15976
15977   // copy some special values to a structure better suited for the snapshot
15978
15979   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15980     SaveEngineSnapshotValues_RND();
15981   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15982     SaveEngineSnapshotValues_EM();
15983   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15984     SaveEngineSnapshotValues_SP(&buffers);
15985   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15986     SaveEngineSnapshotValues_MM();
15987
15988   // save values stored in special snapshot structure
15989
15990   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15991     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15992   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15993     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15994   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15995     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15996   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15997     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15998
15999   // save further RND engine values
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16004
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16010
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16014
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16016
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16019
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16038
16039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16041
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16044   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16045
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16048
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16050   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16051   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16053   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16055
16056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16057   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16058
16059 #if 0
16060   ListNode *node = engine_snapshot_list_rnd;
16061   int num_bytes = 0;
16062
16063   while (node != NULL)
16064   {
16065     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16066
16067     node = node->next;
16068   }
16069
16070   Debug("game:playing:SaveEngineSnapshotBuffers",
16071         "size of engine snapshot: %d bytes", num_bytes);
16072 #endif
16073
16074   return buffers;
16075 }
16076
16077 void SaveEngineSnapshotSingle(void)
16078 {
16079   ListNode *buffers = SaveEngineSnapshotBuffers();
16080
16081   // finally save all snapshot buffers to single snapshot
16082   SaveSnapshotSingle(buffers);
16083
16084   // save level identification information
16085   setString(&snapshot_level_identifier, leveldir_current->identifier);
16086   snapshot_level_nr = level_nr;
16087 }
16088
16089 boolean CheckSaveEngineSnapshotToList(void)
16090 {
16091   boolean save_snapshot =
16092     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16093      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16094       game.snapshot.changed_action) ||
16095      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16096       game.snapshot.collected_item));
16097
16098   game.snapshot.changed_action = FALSE;
16099   game.snapshot.collected_item = FALSE;
16100   game.snapshot.save_snapshot = save_snapshot;
16101
16102   return save_snapshot;
16103 }
16104
16105 void SaveEngineSnapshotToList(void)
16106 {
16107   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16108       tape.quick_resume)
16109     return;
16110
16111   ListNode *buffers = SaveEngineSnapshotBuffers();
16112
16113   // finally save all snapshot buffers to snapshot list
16114   SaveSnapshotToList(buffers);
16115 }
16116
16117 void SaveEngineSnapshotToListInitial(void)
16118 {
16119   FreeEngineSnapshotList();
16120
16121   SaveEngineSnapshotToList();
16122 }
16123
16124 static void LoadEngineSnapshotValues(void)
16125 {
16126   // restore special values from snapshot structure
16127
16128   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16129     LoadEngineSnapshotValues_RND();
16130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16131     LoadEngineSnapshotValues_EM();
16132   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16133     LoadEngineSnapshotValues_SP();
16134   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16135     LoadEngineSnapshotValues_MM();
16136 }
16137
16138 void LoadEngineSnapshotSingle(void)
16139 {
16140   LoadSnapshotSingle();
16141
16142   LoadEngineSnapshotValues();
16143 }
16144
16145 static void LoadEngineSnapshot_Undo(int steps)
16146 {
16147   LoadSnapshotFromList_Older(steps);
16148
16149   LoadEngineSnapshotValues();
16150 }
16151
16152 static void LoadEngineSnapshot_Redo(int steps)
16153 {
16154   LoadSnapshotFromList_Newer(steps);
16155
16156   LoadEngineSnapshotValues();
16157 }
16158
16159 boolean CheckEngineSnapshotSingle(void)
16160 {
16161   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16162           snapshot_level_nr == level_nr);
16163 }
16164
16165 boolean CheckEngineSnapshotList(void)
16166 {
16167   return CheckSnapshotList();
16168 }
16169
16170
16171 // ---------- new game button stuff -------------------------------------------
16172
16173 static struct
16174 {
16175   int graphic;
16176   struct XY *pos;
16177   int gadget_id;
16178   boolean *setup_value;
16179   boolean allowed_on_tape;
16180   boolean is_touch_button;
16181   char *infotext;
16182 } gamebutton_info[NUM_GAME_BUTTONS] =
16183 {
16184   {
16185     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16186     GAME_CTRL_ID_STOP,                          NULL,
16187     TRUE, FALSE,                                "stop game"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16191     GAME_CTRL_ID_PAUSE,                         NULL,
16192     TRUE, FALSE,                                "pause game"
16193   },
16194   {
16195     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16196     GAME_CTRL_ID_PLAY,                          NULL,
16197     TRUE, FALSE,                                "play game"
16198   },
16199   {
16200     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16201     GAME_CTRL_ID_UNDO,                          NULL,
16202     TRUE, FALSE,                                "undo step"
16203   },
16204   {
16205     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16206     GAME_CTRL_ID_REDO,                          NULL,
16207     TRUE, FALSE,                                "redo step"
16208   },
16209   {
16210     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16211     GAME_CTRL_ID_SAVE,                          NULL,
16212     TRUE, FALSE,                                "save game"
16213   },
16214   {
16215     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16216     GAME_CTRL_ID_PAUSE2,                        NULL,
16217     TRUE, FALSE,                                "pause game"
16218   },
16219   {
16220     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16221     GAME_CTRL_ID_LOAD,                          NULL,
16222     TRUE, FALSE,                                "load game"
16223   },
16224   {
16225     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16226     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16227     FALSE, FALSE,                               "stop game"
16228   },
16229   {
16230     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16231     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16232     FALSE, FALSE,                               "pause game"
16233   },
16234   {
16235     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16236     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16237     FALSE, FALSE,                               "play game"
16238   },
16239   {
16240     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16241     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16242     FALSE, TRUE,                                "stop game"
16243   },
16244   {
16245     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16246     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16247     FALSE, TRUE,                                "pause game"
16248   },
16249   {
16250     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16251     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16252     TRUE, FALSE,                                "background music on/off"
16253   },
16254   {
16255     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16256     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16257     TRUE, FALSE,                                "sound loops on/off"
16258   },
16259   {
16260     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16261     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16262     TRUE, FALSE,                                "normal sounds on/off"
16263   },
16264   {
16265     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16266     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16267     FALSE, FALSE,                               "background music on/off"
16268   },
16269   {
16270     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16271     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16272     FALSE, FALSE,                               "sound loops on/off"
16273   },
16274   {
16275     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16276     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16277     FALSE, FALSE,                               "normal sounds on/off"
16278   }
16279 };
16280
16281 void CreateGameButtons(void)
16282 {
16283   int i;
16284
16285   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16286   {
16287     int graphic = gamebutton_info[i].graphic;
16288     struct GraphicInfo *gfx = &graphic_info[graphic];
16289     struct XY *pos = gamebutton_info[i].pos;
16290     struct GadgetInfo *gi;
16291     int button_type;
16292     boolean checked;
16293     unsigned int event_mask;
16294     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16295     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16296     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16297     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16298     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16299     int gd_x   = gfx->src_x;
16300     int gd_y   = gfx->src_y;
16301     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16302     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16303     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16304     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16305     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16306     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16307     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16308     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16309     int id = i;
16310
16311     // do not use touch buttons if overlay touch buttons are disabled
16312     if (is_touch_button && !setup.touch.overlay_buttons)
16313       continue;
16314
16315     if (gfx->bitmap == NULL)
16316     {
16317       game_gadget[id] = NULL;
16318
16319       continue;
16320     }
16321
16322     if (id == GAME_CTRL_ID_STOP ||
16323         id == GAME_CTRL_ID_PANEL_STOP ||
16324         id == GAME_CTRL_ID_TOUCH_STOP ||
16325         id == GAME_CTRL_ID_PLAY ||
16326         id == GAME_CTRL_ID_PANEL_PLAY ||
16327         id == GAME_CTRL_ID_SAVE ||
16328         id == GAME_CTRL_ID_LOAD)
16329     {
16330       button_type = GD_TYPE_NORMAL_BUTTON;
16331       checked = FALSE;
16332       event_mask = GD_EVENT_RELEASED;
16333     }
16334     else if (id == GAME_CTRL_ID_UNDO ||
16335              id == GAME_CTRL_ID_REDO)
16336     {
16337       button_type = GD_TYPE_NORMAL_BUTTON;
16338       checked = FALSE;
16339       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16340     }
16341     else
16342     {
16343       button_type = GD_TYPE_CHECK_BUTTON;
16344       checked = (gamebutton_info[i].setup_value != NULL ?
16345                  *gamebutton_info[i].setup_value : FALSE);
16346       event_mask = GD_EVENT_PRESSED;
16347     }
16348
16349     gi = CreateGadget(GDI_CUSTOM_ID, id,
16350                       GDI_IMAGE_ID, graphic,
16351                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16352                       GDI_X, base_x + x,
16353                       GDI_Y, base_y + y,
16354                       GDI_WIDTH, gfx->width,
16355                       GDI_HEIGHT, gfx->height,
16356                       GDI_TYPE, button_type,
16357                       GDI_STATE, GD_BUTTON_UNPRESSED,
16358                       GDI_CHECKED, checked,
16359                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16360                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16361                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16362                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16363                       GDI_DIRECT_DRAW, FALSE,
16364                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16365                       GDI_EVENT_MASK, event_mask,
16366                       GDI_CALLBACK_ACTION, HandleGameButtons,
16367                       GDI_END);
16368
16369     if (gi == NULL)
16370       Fail("cannot create gadget");
16371
16372     game_gadget[id] = gi;
16373   }
16374 }
16375
16376 void FreeGameButtons(void)
16377 {
16378   int i;
16379
16380   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16381     FreeGadget(game_gadget[i]);
16382 }
16383
16384 static void UnmapGameButtonsAtSamePosition(int id)
16385 {
16386   int i;
16387
16388   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16389     if (i != id &&
16390         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16391         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16392       UnmapGadget(game_gadget[i]);
16393 }
16394
16395 static void UnmapGameButtonsAtSamePosition_All(void)
16396 {
16397   if (setup.show_load_save_buttons)
16398   {
16399     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16400     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16401     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16402   }
16403   else if (setup.show_undo_redo_buttons)
16404   {
16405     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16406     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16407     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16408   }
16409   else
16410   {
16411     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16412     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16413     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16414
16415     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16416     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16417     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16418   }
16419 }
16420
16421 void MapLoadSaveButtons(void)
16422 {
16423   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16424   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16425
16426   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16427   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16428 }
16429
16430 void MapUndoRedoButtons(void)
16431 {
16432   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16433   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16434
16435   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16436   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16437 }
16438
16439 void ModifyPauseButtons(void)
16440 {
16441   static int ids[] =
16442   {
16443     GAME_CTRL_ID_PAUSE,
16444     GAME_CTRL_ID_PAUSE2,
16445     GAME_CTRL_ID_PANEL_PAUSE,
16446     GAME_CTRL_ID_TOUCH_PAUSE,
16447     -1
16448   };
16449   int i;
16450
16451   for (i = 0; ids[i] > -1; i++)
16452     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16453 }
16454
16455 static void MapGameButtonsExt(boolean on_tape)
16456 {
16457   int i;
16458
16459   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16460   {
16461     if ((i == GAME_CTRL_ID_UNDO ||
16462          i == GAME_CTRL_ID_REDO) &&
16463         game_status != GAME_MODE_PLAYING)
16464       continue;
16465
16466     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16467       MapGadget(game_gadget[i]);
16468   }
16469
16470   UnmapGameButtonsAtSamePosition_All();
16471
16472   RedrawGameButtons();
16473 }
16474
16475 static void UnmapGameButtonsExt(boolean on_tape)
16476 {
16477   int i;
16478
16479   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16480     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16481       UnmapGadget(game_gadget[i]);
16482 }
16483
16484 static void RedrawGameButtonsExt(boolean on_tape)
16485 {
16486   int i;
16487
16488   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16489     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16490       RedrawGadget(game_gadget[i]);
16491 }
16492
16493 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16494 {
16495   if (gi == NULL)
16496     return;
16497
16498   gi->checked = state;
16499 }
16500
16501 static void RedrawSoundButtonGadget(int id)
16502 {
16503   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16504              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16505              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16506              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16507              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16508              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16509              id);
16510
16511   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16512   RedrawGadget(game_gadget[id2]);
16513 }
16514
16515 void MapGameButtons(void)
16516 {
16517   MapGameButtonsExt(FALSE);
16518 }
16519
16520 void UnmapGameButtons(void)
16521 {
16522   UnmapGameButtonsExt(FALSE);
16523 }
16524
16525 void RedrawGameButtons(void)
16526 {
16527   RedrawGameButtonsExt(FALSE);
16528 }
16529
16530 void MapGameButtonsOnTape(void)
16531 {
16532   MapGameButtonsExt(TRUE);
16533 }
16534
16535 void UnmapGameButtonsOnTape(void)
16536 {
16537   UnmapGameButtonsExt(TRUE);
16538 }
16539
16540 void RedrawGameButtonsOnTape(void)
16541 {
16542   RedrawGameButtonsExt(TRUE);
16543 }
16544
16545 static void GameUndoRedoExt(void)
16546 {
16547   ClearPlayerAction();
16548
16549   tape.pausing = TRUE;
16550
16551   RedrawPlayfield();
16552   UpdateAndDisplayGameControlValues();
16553
16554   DrawCompleteVideoDisplay();
16555   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16556   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16557   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16558
16559   ModifyPauseButtons();
16560
16561   BackToFront();
16562 }
16563
16564 static void GameUndo(int steps)
16565 {
16566   if (!CheckEngineSnapshotList())
16567     return;
16568
16569   int tape_property_bits = tape.property_bits;
16570
16571   LoadEngineSnapshot_Undo(steps);
16572
16573   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16574
16575   GameUndoRedoExt();
16576 }
16577
16578 static void GameRedo(int steps)
16579 {
16580   if (!CheckEngineSnapshotList())
16581     return;
16582
16583   int tape_property_bits = tape.property_bits;
16584
16585   LoadEngineSnapshot_Redo(steps);
16586
16587   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16588
16589   GameUndoRedoExt();
16590 }
16591
16592 static void HandleGameButtonsExt(int id, int button)
16593 {
16594   static boolean game_undo_executed = FALSE;
16595   int steps = BUTTON_STEPSIZE(button);
16596   boolean handle_game_buttons =
16597     (game_status == GAME_MODE_PLAYING ||
16598      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16599
16600   if (!handle_game_buttons)
16601     return;
16602
16603   switch (id)
16604   {
16605     case GAME_CTRL_ID_STOP:
16606     case GAME_CTRL_ID_PANEL_STOP:
16607     case GAME_CTRL_ID_TOUCH_STOP:
16608       TapeStopGame();
16609
16610       break;
16611
16612     case GAME_CTRL_ID_PAUSE:
16613     case GAME_CTRL_ID_PAUSE2:
16614     case GAME_CTRL_ID_PANEL_PAUSE:
16615     case GAME_CTRL_ID_TOUCH_PAUSE:
16616       if (network.enabled && game_status == GAME_MODE_PLAYING)
16617       {
16618         if (tape.pausing)
16619           SendToServer_ContinuePlaying();
16620         else
16621           SendToServer_PausePlaying();
16622       }
16623       else
16624         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16625
16626       game_undo_executed = FALSE;
16627
16628       break;
16629
16630     case GAME_CTRL_ID_PLAY:
16631     case GAME_CTRL_ID_PANEL_PLAY:
16632       if (game_status == GAME_MODE_MAIN)
16633       {
16634         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16635       }
16636       else if (tape.pausing)
16637       {
16638         if (network.enabled)
16639           SendToServer_ContinuePlaying();
16640         else
16641           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16642       }
16643       break;
16644
16645     case GAME_CTRL_ID_UNDO:
16646       // Important: When using "save snapshot when collecting an item" mode,
16647       // load last (current) snapshot for first "undo" after pressing "pause"
16648       // (else the last-but-one snapshot would be loaded, because the snapshot
16649       // pointer already points to the last snapshot when pressing "pause",
16650       // which is fine for "every step/move" mode, but not for "every collect")
16651       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16652           !game_undo_executed)
16653         steps--;
16654
16655       game_undo_executed = TRUE;
16656
16657       GameUndo(steps);
16658       break;
16659
16660     case GAME_CTRL_ID_REDO:
16661       GameRedo(steps);
16662       break;
16663
16664     case GAME_CTRL_ID_SAVE:
16665       TapeQuickSave();
16666       break;
16667
16668     case GAME_CTRL_ID_LOAD:
16669       TapeQuickLoad();
16670       break;
16671
16672     case SOUND_CTRL_ID_MUSIC:
16673     case SOUND_CTRL_ID_PANEL_MUSIC:
16674       if (setup.sound_music)
16675       { 
16676         setup.sound_music = FALSE;
16677
16678         FadeMusic();
16679       }
16680       else if (audio.music_available)
16681       { 
16682         setup.sound = setup.sound_music = TRUE;
16683
16684         SetAudioMode(setup.sound);
16685
16686         if (game_status == GAME_MODE_PLAYING)
16687           PlayLevelMusic();
16688       }
16689
16690       RedrawSoundButtonGadget(id);
16691
16692       break;
16693
16694     case SOUND_CTRL_ID_LOOPS:
16695     case SOUND_CTRL_ID_PANEL_LOOPS:
16696       if (setup.sound_loops)
16697         setup.sound_loops = FALSE;
16698       else if (audio.loops_available)
16699       {
16700         setup.sound = setup.sound_loops = TRUE;
16701
16702         SetAudioMode(setup.sound);
16703       }
16704
16705       RedrawSoundButtonGadget(id);
16706
16707       break;
16708
16709     case SOUND_CTRL_ID_SIMPLE:
16710     case SOUND_CTRL_ID_PANEL_SIMPLE:
16711       if (setup.sound_simple)
16712         setup.sound_simple = FALSE;
16713       else if (audio.sound_available)
16714       {
16715         setup.sound = setup.sound_simple = TRUE;
16716
16717         SetAudioMode(setup.sound);
16718       }
16719
16720       RedrawSoundButtonGadget(id);
16721
16722       break;
16723
16724     default:
16725       break;
16726   }
16727 }
16728
16729 static void HandleGameButtons(struct GadgetInfo *gi)
16730 {
16731   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16732 }
16733
16734 void HandleSoundButtonKeys(Key key)
16735 {
16736   if (key == setup.shortcut.sound_simple)
16737     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16738   else if (key == setup.shortcut.sound_loops)
16739     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16740   else if (key == setup.shortcut.sound_music)
16741     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16742 }