aff7972a58dc70b9f1e6ed620c77e2b6cacd2c48
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_INVENTORY_COUNT ||
2685           nr == GAME_PANEL_SCORE ||
2686           nr == GAME_PANEL_HIGHSCORE ||
2687           nr == GAME_PANEL_TIME)
2688       {
2689         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2690
2691         if (use_dynamic_size)           // use dynamic number of digits
2692         {
2693           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2694                               nr == GAME_PANEL_INVENTORY_COUNT ||
2695                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2696           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2697                           nr == GAME_PANEL_INVENTORY_COUNT ||
2698                           nr == GAME_PANEL_TIME ? 1 : 2);
2699           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2700                        nr == GAME_PANEL_INVENTORY_COUNT ||
2701                        nr == GAME_PANEL_TIME ? 3 : 5);
2702           int size2 = size1 + size_add;
2703           int font1 = pos->font;
2704           int font2 = pos->font_alt;
2705
2706           size = (value < value_change ? size1 : size2);
2707           font = (value < value_change ? font1 : font2);
2708         }
2709       }
2710
2711       // correct text size if "digits" is zero or less
2712       if (size <= 0)
2713         size = strlen(int2str(value, size));
2714
2715       // dynamically correct text alignment
2716       pos->width = size * getFontWidth(font);
2717
2718       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2719                   int2str(value, size), font, mask_mode);
2720     }
2721     else if (type == TYPE_ELEMENT)
2722     {
2723       int element, graphic;
2724       Bitmap *src_bitmap;
2725       int src_x, src_y;
2726       int width, height;
2727       int dst_x = PANEL_XPOS(pos);
2728       int dst_y = PANEL_YPOS(pos);
2729
2730       if (value != EL_UNDEFINED && value != EL_EMPTY)
2731       {
2732         element = value;
2733         graphic = el2panelimg(value);
2734
2735 #if 0
2736         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2737               element, EL_NAME(element), size);
2738 #endif
2739
2740         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2741           size = TILESIZE;
2742
2743         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2744                               &src_x, &src_y);
2745
2746         width  = graphic_info[graphic].width  * size / TILESIZE;
2747         height = graphic_info[graphic].height * size / TILESIZE;
2748
2749         if (draw_masked)
2750           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2751                            dst_x, dst_y);
2752         else
2753           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2754                      dst_x, dst_y);
2755       }
2756     }
2757     else if (type == TYPE_GRAPHIC)
2758     {
2759       int graphic        = gpc->graphic;
2760       int graphic_active = gpc->graphic_active;
2761       Bitmap *src_bitmap;
2762       int src_x, src_y;
2763       int width, height;
2764       int dst_x = PANEL_XPOS(pos);
2765       int dst_y = PANEL_YPOS(pos);
2766       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2767                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2768
2769       if (graphic != IMG_UNDEFINED && !skip)
2770       {
2771         if (pos->style == STYLE_REVERSE)
2772           value = 100 - value;
2773
2774         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2775
2776         if (pos->direction & MV_HORIZONTAL)
2777         {
2778           width  = graphic_info[graphic_active].width * value / 100;
2779           height = graphic_info[graphic_active].height;
2780
2781           if (pos->direction == MV_LEFT)
2782           {
2783             src_x += graphic_info[graphic_active].width - width;
2784             dst_x += graphic_info[graphic_active].width - width;
2785           }
2786         }
2787         else
2788         {
2789           width  = graphic_info[graphic_active].width;
2790           height = graphic_info[graphic_active].height * value / 100;
2791
2792           if (pos->direction == MV_UP)
2793           {
2794             src_y += graphic_info[graphic_active].height - height;
2795             dst_y += graphic_info[graphic_active].height - height;
2796           }
2797         }
2798
2799         if (draw_masked)
2800           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2801                            dst_x, dst_y);
2802         else
2803           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2804                      dst_x, dst_y);
2805
2806         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2807
2808         if (pos->direction & MV_HORIZONTAL)
2809         {
2810           if (pos->direction == MV_RIGHT)
2811           {
2812             src_x += width;
2813             dst_x += width;
2814           }
2815           else
2816           {
2817             dst_x = PANEL_XPOS(pos);
2818           }
2819
2820           width = graphic_info[graphic].width - width;
2821         }
2822         else
2823         {
2824           if (pos->direction == MV_DOWN)
2825           {
2826             src_y += height;
2827             dst_y += height;
2828           }
2829           else
2830           {
2831             dst_y = PANEL_YPOS(pos);
2832           }
2833
2834           height = graphic_info[graphic].height - height;
2835         }
2836
2837         if (draw_masked)
2838           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2839                            dst_x, dst_y);
2840         else
2841           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2842                      dst_x, dst_y);
2843       }
2844     }
2845     else if (type == TYPE_STRING)
2846     {
2847       boolean active = (value != 0);
2848       char *state_normal = "off";
2849       char *state_active = "on";
2850       char *state = (active ? state_active : state_normal);
2851       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2852                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2853                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2854                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2855
2856       if (nr == GAME_PANEL_GRAVITY_STATE)
2857       {
2858         int font1 = pos->font;          // (used for normal state)
2859         int font2 = pos->font_alt;      // (used for active state)
2860
2861         font = (active ? font2 : font1);
2862       }
2863
2864       if (s != NULL)
2865       {
2866         char *s_cut;
2867
2868         if (size <= 0)
2869         {
2870           // don't truncate output if "chars" is zero or less
2871           size = strlen(s);
2872
2873           // dynamically correct text alignment
2874           pos->width = size * getFontWidth(font);
2875         }
2876
2877         s_cut = getStringCopyN(s, size);
2878
2879         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2880                     s_cut, font, mask_mode);
2881
2882         free(s_cut);
2883       }
2884     }
2885
2886     redraw_mask |= REDRAW_DOOR_1;
2887   }
2888
2889   SetGameStatus(GAME_MODE_PLAYING);
2890 }
2891
2892 void UpdateAndDisplayGameControlValues(void)
2893 {
2894   if (tape.deactivate_display)
2895     return;
2896
2897   UpdateGameControlValues();
2898   DisplayGameControlValues();
2899 }
2900
2901 void UpdateGameDoorValues(void)
2902 {
2903   UpdateGameControlValues();
2904 }
2905
2906 void DrawGameDoorValues(void)
2907 {
2908   DisplayGameControlValues();
2909 }
2910
2911
2912 // ============================================================================
2913 // InitGameEngine()
2914 // ----------------------------------------------------------------------------
2915 // initialize game engine due to level / tape version number
2916 // ============================================================================
2917
2918 static void InitGameEngine(void)
2919 {
2920   int i, j, k, l, x, y;
2921
2922   // set game engine from tape file when re-playing, else from level file
2923   game.engine_version = (tape.playing ? tape.engine_version :
2924                          level.game_version);
2925
2926   // set single or multi-player game mode (needed for re-playing tapes)
2927   game.team_mode = setup.team_mode;
2928
2929   if (tape.playing)
2930   {
2931     int num_players = 0;
2932
2933     for (i = 0; i < MAX_PLAYERS; i++)
2934       if (tape.player_participates[i])
2935         num_players++;
2936
2937     // multi-player tapes contain input data for more than one player
2938     game.team_mode = (num_players > 1);
2939   }
2940
2941 #if 0
2942   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2943         level.game_version);
2944   Debug("game:init:level", "          tape.file_version   == %06d",
2945         tape.file_version);
2946   Debug("game:init:level", "          tape.game_version   == %06d",
2947         tape.game_version);
2948   Debug("game:init:level", "          tape.engine_version == %06d",
2949         tape.engine_version);
2950   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2951         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2952 #endif
2953
2954   // --------------------------------------------------------------------------
2955   // set flags for bugs and changes according to active game engine version
2956   // --------------------------------------------------------------------------
2957
2958   /*
2959     Summary of bugfix:
2960     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2961
2962     Bug was introduced in version:
2963     2.0.1
2964
2965     Bug was fixed in version:
2966     4.2.0.0
2967
2968     Description:
2969     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2970     but the property "can fall" was missing, which caused some levels to be
2971     unsolvable. This was fixed in version 4.2.0.0.
2972
2973     Affected levels/tapes:
2974     An example for a tape that was fixed by this bugfix is tape 029 from the
2975     level set "rnd_sam_bateman".
2976     The wrong behaviour will still be used for all levels or tapes that were
2977     created/recorded with it. An example for this is tape 023 from the level
2978     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2979   */
2980
2981   boolean use_amoeba_dropping_cannot_fall_bug =
2982     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2983       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2984      (tape.playing &&
2985       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2986       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2987
2988   /*
2989     Summary of bugfix/change:
2990     Fixed move speed of elements entering or leaving magic wall.
2991
2992     Fixed/changed in version:
2993     2.0.1
2994
2995     Description:
2996     Before 2.0.1, move speed of elements entering or leaving magic wall was
2997     twice as fast as it is now.
2998     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2999
3000     Affected levels/tapes:
3001     The first condition is generally needed for all levels/tapes before version
3002     2.0.1, which might use the old behaviour before it was changed; known tapes
3003     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3004     The second condition is an exception from the above case and is needed for
3005     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3006     above, but before it was known that this change would break tapes like the
3007     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3008     although the engine version while recording maybe was before 2.0.1. There
3009     are a lot of tapes that are affected by this exception, like tape 006 from
3010     the level set "rnd_conor_mancone".
3011   */
3012
3013   boolean use_old_move_stepsize_for_magic_wall =
3014     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3015      !(tape.playing &&
3016        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3017        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3018
3019   /*
3020     Summary of bugfix/change:
3021     Fixed handling for custom elements that change when pushed by the player.
3022
3023     Fixed/changed in version:
3024     3.1.0
3025
3026     Description:
3027     Before 3.1.0, custom elements that "change when pushing" changed directly
3028     after the player started pushing them (until then handled in "DigField()").
3029     Since 3.1.0, these custom elements are not changed until the "pushing"
3030     move of the element is finished (now handled in "ContinueMoving()").
3031
3032     Affected levels/tapes:
3033     The first condition is generally needed for all levels/tapes before version
3034     3.1.0, which might use the old behaviour before it was changed; known tapes
3035     that are affected are some tapes from the level set "Walpurgis Gardens" by
3036     Jamie Cullen.
3037     The second condition is an exception from the above case and is needed for
3038     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3039     above (including some development versions of 3.1.0), but before it was
3040     known that this change would break tapes like the above and was fixed in
3041     3.1.1, so that the changed behaviour was active although the engine version
3042     while recording maybe was before 3.1.0. There is at least one tape that is
3043     affected by this exception, which is the tape for the one-level set "Bug
3044     Machine" by Juergen Bonhagen.
3045   */
3046
3047   game.use_change_when_pushing_bug =
3048     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3049      !(tape.playing &&
3050        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3051        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for blocking the field the player leaves when moving.
3056
3057     Fixed/changed in version:
3058     3.1.1
3059
3060     Description:
3061     Before 3.1.1, when "block last field when moving" was enabled, the field
3062     the player is leaving when moving was blocked for the time of the move,
3063     and was directly unblocked afterwards. This resulted in the last field
3064     being blocked for exactly one less than the number of frames of one player
3065     move. Additionally, even when blocking was disabled, the last field was
3066     blocked for exactly one frame.
3067     Since 3.1.1, due to changes in player movement handling, the last field
3068     is not blocked at all when blocking is disabled. When blocking is enabled,
3069     the last field is blocked for exactly the number of frames of one player
3070     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3071     last field is blocked for exactly one more than the number of frames of
3072     one player move.
3073
3074     Affected levels/tapes:
3075     (!!! yet to be determined -- probably many !!!)
3076   */
3077
3078   game.use_block_last_field_bug =
3079     (game.engine_version < VERSION_IDENT(3,1,1,0));
3080
3081   /* various special flags and settings for native Emerald Mine game engine */
3082
3083   game_em.use_single_button =
3084     (game.engine_version > VERSION_IDENT(4,0,0,2));
3085
3086   game_em.use_snap_key_bug =
3087     (game.engine_version < VERSION_IDENT(4,0,1,0));
3088
3089   game_em.use_random_bug =
3090     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3091
3092   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3093
3094   game_em.use_old_explosions            = use_old_em_engine;
3095   game_em.use_old_android               = use_old_em_engine;
3096   game_em.use_old_push_elements         = use_old_em_engine;
3097   game_em.use_old_push_into_acid        = use_old_em_engine;
3098
3099   game_em.use_wrap_around               = !use_old_em_engine;
3100
3101   // --------------------------------------------------------------------------
3102
3103   // set maximal allowed number of custom element changes per game frame
3104   game.max_num_changes_per_frame = 1;
3105
3106   // default scan direction: scan playfield from top/left to bottom/right
3107   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3108
3109   // dynamically adjust element properties according to game engine version
3110   InitElementPropertiesEngine(game.engine_version);
3111
3112   // ---------- initialize special element properties -------------------------
3113
3114   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3115   if (use_amoeba_dropping_cannot_fall_bug)
3116     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3117
3118   // ---------- initialize player's initial move delay ------------------------
3119
3120   // dynamically adjust player properties according to level information
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     game.initial_move_delay_value[i] =
3123       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3124
3125   // dynamically adjust player properties according to game engine version
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     game.initial_move_delay[i] =
3128       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3129        game.initial_move_delay_value[i] : 0);
3130
3131   // ---------- initialize player's initial push delay ------------------------
3132
3133   // dynamically adjust player properties according to game engine version
3134   game.initial_push_delay_value =
3135     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3136
3137   // ---------- initialize changing elements ----------------------------------
3138
3139   // initialize changing elements information
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141   {
3142     struct ElementInfo *ei = &element_info[i];
3143
3144     // this pointer might have been changed in the level editor
3145     ei->change = &ei->change_page[0];
3146
3147     if (!IS_CUSTOM_ELEMENT(i))
3148     {
3149       ei->change->target_element = EL_EMPTY_SPACE;
3150       ei->change->delay_fixed = 0;
3151       ei->change->delay_random = 0;
3152       ei->change->delay_frames = 1;
3153     }
3154
3155     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3156     {
3157       ei->has_change_event[j] = FALSE;
3158
3159       ei->event_page_nr[j] = 0;
3160       ei->event_page[j] = &ei->change_page[0];
3161     }
3162   }
3163
3164   // add changing elements from pre-defined list
3165   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3166   {
3167     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3168     struct ElementInfo *ei = &element_info[ch_delay->element];
3169
3170     ei->change->target_element       = ch_delay->target_element;
3171     ei->change->delay_fixed          = ch_delay->change_delay;
3172
3173     ei->change->pre_change_function  = ch_delay->pre_change_function;
3174     ei->change->change_function      = ch_delay->change_function;
3175     ei->change->post_change_function = ch_delay->post_change_function;
3176
3177     ei->change->can_change = TRUE;
3178     ei->change->can_change_or_has_action = TRUE;
3179
3180     ei->has_change_event[CE_DELAY] = TRUE;
3181
3182     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3183     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3184   }
3185
3186   // ---------- initialize internal run-time variables ------------------------
3187
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       ei->change_page[j].can_change_or_has_action =
3195         (ei->change_page[j].can_change |
3196          ei->change_page[j].has_action);
3197     }
3198   }
3199
3200   // add change events from custom element configuration
3201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3202   {
3203     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3204
3205     for (j = 0; j < ei->num_change_pages; j++)
3206     {
3207       if (!ei->change_page[j].can_change_or_has_action)
3208         continue;
3209
3210       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3211       {
3212         // only add event page for the first page found with this event
3213         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3214         {
3215           ei->has_change_event[k] = TRUE;
3216
3217           ei->event_page_nr[k] = j;
3218           ei->event_page[k] = &ei->change_page[j];
3219         }
3220       }
3221     }
3222   }
3223
3224   // ---------- initialize reference elements in change conditions ------------
3225
3226   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3227   {
3228     int element = EL_CUSTOM_START + i;
3229     struct ElementInfo *ei = &element_info[element];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       int trigger_element = ei->change_page[j].initial_trigger_element;
3234
3235       if (trigger_element >= EL_PREV_CE_8 &&
3236           trigger_element <= EL_NEXT_CE_8)
3237         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3238
3239       ei->change_page[j].trigger_element = trigger_element;
3240     }
3241   }
3242
3243   // ---------- initialize run-time trigger player and element ----------------
3244
3245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246   {
3247     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248
3249     for (j = 0; j < ei->num_change_pages; j++)
3250     {
3251       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3252       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3253       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3254       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3255       ei->change_page[j].actual_trigger_ce_value = 0;
3256       ei->change_page[j].actual_trigger_ce_score = 0;
3257     }
3258   }
3259
3260   // ---------- initialize trigger events -------------------------------------
3261
3262   // initialize trigger events information
3263   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3264     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3265       trigger_events[i][j] = FALSE;
3266
3267   // add trigger events from element change event properties
3268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       if (!ei->change_page[j].can_change_or_has_action)
3275         continue;
3276
3277       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3278       {
3279         int trigger_element = ei->change_page[j].trigger_element;
3280
3281         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3282         {
3283           if (ei->change_page[j].has_event[k])
3284           {
3285             if (IS_GROUP_ELEMENT(trigger_element))
3286             {
3287               struct ElementGroupInfo *group =
3288                 element_info[trigger_element].group;
3289
3290               for (l = 0; l < group->num_elements_resolved; l++)
3291                 trigger_events[group->element_resolved[l]][k] = TRUE;
3292             }
3293             else if (trigger_element == EL_ANY_ELEMENT)
3294               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3295                 trigger_events[l][k] = TRUE;
3296             else
3297               trigger_events[trigger_element][k] = TRUE;
3298           }
3299         }
3300       }
3301     }
3302   }
3303
3304   // ---------- initialize push delay -----------------------------------------
3305
3306   // initialize push delay values to default
3307   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3308   {
3309     if (!IS_CUSTOM_ELEMENT(i))
3310     {
3311       // set default push delay values (corrected since version 3.0.7-1)
3312       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3313       {
3314         element_info[i].push_delay_fixed = 2;
3315         element_info[i].push_delay_random = 8;
3316       }
3317       else
3318       {
3319         element_info[i].push_delay_fixed = 8;
3320         element_info[i].push_delay_random = 8;
3321       }
3322     }
3323   }
3324
3325   // set push delay value for certain elements from pre-defined list
3326   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3327   {
3328     int e = push_delay_list[i].element;
3329
3330     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3331     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3332   }
3333
3334   // set push delay value for Supaplex elements for newer engine versions
3335   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3336   {
3337     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338     {
3339       if (IS_SP_ELEMENT(i))
3340       {
3341         // set SP push delay to just enough to push under a falling zonk
3342         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3343
3344         element_info[i].push_delay_fixed  = delay;
3345         element_info[i].push_delay_random = 0;
3346       }
3347     }
3348   }
3349
3350   // ---------- initialize move stepsize --------------------------------------
3351
3352   // initialize move stepsize values to default
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     if (!IS_CUSTOM_ELEMENT(i))
3355       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3356
3357   // set move stepsize value for certain elements from pre-defined list
3358   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = move_stepsize_list[i].element;
3361
3362     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3363
3364     // set move stepsize value for certain elements for older engine versions
3365     if (use_old_move_stepsize_for_magic_wall)
3366     {
3367       if (e == EL_MAGIC_WALL_FILLING ||
3368           e == EL_MAGIC_WALL_EMPTYING ||
3369           e == EL_BD_MAGIC_WALL_FILLING ||
3370           e == EL_BD_MAGIC_WALL_EMPTYING)
3371         element_info[e].move_stepsize *= 2;
3372     }
3373   }
3374
3375   // ---------- initialize collect score --------------------------------------
3376
3377   // initialize collect score values for custom elements from initial value
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379     if (IS_CUSTOM_ELEMENT(i))
3380       element_info[i].collect_score = element_info[i].collect_score_initial;
3381
3382   // ---------- initialize collect count --------------------------------------
3383
3384   // initialize collect count values for non-custom elements
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].collect_count_initial = 0;
3388
3389   // add collect count values for all elements from pre-defined list
3390   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3391     element_info[collect_count_list[i].element].collect_count_initial =
3392       collect_count_list[i].count;
3393
3394   // ---------- initialize access direction -----------------------------------
3395
3396   // initialize access direction values to default (access from every side)
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3400
3401   // set access direction value for certain elements from pre-defined list
3402   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3403     element_info[access_direction_list[i].element].access_direction =
3404       access_direction_list[i].direction;
3405
3406   // ---------- initialize explosion content ----------------------------------
3407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3408   {
3409     if (IS_CUSTOM_ELEMENT(i))
3410       continue;
3411
3412     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3413     {
3414       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3415
3416       element_info[i].content.e[x][y] =
3417         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3418          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3419          i == EL_PLAYER_3 ? EL_EMERALD :
3420          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3421          i == EL_MOLE ? EL_EMERALD_RED :
3422          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3423          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3424          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3425          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3426          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3427          i == EL_WALL_EMERALD ? EL_EMERALD :
3428          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3429          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3430          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3431          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3432          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3433          i == EL_WALL_PEARL ? EL_PEARL :
3434          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3435          EL_EMPTY);
3436     }
3437   }
3438
3439   // ---------- initialize recursion detection --------------------------------
3440   recursion_loop_depth = 0;
3441   recursion_loop_detected = FALSE;
3442   recursion_loop_element = EL_UNDEFINED;
3443
3444   // ---------- initialize graphics engine ------------------------------------
3445   game.scroll_delay_value =
3446     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3447      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3448      !setup.forced_scroll_delay           ? 0 :
3449      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3450   game.scroll_delay_value =
3451     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3452
3453   // ---------- initialize game engine snapshots ------------------------------
3454   for (i = 0; i < MAX_PLAYERS; i++)
3455     game.snapshot.last_action[i] = 0;
3456   game.snapshot.changed_action = FALSE;
3457   game.snapshot.collected_item = FALSE;
3458   game.snapshot.mode =
3459     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3460      SNAPSHOT_MODE_EVERY_STEP :
3461      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3462      SNAPSHOT_MODE_EVERY_MOVE :
3463      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3464      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3465   game.snapshot.save_snapshot = FALSE;
3466
3467   // ---------- initialize level time for Supaplex engine ---------------------
3468   // Supaplex levels with time limit currently unsupported -- should be added
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3470     level.time = 0;
3471
3472   // ---------- initialize flags for handling game actions --------------------
3473
3474   // set flags for game actions to default values
3475   game.use_key_actions = TRUE;
3476   game.use_mouse_actions = FALSE;
3477
3478   // when using Mirror Magic game engine, handle mouse events only
3479   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3480   {
3481     game.use_key_actions = FALSE;
3482     game.use_mouse_actions = TRUE;
3483   }
3484
3485   // check for custom elements with mouse click events
3486   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3487   {
3488     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3489     {
3490       int element = EL_CUSTOM_START + i;
3491
3492       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3493           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3494           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3495           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3496         game.use_mouse_actions = TRUE;
3497     }
3498   }
3499 }
3500
3501 static int get_num_special_action(int element, int action_first,
3502                                   int action_last)
3503 {
3504   int num_special_action = 0;
3505   int i, j;
3506
3507   for (i = action_first; i <= action_last; i++)
3508   {
3509     boolean found = FALSE;
3510
3511     for (j = 0; j < NUM_DIRECTIONS; j++)
3512       if (el_act_dir2img(element, i, j) !=
3513           el_act_dir2img(element, ACTION_DEFAULT, j))
3514         found = TRUE;
3515
3516     if (found)
3517       num_special_action++;
3518     else
3519       break;
3520   }
3521
3522   return num_special_action;
3523 }
3524
3525
3526 // ============================================================================
3527 // InitGame()
3528 // ----------------------------------------------------------------------------
3529 // initialize and start new game
3530 // ============================================================================
3531
3532 #if DEBUG_INIT_PLAYER
3533 static void DebugPrintPlayerStatus(char *message)
3534 {
3535   int i;
3536
3537   if (!options.debug)
3538     return;
3539
3540   Debug("game:init:player", "%s:", message);
3541
3542   for (i = 0; i < MAX_PLAYERS; i++)
3543   {
3544     struct PlayerInfo *player = &stored_player[i];
3545
3546     Debug("game:init:player",
3547           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3548           i + 1,
3549           player->present,
3550           player->connected,
3551           player->connected_locally,
3552           player->connected_network,
3553           player->active,
3554           (local_player == player ? " (local player)" : ""));
3555   }
3556 }
3557 #endif
3558
3559 void InitGame(void)
3560 {
3561   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3562   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3563   int fade_mask = REDRAW_FIELD;
3564
3565   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3566   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3567   int initial_move_dir = MV_DOWN;
3568   int i, j, x, y;
3569
3570   // required here to update video display before fading (FIX THIS)
3571   DrawMaskedBorder(REDRAW_DOOR_2);
3572
3573   if (!game.restart_level)
3574     CloseDoor(DOOR_CLOSE_1);
3575
3576   SetGameStatus(GAME_MODE_PLAYING);
3577
3578   if (level_editor_test_game)
3579     FadeSkipNextFadeOut();
3580   else
3581     FadeSetEnterScreen();
3582
3583   if (CheckFadeAll())
3584     fade_mask = REDRAW_ALL;
3585
3586   FadeLevelSoundsAndMusic();
3587
3588   ExpireSoundLoops(TRUE);
3589
3590   FadeOut(fade_mask);
3591
3592   if (level_editor_test_game)
3593     FadeSkipNextFadeIn();
3594
3595   // needed if different viewport properties defined for playing
3596   ChangeViewportPropertiesIfNeeded();
3597
3598   ClearField();
3599
3600   DrawCompleteVideoDisplay();
3601
3602   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3603
3604   InitGameEngine();
3605   InitGameControlValues();
3606
3607   if (tape.recording)
3608   {
3609     // initialize tape actions from game when recording tape
3610     tape.use_key_actions   = game.use_key_actions;
3611     tape.use_mouse_actions = game.use_mouse_actions;
3612
3613     // initialize visible playfield size when recording tape (for team mode)
3614     tape.scr_fieldx = SCR_FIELDX;
3615     tape.scr_fieldy = SCR_FIELDY;
3616   }
3617
3618   // don't play tapes over network
3619   network_playing = (network.enabled && !tape.playing);
3620
3621   for (i = 0; i < MAX_PLAYERS; i++)
3622   {
3623     struct PlayerInfo *player = &stored_player[i];
3624
3625     player->index_nr = i;
3626     player->index_bit = (1 << i);
3627     player->element_nr = EL_PLAYER_1 + i;
3628
3629     player->present = FALSE;
3630     player->active = FALSE;
3631     player->mapped = FALSE;
3632
3633     player->killed = FALSE;
3634     player->reanimated = FALSE;
3635     player->buried = FALSE;
3636
3637     player->action = 0;
3638     player->effective_action = 0;
3639     player->programmed_action = 0;
3640     player->snap_action = 0;
3641
3642     player->mouse_action.lx = 0;
3643     player->mouse_action.ly = 0;
3644     player->mouse_action.button = 0;
3645     player->mouse_action.button_hint = 0;
3646
3647     player->effective_mouse_action.lx = 0;
3648     player->effective_mouse_action.ly = 0;
3649     player->effective_mouse_action.button = 0;
3650     player->effective_mouse_action.button_hint = 0;
3651
3652     for (j = 0; j < MAX_NUM_KEYS; j++)
3653       player->key[j] = FALSE;
3654
3655     player->num_white_keys = 0;
3656
3657     player->dynabomb_count = 0;
3658     player->dynabomb_size = 1;
3659     player->dynabombs_left = 0;
3660     player->dynabomb_xl = FALSE;
3661
3662     player->MovDir = initial_move_dir;
3663     player->MovPos = 0;
3664     player->GfxPos = 0;
3665     player->GfxDir = initial_move_dir;
3666     player->GfxAction = ACTION_DEFAULT;
3667     player->Frame = 0;
3668     player->StepFrame = 0;
3669
3670     player->initial_element = player->element_nr;
3671     player->artwork_element =
3672       (level.use_artwork_element[i] ? level.artwork_element[i] :
3673        player->element_nr);
3674     player->use_murphy = FALSE;
3675
3676     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3677     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3678
3679     player->gravity = level.initial_player_gravity[i];
3680
3681     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3682
3683     player->actual_frame_counter = 0;
3684
3685     player->step_counter = 0;
3686
3687     player->last_move_dir = initial_move_dir;
3688
3689     player->is_active = FALSE;
3690
3691     player->is_waiting = FALSE;
3692     player->is_moving = FALSE;
3693     player->is_auto_moving = FALSE;
3694     player->is_digging = FALSE;
3695     player->is_snapping = FALSE;
3696     player->is_collecting = FALSE;
3697     player->is_pushing = FALSE;
3698     player->is_switching = FALSE;
3699     player->is_dropping = FALSE;
3700     player->is_dropping_pressed = FALSE;
3701
3702     player->is_bored = FALSE;
3703     player->is_sleeping = FALSE;
3704
3705     player->was_waiting = TRUE;
3706     player->was_moving = FALSE;
3707     player->was_snapping = FALSE;
3708     player->was_dropping = FALSE;
3709
3710     player->force_dropping = FALSE;
3711
3712     player->frame_counter_bored = -1;
3713     player->frame_counter_sleeping = -1;
3714
3715     player->anim_delay_counter = 0;
3716     player->post_delay_counter = 0;
3717
3718     player->dir_waiting = initial_move_dir;
3719     player->action_waiting = ACTION_DEFAULT;
3720     player->last_action_waiting = ACTION_DEFAULT;
3721     player->special_action_bored = ACTION_DEFAULT;
3722     player->special_action_sleeping = ACTION_DEFAULT;
3723
3724     player->switch_x = -1;
3725     player->switch_y = -1;
3726
3727     player->drop_x = -1;
3728     player->drop_y = -1;
3729
3730     player->show_envelope = 0;
3731
3732     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3733
3734     player->push_delay       = -1;      // initialized when pushing starts
3735     player->push_delay_value = game.initial_push_delay_value;
3736
3737     player->drop_delay = 0;
3738     player->drop_pressed_delay = 0;
3739
3740     player->last_jx = -1;
3741     player->last_jy = -1;
3742     player->jx = -1;
3743     player->jy = -1;
3744
3745     player->shield_normal_time_left = 0;
3746     player->shield_deadly_time_left = 0;
3747
3748     player->last_removed_element = EL_UNDEFINED;
3749
3750     player->inventory_infinite_element = EL_UNDEFINED;
3751     player->inventory_size = 0;
3752
3753     if (level.use_initial_inventory[i])
3754     {
3755       for (j = 0; j < level.initial_inventory_size[i]; j++)
3756       {
3757         int element = level.initial_inventory_content[i][j];
3758         int collect_count = element_info[element].collect_count_initial;
3759         int k;
3760
3761         if (!IS_CUSTOM_ELEMENT(element))
3762           collect_count = 1;
3763
3764         if (collect_count == 0)
3765           player->inventory_infinite_element = element;
3766         else
3767           for (k = 0; k < collect_count; k++)
3768             if (player->inventory_size < MAX_INVENTORY_SIZE)
3769               player->inventory_element[player->inventory_size++] = element;
3770       }
3771     }
3772
3773     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3774     SnapField(player, 0, 0);
3775
3776     map_player_action[i] = i;
3777   }
3778
3779   network_player_action_received = FALSE;
3780
3781   // initial null action
3782   if (network_playing)
3783     SendToServer_MovePlayer(MV_NONE);
3784
3785   FrameCounter = 0;
3786   TimeFrames = 0;
3787   TimePlayed = 0;
3788   TimeLeft = level.time;
3789   TapeTime = 0;
3790
3791   ScreenMovDir = MV_NONE;
3792   ScreenMovPos = 0;
3793   ScreenGfxPos = 0;
3794
3795   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3796
3797   game.robot_wheel_x = -1;
3798   game.robot_wheel_y = -1;
3799
3800   game.exit_x = -1;
3801   game.exit_y = -1;
3802
3803   game.all_players_gone = FALSE;
3804
3805   game.LevelSolved = FALSE;
3806   game.GameOver = FALSE;
3807
3808   game.GamePlayed = !tape.playing;
3809
3810   game.LevelSolved_GameWon = FALSE;
3811   game.LevelSolved_GameEnd = FALSE;
3812   game.LevelSolved_SaveTape = FALSE;
3813   game.LevelSolved_SaveScore = FALSE;
3814
3815   game.LevelSolved_CountingTime = 0;
3816   game.LevelSolved_CountingScore = 0;
3817   game.LevelSolved_CountingHealth = 0;
3818
3819   game.panel.active = TRUE;
3820
3821   game.no_time_limit = (level.time == 0);
3822
3823   game.yamyam_content_nr = 0;
3824   game.robot_wheel_active = FALSE;
3825   game.magic_wall_active = FALSE;
3826   game.magic_wall_time_left = 0;
3827   game.light_time_left = 0;
3828   game.timegate_time_left = 0;
3829   game.switchgate_pos = 0;
3830   game.wind_direction = level.wind_direction_initial;
3831
3832   game.time_final = 0;
3833   game.score_time_final = 0;
3834
3835   game.score = 0;
3836   game.score_final = 0;
3837
3838   game.health = MAX_HEALTH;
3839   game.health_final = MAX_HEALTH;
3840
3841   game.gems_still_needed = level.gems_needed;
3842   game.sokoban_fields_still_needed = 0;
3843   game.sokoban_objects_still_needed = 0;
3844   game.lights_still_needed = 0;
3845   game.players_still_needed = 0;
3846   game.friends_still_needed = 0;
3847
3848   game.lenses_time_left = 0;
3849   game.magnify_time_left = 0;
3850
3851   game.ball_active = level.ball_active_initial;
3852   game.ball_content_nr = 0;
3853
3854   game.explosions_delayed = TRUE;
3855
3856   game.envelope_active = FALSE;
3857
3858   // special case: set custom artwork setting to initial value
3859   game.use_masked_elements = game.use_masked_elements_initial;
3860
3861   for (i = 0; i < NUM_BELTS; i++)
3862   {
3863     game.belt_dir[i] = MV_NONE;
3864     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3865   }
3866
3867   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3868     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3869
3870 #if DEBUG_INIT_PLAYER
3871   DebugPrintPlayerStatus("Player status at level initialization");
3872 #endif
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     Tile[x][y] = Last[x][y] = level.field[x][y];
3877     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3878     ChangeDelay[x][y] = 0;
3879     ChangePage[x][y] = -1;
3880     CustomValue[x][y] = 0;              // initialized in InitField()
3881     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3882     AmoebaNr[x][y] = 0;
3883     WasJustMoving[x][y] = 0;
3884     WasJustFalling[x][y] = 0;
3885     CheckCollision[x][y] = 0;
3886     CheckImpact[x][y] = 0;
3887     Stop[x][y] = FALSE;
3888     Pushed[x][y] = FALSE;
3889
3890     ChangeCount[x][y] = 0;
3891     ChangeEvent[x][y] = -1;
3892
3893     ExplodePhase[x][y] = 0;
3894     ExplodeDelay[x][y] = 0;
3895     ExplodeField[x][y] = EX_TYPE_NONE;
3896
3897     RunnerVisit[x][y] = 0;
3898     PlayerVisit[x][y] = 0;
3899
3900     GfxFrame[x][y] = 0;
3901     GfxRandom[x][y] = INIT_GFX_RANDOM();
3902     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3903     GfxElement[x][y] = EL_UNDEFINED;
3904     GfxElementEmpty[x][y] = EL_EMPTY;
3905     GfxAction[x][y] = ACTION_DEFAULT;
3906     GfxDir[x][y] = MV_NONE;
3907     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3908   }
3909
3910   SCAN_PLAYFIELD(x, y)
3911   {
3912     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3913       emulate_bd = FALSE;
3914     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3915       emulate_sp = FALSE;
3916
3917     InitField(x, y, TRUE);
3918
3919     ResetGfxAnimation(x, y);
3920   }
3921
3922   InitBeltMovement();
3923
3924   for (i = 0; i < MAX_PLAYERS; i++)
3925   {
3926     struct PlayerInfo *player = &stored_player[i];
3927
3928     // set number of special actions for bored and sleeping animation
3929     player->num_special_action_bored =
3930       get_num_special_action(player->artwork_element,
3931                              ACTION_BORING_1, ACTION_BORING_LAST);
3932     player->num_special_action_sleeping =
3933       get_num_special_action(player->artwork_element,
3934                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3935   }
3936
3937   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3938                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3939
3940   // initialize type of slippery elements
3941   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3942   {
3943     if (!IS_CUSTOM_ELEMENT(i))
3944     {
3945       // default: elements slip down either to the left or right randomly
3946       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3947
3948       // SP style elements prefer to slip down on the left side
3949       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3950         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3951
3952       // BD style elements prefer to slip down on the left side
3953       if (game.emulation == EMU_BOULDERDASH)
3954         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3955     }
3956   }
3957
3958   // initialize explosion and ignition delay
3959   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3960   {
3961     if (!IS_CUSTOM_ELEMENT(i))
3962     {
3963       int num_phase = 8;
3964       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3965                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3966                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3967       int last_phase = (num_phase + 1) * delay;
3968       int half_phase = (num_phase / 2) * delay;
3969
3970       element_info[i].explosion_delay = last_phase - 1;
3971       element_info[i].ignition_delay = half_phase;
3972
3973       if (i == EL_BLACK_ORB)
3974         element_info[i].ignition_delay = 1;
3975     }
3976   }
3977
3978   // correct non-moving belts to start moving left
3979   for (i = 0; i < NUM_BELTS; i++)
3980     if (game.belt_dir[i] == MV_NONE)
3981       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3982
3983 #if USE_NEW_PLAYER_ASSIGNMENTS
3984   // use preferred player also in local single-player mode
3985   if (!network.enabled && !game.team_mode)
3986   {
3987     int new_index_nr = setup.network_player_nr;
3988
3989     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3990     {
3991       for (i = 0; i < MAX_PLAYERS; i++)
3992         stored_player[i].connected_locally = FALSE;
3993
3994       stored_player[new_index_nr].connected_locally = TRUE;
3995     }
3996   }
3997
3998   for (i = 0; i < MAX_PLAYERS; i++)
3999   {
4000     stored_player[i].connected = FALSE;
4001
4002     // in network game mode, the local player might not be the first player
4003     if (stored_player[i].connected_locally)
4004       local_player = &stored_player[i];
4005   }
4006
4007   if (!network.enabled)
4008     local_player->connected = TRUE;
4009
4010   if (tape.playing)
4011   {
4012     for (i = 0; i < MAX_PLAYERS; i++)
4013       stored_player[i].connected = tape.player_participates[i];
4014   }
4015   else if (network.enabled)
4016   {
4017     // add team mode players connected over the network (needed for correct
4018     // assignment of player figures from level to locally playing players)
4019
4020     for (i = 0; i < MAX_PLAYERS; i++)
4021       if (stored_player[i].connected_network)
4022         stored_player[i].connected = TRUE;
4023   }
4024   else if (game.team_mode)
4025   {
4026     // try to guess locally connected team mode players (needed for correct
4027     // assignment of player figures from level to locally playing players)
4028
4029     for (i = 0; i < MAX_PLAYERS; i++)
4030       if (setup.input[i].use_joystick ||
4031           setup.input[i].key.left != KSYM_UNDEFINED)
4032         stored_player[i].connected = TRUE;
4033   }
4034
4035 #if DEBUG_INIT_PLAYER
4036   DebugPrintPlayerStatus("Player status after level initialization");
4037 #endif
4038
4039 #if DEBUG_INIT_PLAYER
4040   Debug("game:init:player", "Reassigning players ...");
4041 #endif
4042
4043   // check if any connected player was not found in playfield
4044   for (i = 0; i < MAX_PLAYERS; i++)
4045   {
4046     struct PlayerInfo *player = &stored_player[i];
4047
4048     if (player->connected && !player->present)
4049     {
4050       struct PlayerInfo *field_player = NULL;
4051
4052 #if DEBUG_INIT_PLAYER
4053       Debug("game:init:player",
4054             "- looking for field player for player %d ...", i + 1);
4055 #endif
4056
4057       // assign first free player found that is present in the playfield
4058
4059       // first try: look for unmapped playfield player that is not connected
4060       for (j = 0; j < MAX_PLAYERS; j++)
4061         if (field_player == NULL &&
4062             stored_player[j].present &&
4063             !stored_player[j].mapped &&
4064             !stored_player[j].connected)
4065           field_player = &stored_player[j];
4066
4067       // second try: look for *any* unmapped playfield player
4068       for (j = 0; j < MAX_PLAYERS; j++)
4069         if (field_player == NULL &&
4070             stored_player[j].present &&
4071             !stored_player[j].mapped)
4072           field_player = &stored_player[j];
4073
4074       if (field_player != NULL)
4075       {
4076         int jx = field_player->jx, jy = field_player->jy;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- found player %d",
4080               field_player->index_nr + 1);
4081 #endif
4082
4083         player->present = FALSE;
4084         player->active = FALSE;
4085
4086         field_player->present = TRUE;
4087         field_player->active = TRUE;
4088
4089         /*
4090         player->initial_element = field_player->initial_element;
4091         player->artwork_element = field_player->artwork_element;
4092
4093         player->block_last_field       = field_player->block_last_field;
4094         player->block_delay_adjustment = field_player->block_delay_adjustment;
4095         */
4096
4097         StorePlayer[jx][jy] = field_player->element_nr;
4098
4099         field_player->jx = field_player->last_jx = jx;
4100         field_player->jy = field_player->last_jy = jy;
4101
4102         if (local_player == player)
4103           local_player = field_player;
4104
4105         map_player_action[field_player->index_nr] = i;
4106
4107         field_player->mapped = TRUE;
4108
4109 #if DEBUG_INIT_PLAYER
4110         Debug("game:init:player", "- map_player_action[%d] == %d",
4111               field_player->index_nr + 1, i + 1);
4112 #endif
4113       }
4114     }
4115
4116     if (player->connected && player->present)
4117       player->mapped = TRUE;
4118   }
4119
4120 #if DEBUG_INIT_PLAYER
4121   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4122 #endif
4123
4124 #else
4125
4126   // check if any connected player was not found in playfield
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128   {
4129     struct PlayerInfo *player = &stored_player[i];
4130
4131     if (player->connected && !player->present)
4132     {
4133       for (j = 0; j < MAX_PLAYERS; j++)
4134       {
4135         struct PlayerInfo *field_player = &stored_player[j];
4136         int jx = field_player->jx, jy = field_player->jy;
4137
4138         // assign first free player found that is present in the playfield
4139         if (field_player->present && !field_player->connected)
4140         {
4141           player->present = TRUE;
4142           player->active = TRUE;
4143
4144           field_player->present = FALSE;
4145           field_player->active = FALSE;
4146
4147           player->initial_element = field_player->initial_element;
4148           player->artwork_element = field_player->artwork_element;
4149
4150           player->block_last_field       = field_player->block_last_field;
4151           player->block_delay_adjustment = field_player->block_delay_adjustment;
4152
4153           StorePlayer[jx][jy] = player->element_nr;
4154
4155           player->jx = player->last_jx = jx;
4156           player->jy = player->last_jy = jy;
4157
4158           break;
4159         }
4160       }
4161     }
4162   }
4163 #endif
4164
4165 #if 0
4166   Debug("game:init:player", "local_player->present == %d",
4167         local_player->present);
4168 #endif
4169
4170   // set focus to local player for network games, else to all players
4171   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4172   game.centered_player_nr_next = game.centered_player_nr;
4173   game.set_centered_player = FALSE;
4174   game.set_centered_player_wrap = FALSE;
4175
4176   if (network_playing && tape.recording)
4177   {
4178     // store client dependent player focus when recording network games
4179     tape.centered_player_nr_next = game.centered_player_nr_next;
4180     tape.set_centered_player = TRUE;
4181   }
4182
4183   if (tape.playing)
4184   {
4185     // when playing a tape, eliminate all players who do not participate
4186
4187 #if USE_NEW_PLAYER_ASSIGNMENTS
4188
4189     if (!game.team_mode)
4190     {
4191       for (i = 0; i < MAX_PLAYERS; i++)
4192       {
4193         if (stored_player[i].active &&
4194             !tape.player_participates[map_player_action[i]])
4195         {
4196           struct PlayerInfo *player = &stored_player[i];
4197           int jx = player->jx, jy = player->jy;
4198
4199 #if DEBUG_INIT_PLAYER
4200           Debug("game:init:player", "Removing player %d at (%d, %d)",
4201                 i + 1, jx, jy);
4202 #endif
4203
4204           player->active = FALSE;
4205           StorePlayer[jx][jy] = 0;
4206           Tile[jx][jy] = EL_EMPTY;
4207         }
4208       }
4209     }
4210
4211 #else
4212
4213     for (i = 0; i < MAX_PLAYERS; i++)
4214     {
4215       if (stored_player[i].active &&
4216           !tape.player_participates[i])
4217       {
4218         struct PlayerInfo *player = &stored_player[i];
4219         int jx = player->jx, jy = player->jy;
4220
4221         player->active = FALSE;
4222         StorePlayer[jx][jy] = 0;
4223         Tile[jx][jy] = EL_EMPTY;
4224       }
4225     }
4226 #endif
4227   }
4228   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4229   {
4230     // when in single player mode, eliminate all but the local player
4231
4232     for (i = 0; i < MAX_PLAYERS; i++)
4233     {
4234       struct PlayerInfo *player = &stored_player[i];
4235
4236       if (player->active && player != local_player)
4237       {
4238         int jx = player->jx, jy = player->jy;
4239
4240         player->active = FALSE;
4241         player->present = FALSE;
4242
4243         StorePlayer[jx][jy] = 0;
4244         Tile[jx][jy] = EL_EMPTY;
4245       }
4246     }
4247   }
4248
4249   for (i = 0; i < MAX_PLAYERS; i++)
4250     if (stored_player[i].active)
4251       game.players_still_needed++;
4252
4253   if (level.solved_by_one_player)
4254     game.players_still_needed = 1;
4255
4256   // when recording the game, store which players take part in the game
4257   if (tape.recording)
4258   {
4259 #if USE_NEW_PLAYER_ASSIGNMENTS
4260     for (i = 0; i < MAX_PLAYERS; i++)
4261       if (stored_player[i].connected)
4262         tape.player_participates[i] = TRUE;
4263 #else
4264     for (i = 0; i < MAX_PLAYERS; i++)
4265       if (stored_player[i].active)
4266         tape.player_participates[i] = TRUE;
4267 #endif
4268   }
4269
4270 #if DEBUG_INIT_PLAYER
4271   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4272 #endif
4273
4274   if (BorderElement == EL_EMPTY)
4275   {
4276     SBX_Left = 0;
4277     SBX_Right = lev_fieldx - SCR_FIELDX;
4278     SBY_Upper = 0;
4279     SBY_Lower = lev_fieldy - SCR_FIELDY;
4280   }
4281   else
4282   {
4283     SBX_Left = -1;
4284     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4285     SBY_Upper = -1;
4286     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4287   }
4288
4289   if (full_lev_fieldx <= SCR_FIELDX)
4290     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4291   if (full_lev_fieldy <= SCR_FIELDY)
4292     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4293
4294   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4295     SBX_Left--;
4296   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4297     SBY_Upper--;
4298
4299   // if local player not found, look for custom element that might create
4300   // the player (make some assumptions about the right custom element)
4301   if (!local_player->present)
4302   {
4303     int start_x = 0, start_y = 0;
4304     int found_rating = 0;
4305     int found_element = EL_UNDEFINED;
4306     int player_nr = local_player->index_nr;
4307
4308     SCAN_PLAYFIELD(x, y)
4309     {
4310       int element = Tile[x][y];
4311       int content;
4312       int xx, yy;
4313       boolean is_player;
4314
4315       if (level.use_start_element[player_nr] &&
4316           level.start_element[player_nr] == element &&
4317           found_rating < 4)
4318       {
4319         start_x = x;
4320         start_y = y;
4321
4322         found_rating = 4;
4323         found_element = element;
4324       }
4325
4326       if (!IS_CUSTOM_ELEMENT(element))
4327         continue;
4328
4329       if (CAN_CHANGE(element))
4330       {
4331         for (i = 0; i < element_info[element].num_change_pages; i++)
4332         {
4333           // check for player created from custom element as single target
4334           content = element_info[element].change_page[i].target_element;
4335           is_player = IS_PLAYER_ELEMENT(content);
4336
4337           if (is_player && (found_rating < 3 ||
4338                             (found_rating == 3 && element < found_element)))
4339           {
4340             start_x = x;
4341             start_y = y;
4342
4343             found_rating = 3;
4344             found_element = element;
4345           }
4346         }
4347       }
4348
4349       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4350       {
4351         // check for player created from custom element as explosion content
4352         content = element_info[element].content.e[xx][yy];
4353         is_player = IS_PLAYER_ELEMENT(content);
4354
4355         if (is_player && (found_rating < 2 ||
4356                           (found_rating == 2 && element < found_element)))
4357         {
4358           start_x = x + xx - 1;
4359           start_y = y + yy - 1;
4360
4361           found_rating = 2;
4362           found_element = element;
4363         }
4364
4365         if (!CAN_CHANGE(element))
4366           continue;
4367
4368         for (i = 0; i < element_info[element].num_change_pages; i++)
4369         {
4370           // check for player created from custom element as extended target
4371           content =
4372             element_info[element].change_page[i].target_content.e[xx][yy];
4373
4374           is_player = IS_PLAYER_ELEMENT(content);
4375
4376           if (is_player && (found_rating < 1 ||
4377                             (found_rating == 1 && element < found_element)))
4378           {
4379             start_x = x + xx - 1;
4380             start_y = y + yy - 1;
4381
4382             found_rating = 1;
4383             found_element = element;
4384           }
4385         }
4386       }
4387     }
4388
4389     scroll_x = SCROLL_POSITION_X(start_x);
4390     scroll_y = SCROLL_POSITION_Y(start_y);
4391   }
4392   else
4393   {
4394     scroll_x = SCROLL_POSITION_X(local_player->jx);
4395     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4396   }
4397
4398   // !!! FIX THIS (START) !!!
4399   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4400   {
4401     InitGameEngine_EM();
4402   }
4403   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4404   {
4405     InitGameEngine_SP();
4406   }
4407   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4408   {
4409     InitGameEngine_MM();
4410   }
4411   else
4412   {
4413     DrawLevel(REDRAW_FIELD);
4414     DrawAllPlayers();
4415
4416     // after drawing the level, correct some elements
4417     if (game.timegate_time_left == 0)
4418       CloseAllOpenTimegates();
4419   }
4420
4421   // blit playfield from scroll buffer to normal back buffer for fading in
4422   BlitScreenToBitmap(backbuffer);
4423   // !!! FIX THIS (END) !!!
4424
4425   DrawMaskedBorder(fade_mask);
4426
4427   FadeIn(fade_mask);
4428
4429 #if 1
4430   // full screen redraw is required at this point in the following cases:
4431   // - special editor door undrawn when game was started from level editor
4432   // - drawing area (playfield) was changed and has to be removed completely
4433   redraw_mask = REDRAW_ALL;
4434   BackToFront();
4435 #endif
4436
4437   if (!game.restart_level)
4438   {
4439     // copy default game door content to main double buffer
4440
4441     // !!! CHECK AGAIN !!!
4442     SetPanelBackground();
4443     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4444     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4445   }
4446
4447   SetPanelBackground();
4448   SetDrawBackgroundMask(REDRAW_DOOR_1);
4449
4450   UpdateAndDisplayGameControlValues();
4451
4452   if (!game.restart_level)
4453   {
4454     UnmapGameButtons();
4455     UnmapTapeButtons();
4456
4457     FreeGameButtons();
4458     CreateGameButtons();
4459
4460     MapGameButtons();
4461     MapTapeButtons();
4462
4463     // copy actual game door content to door double buffer for OpenDoor()
4464     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4465
4466     OpenDoor(DOOR_OPEN_ALL);
4467
4468     KeyboardAutoRepeatOffUnlessAutoplay();
4469
4470 #if DEBUG_INIT_PLAYER
4471     DebugPrintPlayerStatus("Player status (final)");
4472 #endif
4473   }
4474
4475   UnmapAllGadgets();
4476
4477   MapGameButtons();
4478   MapTapeButtons();
4479
4480   if (!game.restart_level && !tape.playing)
4481   {
4482     LevelStats_incPlayed(level_nr);
4483
4484     SaveLevelSetup_SeriesInfo();
4485   }
4486
4487   game.restart_level = FALSE;
4488   game.restart_game_message = NULL;
4489
4490   game.request_active = FALSE;
4491   game.request_active_or_moving = FALSE;
4492
4493   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4494     InitGameActions_MM();
4495
4496   SaveEngineSnapshotToListInitial();
4497
4498   if (!game.restart_level)
4499   {
4500     PlaySound(SND_GAME_STARTING);
4501
4502     if (setup.sound_music)
4503       PlayLevelMusic();
4504   }
4505
4506   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4507 }
4508
4509 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4510                         int actual_player_x, int actual_player_y)
4511 {
4512   // this is used for non-R'n'D game engines to update certain engine values
4513
4514   // needed to determine if sounds are played within the visible screen area
4515   scroll_x = actual_scroll_x;
4516   scroll_y = actual_scroll_y;
4517
4518   // needed to get player position for "follow finger" playing input method
4519   local_player->jx = actual_player_x;
4520   local_player->jy = actual_player_y;
4521 }
4522
4523 void InitMovDir(int x, int y)
4524 {
4525   int i, element = Tile[x][y];
4526   static int xy[4][2] =
4527   {
4528     {  0, +1 },
4529     { +1,  0 },
4530     {  0, -1 },
4531     { -1,  0 }
4532   };
4533   static int direction[3][4] =
4534   {
4535     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4536     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4537     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4538   };
4539
4540   switch (element)
4541   {
4542     case EL_BUG_RIGHT:
4543     case EL_BUG_UP:
4544     case EL_BUG_LEFT:
4545     case EL_BUG_DOWN:
4546       Tile[x][y] = EL_BUG;
4547       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4548       break;
4549
4550     case EL_SPACESHIP_RIGHT:
4551     case EL_SPACESHIP_UP:
4552     case EL_SPACESHIP_LEFT:
4553     case EL_SPACESHIP_DOWN:
4554       Tile[x][y] = EL_SPACESHIP;
4555       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4556       break;
4557
4558     case EL_BD_BUTTERFLY_RIGHT:
4559     case EL_BD_BUTTERFLY_UP:
4560     case EL_BD_BUTTERFLY_LEFT:
4561     case EL_BD_BUTTERFLY_DOWN:
4562       Tile[x][y] = EL_BD_BUTTERFLY;
4563       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4564       break;
4565
4566     case EL_BD_FIREFLY_RIGHT:
4567     case EL_BD_FIREFLY_UP:
4568     case EL_BD_FIREFLY_LEFT:
4569     case EL_BD_FIREFLY_DOWN:
4570       Tile[x][y] = EL_BD_FIREFLY;
4571       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4572       break;
4573
4574     case EL_PACMAN_RIGHT:
4575     case EL_PACMAN_UP:
4576     case EL_PACMAN_LEFT:
4577     case EL_PACMAN_DOWN:
4578       Tile[x][y] = EL_PACMAN;
4579       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4580       break;
4581
4582     case EL_YAMYAM_LEFT:
4583     case EL_YAMYAM_RIGHT:
4584     case EL_YAMYAM_UP:
4585     case EL_YAMYAM_DOWN:
4586       Tile[x][y] = EL_YAMYAM;
4587       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4588       break;
4589
4590     case EL_SP_SNIKSNAK:
4591       MovDir[x][y] = MV_UP;
4592       break;
4593
4594     case EL_SP_ELECTRON:
4595       MovDir[x][y] = MV_LEFT;
4596       break;
4597
4598     case EL_MOLE_LEFT:
4599     case EL_MOLE_RIGHT:
4600     case EL_MOLE_UP:
4601     case EL_MOLE_DOWN:
4602       Tile[x][y] = EL_MOLE;
4603       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4604       break;
4605
4606     case EL_SPRING_LEFT:
4607     case EL_SPRING_RIGHT:
4608       Tile[x][y] = EL_SPRING;
4609       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4610       break;
4611
4612     default:
4613       if (IS_CUSTOM_ELEMENT(element))
4614       {
4615         struct ElementInfo *ei = &element_info[element];
4616         int move_direction_initial = ei->move_direction_initial;
4617         int move_pattern = ei->move_pattern;
4618
4619         if (move_direction_initial == MV_START_PREVIOUS)
4620         {
4621           if (MovDir[x][y] != MV_NONE)
4622             return;
4623
4624           move_direction_initial = MV_START_AUTOMATIC;
4625         }
4626
4627         if (move_direction_initial == MV_START_RANDOM)
4628           MovDir[x][y] = 1 << RND(4);
4629         else if (move_direction_initial & MV_ANY_DIRECTION)
4630           MovDir[x][y] = move_direction_initial;
4631         else if (move_pattern == MV_ALL_DIRECTIONS ||
4632                  move_pattern == MV_TURNING_LEFT ||
4633                  move_pattern == MV_TURNING_RIGHT ||
4634                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4635                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4636                  move_pattern == MV_TURNING_RANDOM)
4637           MovDir[x][y] = 1 << RND(4);
4638         else if (move_pattern == MV_HORIZONTAL)
4639           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4640         else if (move_pattern == MV_VERTICAL)
4641           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4642         else if (move_pattern & MV_ANY_DIRECTION)
4643           MovDir[x][y] = element_info[element].move_pattern;
4644         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4645                  move_pattern == MV_ALONG_RIGHT_SIDE)
4646         {
4647           // use random direction as default start direction
4648           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4649             MovDir[x][y] = 1 << RND(4);
4650
4651           for (i = 0; i < NUM_DIRECTIONS; i++)
4652           {
4653             int x1 = x + xy[i][0];
4654             int y1 = y + xy[i][1];
4655
4656             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4657             {
4658               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4659                 MovDir[x][y] = direction[0][i];
4660               else
4661                 MovDir[x][y] = direction[1][i];
4662
4663               break;
4664             }
4665           }
4666         }                
4667       }
4668       else
4669       {
4670         MovDir[x][y] = 1 << RND(4);
4671
4672         if (element != EL_BUG &&
4673             element != EL_SPACESHIP &&
4674             element != EL_BD_BUTTERFLY &&
4675             element != EL_BD_FIREFLY)
4676           break;
4677
4678         for (i = 0; i < NUM_DIRECTIONS; i++)
4679         {
4680           int x1 = x + xy[i][0];
4681           int y1 = y + xy[i][1];
4682
4683           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4684           {
4685             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4686             {
4687               MovDir[x][y] = direction[0][i];
4688               break;
4689             }
4690             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4691                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4692             {
4693               MovDir[x][y] = direction[1][i];
4694               break;
4695             }
4696           }
4697         }
4698       }
4699       break;
4700   }
4701
4702   GfxDir[x][y] = MovDir[x][y];
4703 }
4704
4705 void InitAmoebaNr(int x, int y)
4706 {
4707   int i;
4708   int group_nr = AmoebaNeighbourNr(x, y);
4709
4710   if (group_nr == 0)
4711   {
4712     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4713     {
4714       if (AmoebaCnt[i] == 0)
4715       {
4716         group_nr = i;
4717         break;
4718       }
4719     }
4720   }
4721
4722   AmoebaNr[x][y] = group_nr;
4723   AmoebaCnt[group_nr]++;
4724   AmoebaCnt2[group_nr]++;
4725 }
4726
4727 static void LevelSolved_SetFinalGameValues(void)
4728 {
4729   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4730   game.score_time_final = (level.use_step_counter ? TimePlayed :
4731                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4732
4733   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4734                       game_em.lev->score :
4735                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4736                       game_mm.score :
4737                       game.score);
4738
4739   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                        MM_HEALTH(game_mm.laser_overload_value) :
4741                        game.health);
4742
4743   game.LevelSolved_CountingTime = game.time_final;
4744   game.LevelSolved_CountingScore = game.score_final;
4745   game.LevelSolved_CountingHealth = game.health_final;
4746 }
4747
4748 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4749 {
4750   game.LevelSolved_CountingTime = time;
4751   game.LevelSolved_CountingScore = score;
4752   game.LevelSolved_CountingHealth = health;
4753
4754   game_panel_controls[GAME_PANEL_TIME].value = time;
4755   game_panel_controls[GAME_PANEL_SCORE].value = score;
4756   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4757
4758   DisplayGameControlValues();
4759 }
4760
4761 static void LevelSolved(void)
4762 {
4763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4764       game.players_still_needed > 0)
4765     return;
4766
4767   game.LevelSolved = TRUE;
4768   game.GameOver = TRUE;
4769
4770   // needed here to display correct panel values while player walks into exit
4771   LevelSolved_SetFinalGameValues();
4772 }
4773
4774 void GameWon(void)
4775 {
4776   static int time_count_steps;
4777   static int time, time_final;
4778   static float score, score_final; // needed for time score < 10 for 10 seconds
4779   static int health, health_final;
4780   static int game_over_delay_1 = 0;
4781   static int game_over_delay_2 = 0;
4782   static int game_over_delay_3 = 0;
4783   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4784   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4785
4786   if (!game.LevelSolved_GameWon)
4787   {
4788     int i;
4789
4790     // do not start end game actions before the player stops moving (to exit)
4791     if (local_player->active && local_player->MovPos)
4792       return;
4793
4794     // calculate final game values after player finished walking into exit
4795     LevelSolved_SetFinalGameValues();
4796
4797     game.LevelSolved_GameWon = TRUE;
4798     game.LevelSolved_SaveTape = tape.recording;
4799     game.LevelSolved_SaveScore = !tape.playing;
4800
4801     if (!tape.playing)
4802     {
4803       LevelStats_incSolved(level_nr);
4804
4805       SaveLevelSetup_SeriesInfo();
4806     }
4807
4808     if (tape.auto_play)         // tape might already be stopped here
4809       tape.auto_play_level_solved = TRUE;
4810
4811     TapeStop();
4812
4813     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4814     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4815     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4816
4817     time = time_final = game.time_final;
4818     score = score_final = game.score_final;
4819     health = health_final = game.health_final;
4820
4821     // update game panel values before (delayed) counting of score (if any)
4822     LevelSolved_DisplayFinalGameValues(time, score, health);
4823
4824     // if level has time score defined, calculate new final game values
4825     if (time_score > 0)
4826     {
4827       int time_final_max = 999;
4828       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4829       int time_frames = 0;
4830       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4831       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4832
4833       if (TimeLeft > 0)
4834       {
4835         time_final = 0;
4836         time_frames = time_frames_left;
4837       }
4838       else if (game.no_time_limit && TimePlayed < time_final_max)
4839       {
4840         time_final = time_final_max;
4841         time_frames = time_frames_final_max - time_frames_played;
4842       }
4843
4844       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4845
4846       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4847
4848       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4849       {
4850         health_final = 0;
4851         score_final += health * time_score;
4852       }
4853
4854       game.score_final = score_final;
4855       game.health_final = health_final;
4856     }
4857
4858     // if not counting score after game, immediately update game panel values
4859     if (level_editor_test_game || !setup.count_score_after_game)
4860     {
4861       time = time_final;
4862       score = score_final;
4863
4864       LevelSolved_DisplayFinalGameValues(time, score, health);
4865     }
4866
4867     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4868     {
4869       // check if last player has left the level
4870       if (game.exit_x >= 0 &&
4871           game.exit_y >= 0)
4872       {
4873         int x = game.exit_x;
4874         int y = game.exit_y;
4875         int element = Tile[x][y];
4876
4877         // close exit door after last player
4878         if ((game.all_players_gone &&
4879              (element == EL_EXIT_OPEN ||
4880               element == EL_SP_EXIT_OPEN ||
4881               element == EL_STEEL_EXIT_OPEN)) ||
4882             element == EL_EM_EXIT_OPEN ||
4883             element == EL_EM_STEEL_EXIT_OPEN)
4884         {
4885
4886           Tile[x][y] =
4887             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4888              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4889              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4890              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4891              EL_EM_STEEL_EXIT_CLOSING);
4892
4893           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4894         }
4895
4896         // player disappears
4897         DrawLevelField(x, y);
4898       }
4899
4900       for (i = 0; i < MAX_PLAYERS; i++)
4901       {
4902         struct PlayerInfo *player = &stored_player[i];
4903
4904         if (player->present)
4905         {
4906           RemovePlayer(player);
4907
4908           // player disappears
4909           DrawLevelField(player->jx, player->jy);
4910         }
4911       }
4912     }
4913
4914     PlaySound(SND_GAME_WINNING);
4915   }
4916
4917   if (setup.count_score_after_game)
4918   {
4919     if (time != time_final)
4920     {
4921       if (game_over_delay_1 > 0)
4922       {
4923         game_over_delay_1--;
4924
4925         return;
4926       }
4927
4928       int time_to_go = ABS(time_final - time);
4929       int time_count_dir = (time < time_final ? +1 : -1);
4930
4931       if (time_to_go < time_count_steps)
4932         time_count_steps = 1;
4933
4934       time  += time_count_steps * time_count_dir;
4935       score += time_count_steps * time_score;
4936
4937       // set final score to correct rounding differences after counting score
4938       if (time == time_final)
4939         score = score_final;
4940
4941       LevelSolved_DisplayFinalGameValues(time, score, health);
4942
4943       if (time == time_final)
4944         StopSound(SND_GAME_LEVELTIME_BONUS);
4945       else if (setup.sound_loops)
4946         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4947       else
4948         PlaySound(SND_GAME_LEVELTIME_BONUS);
4949
4950       return;
4951     }
4952
4953     if (health != health_final)
4954     {
4955       if (game_over_delay_2 > 0)
4956       {
4957         game_over_delay_2--;
4958
4959         return;
4960       }
4961
4962       int health_count_dir = (health < health_final ? +1 : -1);
4963
4964       health += health_count_dir;
4965       score  += time_score;
4966
4967       LevelSolved_DisplayFinalGameValues(time, score, health);
4968
4969       if (health == health_final)
4970         StopSound(SND_GAME_LEVELTIME_BONUS);
4971       else if (setup.sound_loops)
4972         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4973       else
4974         PlaySound(SND_GAME_LEVELTIME_BONUS);
4975
4976       return;
4977     }
4978   }
4979
4980   game.panel.active = FALSE;
4981
4982   if (game_over_delay_3 > 0)
4983   {
4984     game_over_delay_3--;
4985
4986     return;
4987   }
4988
4989   GameEnd();
4990 }
4991
4992 void GameEnd(void)
4993 {
4994   // used instead of "level_nr" (needed for network games)
4995   int last_level_nr = levelset.level_nr;
4996   boolean tape_saved = FALSE;
4997
4998   game.LevelSolved_GameEnd = TRUE;
4999
5000   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5001   {
5002     // make sure that request dialog to save tape does not open door again
5003     if (!global.use_envelope_request)
5004       CloseDoor(DOOR_CLOSE_1);
5005
5006     // ask to save tape
5007     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5008
5009     // set unique basename for score tape (also saved in high score table)
5010     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5011   }
5012
5013   // if no tape is to be saved, close both doors simultaneously
5014   CloseDoor(DOOR_CLOSE_ALL);
5015
5016   if (level_editor_test_game || score_info_tape_play)
5017   {
5018     SetGameStatus(GAME_MODE_MAIN);
5019
5020     DrawMainMenu();
5021
5022     return;
5023   }
5024
5025   if (!game.LevelSolved_SaveScore)
5026   {
5027     SetGameStatus(GAME_MODE_MAIN);
5028
5029     DrawMainMenu();
5030
5031     return;
5032   }
5033
5034   if (level_nr == leveldir_current->handicap_level)
5035   {
5036     leveldir_current->handicap_level++;
5037
5038     SaveLevelSetup_SeriesInfo();
5039   }
5040
5041   // save score and score tape before potentially erasing tape below
5042   NewHighScore(last_level_nr, tape_saved);
5043
5044   if (setup.increment_levels &&
5045       level_nr < leveldir_current->last_level &&
5046       !network_playing)
5047   {
5048     level_nr++;         // advance to next level
5049     TapeErase();        // start with empty tape
5050
5051     if (setup.auto_play_next_level)
5052     {
5053       scores.continue_playing = TRUE;
5054
5055       LoadLevel(level_nr);
5056
5057       SaveLevelSetup_SeriesInfo();
5058     }
5059   }
5060
5061   if (scores.last_added >= 0 && setup.show_scores_after_game)
5062   {
5063     SetGameStatus(GAME_MODE_SCORES);
5064
5065     DrawHallOfFame(last_level_nr);
5066   }
5067   else if (scores.continue_playing)
5068   {
5069     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5070   }
5071   else
5072   {
5073     SetGameStatus(GAME_MODE_MAIN);
5074
5075     DrawMainMenu();
5076   }
5077 }
5078
5079 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5080                          boolean one_score_entry_per_name)
5081 {
5082   int i;
5083
5084   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5085     return -1;
5086
5087   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5088   {
5089     struct ScoreEntry *entry = &list->entry[i];
5090     boolean score_is_better = (new_entry->score >  entry->score);
5091     boolean score_is_equal  = (new_entry->score == entry->score);
5092     boolean time_is_better  = (new_entry->time  <  entry->time);
5093     boolean time_is_equal   = (new_entry->time  == entry->time);
5094     boolean better_by_score = (score_is_better ||
5095                                (score_is_equal && time_is_better));
5096     boolean better_by_time  = (time_is_better ||
5097                                (time_is_equal && score_is_better));
5098     boolean is_better = (level.rate_time_over_score ? better_by_time :
5099                          better_by_score);
5100     boolean entry_is_empty = (entry->score == 0 &&
5101                               entry->time == 0);
5102
5103     // prevent adding server score entries if also existing in local score file
5104     // (special case: historic score entries have an empty tape basename entry)
5105     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5106         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5107     {
5108       // special case: use server score instead of local score value if higher
5109       // (historic scores might have been truncated to 16-bit values locally)
5110       if (score_is_better)
5111         entry->score = new_entry->score;
5112
5113       return -1;
5114     }
5115
5116     if (is_better || entry_is_empty)
5117     {
5118       // player has made it to the hall of fame
5119
5120       if (i < MAX_SCORE_ENTRIES - 1)
5121       {
5122         int m = MAX_SCORE_ENTRIES - 1;
5123         int l;
5124
5125         if (one_score_entry_per_name)
5126         {
5127           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5128             if (strEqual(list->entry[l].name, new_entry->name))
5129               m = l;
5130
5131           if (m == i)   // player's new highscore overwrites his old one
5132             goto put_into_list;
5133         }
5134
5135         for (l = m; l > i; l--)
5136           list->entry[l] = list->entry[l - 1];
5137       }
5138
5139       put_into_list:
5140
5141       *entry = *new_entry;
5142
5143       return i;
5144     }
5145     else if (one_score_entry_per_name &&
5146              strEqual(entry->name, new_entry->name))
5147     {
5148       // player already in high score list with better score or time
5149
5150       return -1;
5151     }
5152   }
5153
5154   // special case: new score is beyond the last high score list position
5155   return MAX_SCORE_ENTRIES;
5156 }
5157
5158 void NewHighScore(int level_nr, boolean tape_saved)
5159 {
5160   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5161   boolean one_per_name = FALSE;
5162
5163   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5164   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5165
5166   new_entry.score = game.score_final;
5167   new_entry.time = game.score_time_final;
5168
5169   LoadScore(level_nr);
5170
5171   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5172
5173   if (scores.last_added >= MAX_SCORE_ENTRIES)
5174   {
5175     scores.last_added = MAX_SCORE_ENTRIES - 1;
5176     scores.force_last_added = TRUE;
5177
5178     scores.entry[scores.last_added] = new_entry;
5179
5180     // store last added local score entry (before merging server scores)
5181     scores.last_added_local = scores.last_added;
5182
5183     return;
5184   }
5185
5186   if (scores.last_added < 0)
5187     return;
5188
5189   SaveScore(level_nr);
5190
5191   // store last added local score entry (before merging server scores)
5192   scores.last_added_local = scores.last_added;
5193
5194   if (!game.LevelSolved_SaveTape)
5195     return;
5196
5197   SaveScoreTape(level_nr);
5198
5199   if (setup.ask_for_using_api_server)
5200   {
5201     setup.use_api_server =
5202       Request("Upload your score and tape to the high score server?", REQ_ASK);
5203
5204     if (!setup.use_api_server)
5205       Request("Not using high score server! Use setup menu to enable again!",
5206               REQ_CONFIRM);
5207
5208     runtime.use_api_server = setup.use_api_server;
5209
5210     // after asking for using API server once, do not ask again
5211     setup.ask_for_using_api_server = FALSE;
5212
5213     SaveSetup_ServerSetup();
5214   }
5215
5216   SaveServerScore(level_nr, tape_saved);
5217 }
5218
5219 void MergeServerScore(void)
5220 {
5221   struct ScoreEntry last_added_entry;
5222   boolean one_per_name = FALSE;
5223   int i;
5224
5225   if (scores.last_added >= 0)
5226     last_added_entry = scores.entry[scores.last_added];
5227
5228   for (i = 0; i < server_scores.num_entries; i++)
5229   {
5230     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5231
5232     if (pos >= 0 && pos <= scores.last_added)
5233       scores.last_added++;
5234   }
5235
5236   if (scores.last_added >= MAX_SCORE_ENTRIES)
5237   {
5238     scores.last_added = MAX_SCORE_ENTRIES - 1;
5239     scores.force_last_added = TRUE;
5240
5241     scores.entry[scores.last_added] = last_added_entry;
5242   }
5243 }
5244
5245 static int getElementMoveStepsizeExt(int x, int y, int direction)
5246 {
5247   int element = Tile[x][y];
5248   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5249   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5250   int horiz_move = (dx != 0);
5251   int sign = (horiz_move ? dx : dy);
5252   int step = sign * element_info[element].move_stepsize;
5253
5254   // special values for move stepsize for spring and things on conveyor belt
5255   if (horiz_move)
5256   {
5257     if (CAN_FALL(element) &&
5258         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5259       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5260     else if (element == EL_SPRING)
5261       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5262   }
5263
5264   return step;
5265 }
5266
5267 static int getElementMoveStepsize(int x, int y)
5268 {
5269   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5270 }
5271
5272 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5273 {
5274   if (player->GfxAction != action || player->GfxDir != dir)
5275   {
5276     player->GfxAction = action;
5277     player->GfxDir = dir;
5278     player->Frame = 0;
5279     player->StepFrame = 0;
5280   }
5281 }
5282
5283 static void ResetGfxFrame(int x, int y)
5284 {
5285   // profiling showed that "autotest" spends 10~20% of its time in this function
5286   if (DrawingDeactivatedField())
5287     return;
5288
5289   int element = Tile[x][y];
5290   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5291
5292   if (graphic_info[graphic].anim_global_sync)
5293     GfxFrame[x][y] = FrameCounter;
5294   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5295     GfxFrame[x][y] = CustomValue[x][y];
5296   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5297     GfxFrame[x][y] = element_info[element].collect_score;
5298   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5299     GfxFrame[x][y] = ChangeDelay[x][y];
5300 }
5301
5302 static void ResetGfxAnimation(int x, int y)
5303 {
5304   GfxAction[x][y] = ACTION_DEFAULT;
5305   GfxDir[x][y] = MovDir[x][y];
5306   GfxFrame[x][y] = 0;
5307
5308   ResetGfxFrame(x, y);
5309 }
5310
5311 static void ResetRandomAnimationValue(int x, int y)
5312 {
5313   GfxRandom[x][y] = INIT_GFX_RANDOM();
5314 }
5315
5316 static void InitMovingField(int x, int y, int direction)
5317 {
5318   int element = Tile[x][y];
5319   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5320   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5321   int newx = x + dx;
5322   int newy = y + dy;
5323   boolean is_moving_before, is_moving_after;
5324
5325   // check if element was/is moving or being moved before/after mode change
5326   is_moving_before = (WasJustMoving[x][y] != 0);
5327   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5328
5329   // reset animation only for moving elements which change direction of moving
5330   // or which just started or stopped moving
5331   // (else CEs with property "can move" / "not moving" are reset each frame)
5332   if (is_moving_before != is_moving_after ||
5333       direction != MovDir[x][y])
5334     ResetGfxAnimation(x, y);
5335
5336   MovDir[x][y] = direction;
5337   GfxDir[x][y] = direction;
5338
5339   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5340                      direction == MV_DOWN && CAN_FALL(element) ?
5341                      ACTION_FALLING : ACTION_MOVING);
5342
5343   // this is needed for CEs with property "can move" / "not moving"
5344
5345   if (is_moving_after)
5346   {
5347     if (Tile[newx][newy] == EL_EMPTY)
5348       Tile[newx][newy] = EL_BLOCKED;
5349
5350     MovDir[newx][newy] = MovDir[x][y];
5351
5352     CustomValue[newx][newy] = CustomValue[x][y];
5353
5354     GfxFrame[newx][newy] = GfxFrame[x][y];
5355     GfxRandom[newx][newy] = GfxRandom[x][y];
5356     GfxAction[newx][newy] = GfxAction[x][y];
5357     GfxDir[newx][newy] = GfxDir[x][y];
5358   }
5359 }
5360
5361 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5362 {
5363   int direction = MovDir[x][y];
5364   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5365   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5366
5367   *goes_to_x = newx;
5368   *goes_to_y = newy;
5369 }
5370
5371 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5372 {
5373   int oldx = x, oldy = y;
5374   int direction = MovDir[x][y];
5375
5376   if (direction == MV_LEFT)
5377     oldx++;
5378   else if (direction == MV_RIGHT)
5379     oldx--;
5380   else if (direction == MV_UP)
5381     oldy++;
5382   else if (direction == MV_DOWN)
5383     oldy--;
5384
5385   *comes_from_x = oldx;
5386   *comes_from_y = oldy;
5387 }
5388
5389 static int MovingOrBlocked2Element(int x, int y)
5390 {
5391   int element = Tile[x][y];
5392
5393   if (element == EL_BLOCKED)
5394   {
5395     int oldx, oldy;
5396
5397     Blocked2Moving(x, y, &oldx, &oldy);
5398     return Tile[oldx][oldy];
5399   }
5400   else
5401     return element;
5402 }
5403
5404 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5405 {
5406   // like MovingOrBlocked2Element(), but if element is moving
5407   // and (x,y) is the field the moving element is just leaving,
5408   // return EL_BLOCKED instead of the element value
5409   int element = Tile[x][y];
5410
5411   if (IS_MOVING(x, y))
5412   {
5413     if (element == EL_BLOCKED)
5414     {
5415       int oldx, oldy;
5416
5417       Blocked2Moving(x, y, &oldx, &oldy);
5418       return Tile[oldx][oldy];
5419     }
5420     else
5421       return EL_BLOCKED;
5422   }
5423   else
5424     return element;
5425 }
5426
5427 static void RemoveField(int x, int y)
5428 {
5429   Tile[x][y] = EL_EMPTY;
5430
5431   MovPos[x][y] = 0;
5432   MovDir[x][y] = 0;
5433   MovDelay[x][y] = 0;
5434
5435   CustomValue[x][y] = 0;
5436
5437   AmoebaNr[x][y] = 0;
5438   ChangeDelay[x][y] = 0;
5439   ChangePage[x][y] = -1;
5440   Pushed[x][y] = FALSE;
5441
5442   GfxElement[x][y] = EL_UNDEFINED;
5443   GfxAction[x][y] = ACTION_DEFAULT;
5444   GfxDir[x][y] = MV_NONE;
5445 }
5446
5447 static void RemoveMovingField(int x, int y)
5448 {
5449   int oldx = x, oldy = y, newx = x, newy = y;
5450   int element = Tile[x][y];
5451   int next_element = EL_UNDEFINED;
5452
5453   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5454     return;
5455
5456   if (IS_MOVING(x, y))
5457   {
5458     Moving2Blocked(x, y, &newx, &newy);
5459
5460     if (Tile[newx][newy] != EL_BLOCKED)
5461     {
5462       // element is moving, but target field is not free (blocked), but
5463       // already occupied by something different (example: acid pool);
5464       // in this case, only remove the moving field, but not the target
5465
5466       RemoveField(oldx, oldy);
5467
5468       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5469
5470       TEST_DrawLevelField(oldx, oldy);
5471
5472       return;
5473     }
5474   }
5475   else if (element == EL_BLOCKED)
5476   {
5477     Blocked2Moving(x, y, &oldx, &oldy);
5478     if (!IS_MOVING(oldx, oldy))
5479       return;
5480   }
5481
5482   if (element == EL_BLOCKED &&
5483       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5484        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5485        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5486        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5487        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5488        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5489     next_element = get_next_element(Tile[oldx][oldy]);
5490
5491   RemoveField(oldx, oldy);
5492   RemoveField(newx, newy);
5493
5494   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5495
5496   if (next_element != EL_UNDEFINED)
5497     Tile[oldx][oldy] = next_element;
5498
5499   TEST_DrawLevelField(oldx, oldy);
5500   TEST_DrawLevelField(newx, newy);
5501 }
5502
5503 void DrawDynamite(int x, int y)
5504 {
5505   int sx = SCREENX(x), sy = SCREENY(y);
5506   int graphic = el2img(Tile[x][y]);
5507   int frame;
5508
5509   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5510     return;
5511
5512   if (IS_WALKABLE_INSIDE(Back[x][y]))
5513     return;
5514
5515   if (Back[x][y])
5516     DrawLevelElement(x, y, Back[x][y]);
5517   else if (Store[x][y])
5518     DrawLevelElement(x, y, Store[x][y]);
5519   else if (game.use_masked_elements)
5520     DrawLevelElement(x, y, EL_EMPTY);
5521
5522   frame = getGraphicAnimationFrameXY(graphic, x, y);
5523
5524   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5525     DrawGraphicThruMask(sx, sy, graphic, frame);
5526   else
5527     DrawGraphic(sx, sy, graphic, frame);
5528 }
5529
5530 static void CheckDynamite(int x, int y)
5531 {
5532   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5533   {
5534     MovDelay[x][y]--;
5535
5536     if (MovDelay[x][y] != 0)
5537     {
5538       DrawDynamite(x, y);
5539       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5540
5541       return;
5542     }
5543   }
5544
5545   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5546
5547   Bang(x, y);
5548 }
5549
5550 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5551 {
5552   boolean num_checked_players = 0;
5553   int i;
5554
5555   for (i = 0; i < MAX_PLAYERS; i++)
5556   {
5557     if (stored_player[i].active)
5558     {
5559       int sx = stored_player[i].jx;
5560       int sy = stored_player[i].jy;
5561
5562       if (num_checked_players == 0)
5563       {
5564         *sx1 = *sx2 = sx;
5565         *sy1 = *sy2 = sy;
5566       }
5567       else
5568       {
5569         *sx1 = MIN(*sx1, sx);
5570         *sy1 = MIN(*sy1, sy);
5571         *sx2 = MAX(*sx2, sx);
5572         *sy2 = MAX(*sy2, sy);
5573       }
5574
5575       num_checked_players++;
5576     }
5577   }
5578 }
5579
5580 static boolean checkIfAllPlayersFitToScreen_RND(void)
5581 {
5582   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5583
5584   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5585
5586   return (sx2 - sx1 < SCR_FIELDX &&
5587           sy2 - sy1 < SCR_FIELDY);
5588 }
5589
5590 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5591 {
5592   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5593
5594   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5595
5596   *sx = (sx1 + sx2) / 2;
5597   *sy = (sy1 + sy2) / 2;
5598 }
5599
5600 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5601                                boolean center_screen, boolean quick_relocation)
5602 {
5603   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5604   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5605   boolean no_delay = (tape.warp_forward);
5606   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5607   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5608   int new_scroll_x, new_scroll_y;
5609
5610   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5611   {
5612     // case 1: quick relocation inside visible screen (without scrolling)
5613
5614     RedrawPlayfield();
5615
5616     return;
5617   }
5618
5619   if (!level.shifted_relocation || center_screen)
5620   {
5621     // relocation _with_ centering of screen
5622
5623     new_scroll_x = SCROLL_POSITION_X(x);
5624     new_scroll_y = SCROLL_POSITION_Y(y);
5625   }
5626   else
5627   {
5628     // relocation _without_ centering of screen
5629
5630     int center_scroll_x = SCROLL_POSITION_X(old_x);
5631     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5632     int offset_x = x + (scroll_x - center_scroll_x);
5633     int offset_y = y + (scroll_y - center_scroll_y);
5634
5635     // for new screen position, apply previous offset to center position
5636     new_scroll_x = SCROLL_POSITION_X(offset_x);
5637     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5638   }
5639
5640   if (quick_relocation)
5641   {
5642     // case 2: quick relocation (redraw without visible scrolling)
5643
5644     scroll_x = new_scroll_x;
5645     scroll_y = new_scroll_y;
5646
5647     RedrawPlayfield();
5648
5649     return;
5650   }
5651
5652   // case 3: visible relocation (with scrolling to new position)
5653
5654   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5655
5656   SetVideoFrameDelay(wait_delay_value);
5657
5658   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5659   {
5660     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5661     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5662
5663     if (dx == 0 && dy == 0)             // no scrolling needed at all
5664       break;
5665
5666     scroll_x -= dx;
5667     scroll_y -= dy;
5668
5669     // set values for horizontal/vertical screen scrolling (half tile size)
5670     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5671     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5672     int pos_x = dx * TILEX / 2;
5673     int pos_y = dy * TILEY / 2;
5674     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5675     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5676
5677     ScrollLevel(dx, dy);
5678     DrawAllPlayers();
5679
5680     // scroll in two steps of half tile size to make things smoother
5681     BlitScreenToBitmapExt_RND(window, fx, fy);
5682
5683     // scroll second step to align at full tile size
5684     BlitScreenToBitmap(window);
5685   }
5686
5687   DrawAllPlayers();
5688   BackToFront();
5689
5690   SetVideoFrameDelay(frame_delay_value_old);
5691 }
5692
5693 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5694 {
5695   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5696   int player_nr = GET_PLAYER_NR(el_player);
5697   struct PlayerInfo *player = &stored_player[player_nr];
5698   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5699   boolean no_delay = (tape.warp_forward);
5700   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5701   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5702   int old_jx = player->jx;
5703   int old_jy = player->jy;
5704   int old_element = Tile[old_jx][old_jy];
5705   int element = Tile[jx][jy];
5706   boolean player_relocated = (old_jx != jx || old_jy != jy);
5707
5708   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5709   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5710   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5711   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5712   int leave_side_horiz = move_dir_horiz;
5713   int leave_side_vert  = move_dir_vert;
5714   int enter_side = enter_side_horiz | enter_side_vert;
5715   int leave_side = leave_side_horiz | leave_side_vert;
5716
5717   if (player->buried)           // do not reanimate dead player
5718     return;
5719
5720   if (!player_relocated)        // no need to relocate the player
5721     return;
5722
5723   if (IS_PLAYER(jx, jy))        // player already placed at new position
5724   {
5725     RemoveField(jx, jy);        // temporarily remove newly placed player
5726     DrawLevelField(jx, jy);
5727   }
5728
5729   if (player->present)
5730   {
5731     while (player->MovPos)
5732     {
5733       ScrollPlayer(player, SCROLL_GO_ON);
5734       ScrollScreen(NULL, SCROLL_GO_ON);
5735
5736       AdvanceFrameAndPlayerCounters(player->index_nr);
5737
5738       DrawPlayer(player);
5739
5740       BackToFront_WithFrameDelay(wait_delay_value);
5741     }
5742
5743     DrawPlayer(player);         // needed here only to cleanup last field
5744     DrawLevelField(player->jx, player->jy);     // remove player graphic
5745
5746     player->is_moving = FALSE;
5747   }
5748
5749   if (IS_CUSTOM_ELEMENT(old_element))
5750     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5751                                CE_LEFT_BY_PLAYER,
5752                                player->index_bit, leave_side);
5753
5754   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5755                                       CE_PLAYER_LEAVES_X,
5756                                       player->index_bit, leave_side);
5757
5758   Tile[jx][jy] = el_player;
5759   InitPlayerField(jx, jy, el_player, TRUE);
5760
5761   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5762      possible that the relocation target field did not contain a player element,
5763      but a walkable element, to which the new player was relocated -- in this
5764      case, restore that (already initialized!) element on the player field */
5765   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5766   {
5767     Tile[jx][jy] = element;     // restore previously existing element
5768   }
5769
5770   // only visually relocate centered player
5771   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5772                      FALSE, level.instant_relocation);
5773
5774   TestIfPlayerTouchesBadThing(jx, jy);
5775   TestIfPlayerTouchesCustomElement(jx, jy);
5776
5777   if (IS_CUSTOM_ELEMENT(element))
5778     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5779                                player->index_bit, enter_side);
5780
5781   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5782                                       player->index_bit, enter_side);
5783
5784   if (player->is_switching)
5785   {
5786     /* ensure that relocation while still switching an element does not cause
5787        a new element to be treated as also switched directly after relocation
5788        (this is important for teleporter switches that teleport the player to
5789        a place where another teleporter switch is in the same direction, which
5790        would then incorrectly be treated as immediately switched before the
5791        direction key that caused the switch was released) */
5792
5793     player->switch_x += jx - old_jx;
5794     player->switch_y += jy - old_jy;
5795   }
5796 }
5797
5798 static void Explode(int ex, int ey, int phase, int mode)
5799 {
5800   int x, y;
5801   int last_phase;
5802   int border_element;
5803
5804   // !!! eliminate this variable !!!
5805   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5806
5807   if (game.explosions_delayed)
5808   {
5809     ExplodeField[ex][ey] = mode;
5810     return;
5811   }
5812
5813   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5814   {
5815     int center_element = Tile[ex][ey];
5816     int artwork_element, explosion_element;     // set these values later
5817
5818     // remove things displayed in background while burning dynamite
5819     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5820       Back[ex][ey] = 0;
5821
5822     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5823     {
5824       // put moving element to center field (and let it explode there)
5825       center_element = MovingOrBlocked2Element(ex, ey);
5826       RemoveMovingField(ex, ey);
5827       Tile[ex][ey] = center_element;
5828     }
5829
5830     // now "center_element" is finally determined -- set related values now
5831     artwork_element = center_element;           // for custom player artwork
5832     explosion_element = center_element;         // for custom player artwork
5833
5834     if (IS_PLAYER(ex, ey))
5835     {
5836       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5837
5838       artwork_element = stored_player[player_nr].artwork_element;
5839
5840       if (level.use_explosion_element[player_nr])
5841       {
5842         explosion_element = level.explosion_element[player_nr];
5843         artwork_element = explosion_element;
5844       }
5845     }
5846
5847     if (mode == EX_TYPE_NORMAL ||
5848         mode == EX_TYPE_CENTER ||
5849         mode == EX_TYPE_CROSS)
5850       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5851
5852     last_phase = element_info[explosion_element].explosion_delay + 1;
5853
5854     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5855     {
5856       int xx = x - ex + 1;
5857       int yy = y - ey + 1;
5858       int element;
5859
5860       if (!IN_LEV_FIELD(x, y) ||
5861           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5862           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5863         continue;
5864
5865       element = Tile[x][y];
5866
5867       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5868       {
5869         element = MovingOrBlocked2Element(x, y);
5870
5871         if (!IS_EXPLOSION_PROOF(element))
5872           RemoveMovingField(x, y);
5873       }
5874
5875       // indestructible elements can only explode in center (but not flames)
5876       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5877                                            mode == EX_TYPE_BORDER)) ||
5878           element == EL_FLAMES)
5879         continue;
5880
5881       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5882          behaviour, for example when touching a yamyam that explodes to rocks
5883          with active deadly shield, a rock is created under the player !!! */
5884       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5885 #if 0
5886       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5887           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5888            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5889 #else
5890       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5891 #endif
5892       {
5893         if (IS_ACTIVE_BOMB(element))
5894         {
5895           // re-activate things under the bomb like gate or penguin
5896           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5897           Back[x][y] = 0;
5898         }
5899
5900         continue;
5901       }
5902
5903       // save walkable background elements while explosion on same tile
5904       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5905           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5906         Back[x][y] = element;
5907
5908       // ignite explodable elements reached by other explosion
5909       if (element == EL_EXPLOSION)
5910         element = Store2[x][y];
5911
5912       if (AmoebaNr[x][y] &&
5913           (element == EL_AMOEBA_FULL ||
5914            element == EL_BD_AMOEBA ||
5915            element == EL_AMOEBA_GROWING))
5916       {
5917         AmoebaCnt[AmoebaNr[x][y]]--;
5918         AmoebaCnt2[AmoebaNr[x][y]]--;
5919       }
5920
5921       RemoveField(x, y);
5922
5923       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5924       {
5925         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5926
5927         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5928
5929         if (PLAYERINFO(ex, ey)->use_murphy)
5930           Store[x][y] = EL_EMPTY;
5931       }
5932
5933       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5934       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5935       else if (IS_PLAYER_ELEMENT(center_element))
5936         Store[x][y] = EL_EMPTY;
5937       else if (center_element == EL_YAMYAM)
5938         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5939       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5940         Store[x][y] = element_info[center_element].content.e[xx][yy];
5941 #if 1
5942       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5943       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5944       // otherwise) -- FIX THIS !!!
5945       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5946         Store[x][y] = element_info[element].content.e[1][1];
5947 #else
5948       else if (!CAN_EXPLODE(element))
5949         Store[x][y] = element_info[element].content.e[1][1];
5950 #endif
5951       else
5952         Store[x][y] = EL_EMPTY;
5953
5954       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5955           center_element == EL_AMOEBA_TO_DIAMOND)
5956         Store2[x][y] = element;
5957
5958       Tile[x][y] = EL_EXPLOSION;
5959       GfxElement[x][y] = artwork_element;
5960
5961       ExplodePhase[x][y] = 1;
5962       ExplodeDelay[x][y] = last_phase;
5963
5964       Stop[x][y] = TRUE;
5965     }
5966
5967     if (center_element == EL_YAMYAM)
5968       game.yamyam_content_nr =
5969         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5970
5971     return;
5972   }
5973
5974   if (Stop[ex][ey])
5975     return;
5976
5977   x = ex;
5978   y = ey;
5979
5980   if (phase == 1)
5981     GfxFrame[x][y] = 0;         // restart explosion animation
5982
5983   last_phase = ExplodeDelay[x][y];
5984
5985   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5986
5987   // this can happen if the player leaves an explosion just in time
5988   if (GfxElement[x][y] == EL_UNDEFINED)
5989     GfxElement[x][y] = EL_EMPTY;
5990
5991   border_element = Store2[x][y];
5992   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5993     border_element = StorePlayer[x][y];
5994
5995   if (phase == element_info[border_element].ignition_delay ||
5996       phase == last_phase)
5997   {
5998     boolean border_explosion = FALSE;
5999
6000     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6001         !PLAYER_EXPLOSION_PROTECTED(x, y))
6002     {
6003       KillPlayerUnlessExplosionProtected(x, y);
6004       border_explosion = TRUE;
6005     }
6006     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6007     {
6008       Tile[x][y] = Store2[x][y];
6009       Store2[x][y] = 0;
6010       Bang(x, y);
6011       border_explosion = TRUE;
6012     }
6013     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6014     {
6015       AmoebaToDiamond(x, y);
6016       Store2[x][y] = 0;
6017       border_explosion = TRUE;
6018     }
6019
6020     // if an element just explodes due to another explosion (chain-reaction),
6021     // do not immediately end the new explosion when it was the last frame of
6022     // the explosion (as it would be done in the following "if"-statement!)
6023     if (border_explosion && phase == last_phase)
6024       return;
6025   }
6026
6027   // this can happen if the player was just killed by an explosion
6028   if (GfxElement[x][y] == EL_UNDEFINED)
6029     GfxElement[x][y] = EL_EMPTY;
6030
6031   if (phase == last_phase)
6032   {
6033     int element;
6034
6035     element = Tile[x][y] = Store[x][y];
6036     Store[x][y] = Store2[x][y] = 0;
6037     GfxElement[x][y] = EL_UNDEFINED;
6038
6039     // player can escape from explosions and might therefore be still alive
6040     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6041         element <= EL_PLAYER_IS_EXPLODING_4)
6042     {
6043       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6044       int explosion_element = EL_PLAYER_1 + player_nr;
6045       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6046       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6047
6048       if (level.use_explosion_element[player_nr])
6049         explosion_element = level.explosion_element[player_nr];
6050
6051       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6052                     element_info[explosion_element].content.e[xx][yy]);
6053     }
6054
6055     // restore probably existing indestructible background element
6056     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6057       element = Tile[x][y] = Back[x][y];
6058     Back[x][y] = 0;
6059
6060     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6061     GfxDir[x][y] = MV_NONE;
6062     ChangeDelay[x][y] = 0;
6063     ChangePage[x][y] = -1;
6064
6065     CustomValue[x][y] = 0;
6066
6067     InitField_WithBug2(x, y, FALSE);
6068
6069     TEST_DrawLevelField(x, y);
6070
6071     TestIfElementTouchesCustomElement(x, y);
6072
6073     if (GFX_CRUMBLED(element))
6074       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6075
6076     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6077       StorePlayer[x][y] = 0;
6078
6079     if (IS_PLAYER_ELEMENT(element))
6080       RelocatePlayer(x, y, element);
6081   }
6082   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6083   {
6084     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6085     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6086
6087     if (phase == delay)
6088       TEST_DrawLevelFieldCrumbled(x, y);
6089
6090     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6091     {
6092       DrawLevelElement(x, y, Back[x][y]);
6093       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6094     }
6095     else if (IS_WALKABLE_UNDER(Back[x][y]))
6096     {
6097       DrawLevelGraphic(x, y, graphic, frame);
6098       DrawLevelElementThruMask(x, y, Back[x][y]);
6099     }
6100     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6101       DrawLevelGraphic(x, y, graphic, frame);
6102   }
6103 }
6104
6105 static void DynaExplode(int ex, int ey)
6106 {
6107   int i, j;
6108   int dynabomb_element = Tile[ex][ey];
6109   int dynabomb_size = 1;
6110   boolean dynabomb_xl = FALSE;
6111   struct PlayerInfo *player;
6112   static int xy[4][2] =
6113   {
6114     { 0, -1 },
6115     { -1, 0 },
6116     { +1, 0 },
6117     { 0, +1 }
6118   };
6119
6120   if (IS_ACTIVE_BOMB(dynabomb_element))
6121   {
6122     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6123     dynabomb_size = player->dynabomb_size;
6124     dynabomb_xl = player->dynabomb_xl;
6125     player->dynabombs_left++;
6126   }
6127
6128   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6129
6130   for (i = 0; i < NUM_DIRECTIONS; i++)
6131   {
6132     for (j = 1; j <= dynabomb_size; j++)
6133     {
6134       int x = ex + j * xy[i][0];
6135       int y = ey + j * xy[i][1];
6136       int element;
6137
6138       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6139         break;
6140
6141       element = Tile[x][y];
6142
6143       // do not restart explosions of fields with active bombs
6144       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6145         continue;
6146
6147       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6148
6149       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6150           !IS_DIGGABLE(element) && !dynabomb_xl)
6151         break;
6152     }
6153   }
6154 }
6155
6156 void Bang(int x, int y)
6157 {
6158   int element = MovingOrBlocked2Element(x, y);
6159   int explosion_type = EX_TYPE_NORMAL;
6160
6161   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6162   {
6163     struct PlayerInfo *player = PLAYERINFO(x, y);
6164
6165     element = Tile[x][y] = player->initial_element;
6166
6167     if (level.use_explosion_element[player->index_nr])
6168     {
6169       int explosion_element = level.explosion_element[player->index_nr];
6170
6171       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6172         explosion_type = EX_TYPE_CROSS;
6173       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6174         explosion_type = EX_TYPE_CENTER;
6175     }
6176   }
6177
6178   switch (element)
6179   {
6180     case EL_BUG:
6181     case EL_SPACESHIP:
6182     case EL_BD_BUTTERFLY:
6183     case EL_BD_FIREFLY:
6184     case EL_YAMYAM:
6185     case EL_DARK_YAMYAM:
6186     case EL_ROBOT:
6187     case EL_PACMAN:
6188     case EL_MOLE:
6189       RaiseScoreElement(element);
6190       break;
6191
6192     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6193     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6194     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6195     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6196     case EL_DYNABOMB_INCREASE_NUMBER:
6197     case EL_DYNABOMB_INCREASE_SIZE:
6198     case EL_DYNABOMB_INCREASE_POWER:
6199       explosion_type = EX_TYPE_DYNA;
6200       break;
6201
6202     case EL_DC_LANDMINE:
6203       explosion_type = EX_TYPE_CENTER;
6204       break;
6205
6206     case EL_PENGUIN:
6207     case EL_LAMP:
6208     case EL_LAMP_ACTIVE:
6209     case EL_AMOEBA_TO_DIAMOND:
6210       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6211         explosion_type = EX_TYPE_CENTER;
6212       break;
6213
6214     default:
6215       if (element_info[element].explosion_type == EXPLODES_CROSS)
6216         explosion_type = EX_TYPE_CROSS;
6217       else if (element_info[element].explosion_type == EXPLODES_1X1)
6218         explosion_type = EX_TYPE_CENTER;
6219       break;
6220   }
6221
6222   if (explosion_type == EX_TYPE_DYNA)
6223     DynaExplode(x, y);
6224   else
6225     Explode(x, y, EX_PHASE_START, explosion_type);
6226
6227   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6228 }
6229
6230 static void SplashAcid(int x, int y)
6231 {
6232   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6233       (!IN_LEV_FIELD(x - 1, y - 2) ||
6234        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6235     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6236
6237   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6238       (!IN_LEV_FIELD(x + 1, y - 2) ||
6239        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6240     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6241
6242   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6243 }
6244
6245 static void InitBeltMovement(void)
6246 {
6247   static int belt_base_element[4] =
6248   {
6249     EL_CONVEYOR_BELT_1_LEFT,
6250     EL_CONVEYOR_BELT_2_LEFT,
6251     EL_CONVEYOR_BELT_3_LEFT,
6252     EL_CONVEYOR_BELT_4_LEFT
6253   };
6254   static int belt_base_active_element[4] =
6255   {
6256     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6257     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6258     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6259     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6260   };
6261
6262   int x, y, i, j;
6263
6264   // set frame order for belt animation graphic according to belt direction
6265   for (i = 0; i < NUM_BELTS; i++)
6266   {
6267     int belt_nr = i;
6268
6269     for (j = 0; j < NUM_BELT_PARTS; j++)
6270     {
6271       int element = belt_base_active_element[belt_nr] + j;
6272       int graphic_1 = el2img(element);
6273       int graphic_2 = el2panelimg(element);
6274
6275       if (game.belt_dir[i] == MV_LEFT)
6276       {
6277         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6278         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6279       }
6280       else
6281       {
6282         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6283         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6284       }
6285     }
6286   }
6287
6288   SCAN_PLAYFIELD(x, y)
6289   {
6290     int element = Tile[x][y];
6291
6292     for (i = 0; i < NUM_BELTS; i++)
6293     {
6294       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6295       {
6296         int e_belt_nr = getBeltNrFromBeltElement(element);
6297         int belt_nr = i;
6298
6299         if (e_belt_nr == belt_nr)
6300         {
6301           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6302
6303           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6304         }
6305       }
6306     }
6307   }
6308 }
6309
6310 static void ToggleBeltSwitch(int x, int y)
6311 {
6312   static int belt_base_element[4] =
6313   {
6314     EL_CONVEYOR_BELT_1_LEFT,
6315     EL_CONVEYOR_BELT_2_LEFT,
6316     EL_CONVEYOR_BELT_3_LEFT,
6317     EL_CONVEYOR_BELT_4_LEFT
6318   };
6319   static int belt_base_active_element[4] =
6320   {
6321     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6322     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6323     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6324     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6325   };
6326   static int belt_base_switch_element[4] =
6327   {
6328     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6329     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6330     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6331     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6332   };
6333   static int belt_move_dir[4] =
6334   {
6335     MV_LEFT,
6336     MV_NONE,
6337     MV_RIGHT,
6338     MV_NONE,
6339   };
6340
6341   int element = Tile[x][y];
6342   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6343   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6344   int belt_dir = belt_move_dir[belt_dir_nr];
6345   int xx, yy, i;
6346
6347   if (!IS_BELT_SWITCH(element))
6348     return;
6349
6350   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6351   game.belt_dir[belt_nr] = belt_dir;
6352
6353   if (belt_dir_nr == 3)
6354     belt_dir_nr = 1;
6355
6356   // set frame order for belt animation graphic according to belt direction
6357   for (i = 0; i < NUM_BELT_PARTS; i++)
6358   {
6359     int element = belt_base_active_element[belt_nr] + i;
6360     int graphic_1 = el2img(element);
6361     int graphic_2 = el2panelimg(element);
6362
6363     if (belt_dir == MV_LEFT)
6364     {
6365       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6366       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6367     }
6368     else
6369     {
6370       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6371       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6372     }
6373   }
6374
6375   SCAN_PLAYFIELD(xx, yy)
6376   {
6377     int element = Tile[xx][yy];
6378
6379     if (IS_BELT_SWITCH(element))
6380     {
6381       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6382
6383       if (e_belt_nr == belt_nr)
6384       {
6385         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6386         TEST_DrawLevelField(xx, yy);
6387       }
6388     }
6389     else if (IS_BELT(element) && belt_dir != MV_NONE)
6390     {
6391       int e_belt_nr = getBeltNrFromBeltElement(element);
6392
6393       if (e_belt_nr == belt_nr)
6394       {
6395         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6396
6397         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6398         TEST_DrawLevelField(xx, yy);
6399       }
6400     }
6401     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6402     {
6403       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6404
6405       if (e_belt_nr == belt_nr)
6406       {
6407         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6408
6409         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6410         TEST_DrawLevelField(xx, yy);
6411       }
6412     }
6413   }
6414 }
6415
6416 static void ToggleSwitchgateSwitch(int x, int y)
6417 {
6418   int xx, yy;
6419
6420   game.switchgate_pos = !game.switchgate_pos;
6421
6422   SCAN_PLAYFIELD(xx, yy)
6423   {
6424     int element = Tile[xx][yy];
6425
6426     if (element == EL_SWITCHGATE_SWITCH_UP)
6427     {
6428       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6429       TEST_DrawLevelField(xx, yy);
6430     }
6431     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6432     {
6433       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6434       TEST_DrawLevelField(xx, yy);
6435     }
6436     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6437     {
6438       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6439       TEST_DrawLevelField(xx, yy);
6440     }
6441     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6442     {
6443       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6444       TEST_DrawLevelField(xx, yy);
6445     }
6446     else if (element == EL_SWITCHGATE_OPEN ||
6447              element == EL_SWITCHGATE_OPENING)
6448     {
6449       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6450
6451       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6452     }
6453     else if (element == EL_SWITCHGATE_CLOSED ||
6454              element == EL_SWITCHGATE_CLOSING)
6455     {
6456       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6457
6458       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6459     }
6460   }
6461 }
6462
6463 static int getInvisibleActiveFromInvisibleElement(int element)
6464 {
6465   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6466           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6467           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6468           element);
6469 }
6470
6471 static int getInvisibleFromInvisibleActiveElement(int element)
6472 {
6473   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6474           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6475           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6476           element);
6477 }
6478
6479 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6480 {
6481   int x, y;
6482
6483   SCAN_PLAYFIELD(x, y)
6484   {
6485     int element = Tile[x][y];
6486
6487     if (element == EL_LIGHT_SWITCH &&
6488         game.light_time_left > 0)
6489     {
6490       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6491       TEST_DrawLevelField(x, y);
6492     }
6493     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6494              game.light_time_left == 0)
6495     {
6496       Tile[x][y] = EL_LIGHT_SWITCH;
6497       TEST_DrawLevelField(x, y);
6498     }
6499     else if (element == EL_EMC_DRIPPER &&
6500              game.light_time_left > 0)
6501     {
6502       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6503       TEST_DrawLevelField(x, y);
6504     }
6505     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6506              game.light_time_left == 0)
6507     {
6508       Tile[x][y] = EL_EMC_DRIPPER;
6509       TEST_DrawLevelField(x, y);
6510     }
6511     else if (element == EL_INVISIBLE_STEELWALL ||
6512              element == EL_INVISIBLE_WALL ||
6513              element == EL_INVISIBLE_SAND)
6514     {
6515       if (game.light_time_left > 0)
6516         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6517
6518       TEST_DrawLevelField(x, y);
6519
6520       // uncrumble neighbour fields, if needed
6521       if (element == EL_INVISIBLE_SAND)
6522         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6523     }
6524     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6525              element == EL_INVISIBLE_WALL_ACTIVE ||
6526              element == EL_INVISIBLE_SAND_ACTIVE)
6527     {
6528       if (game.light_time_left == 0)
6529         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6530
6531       TEST_DrawLevelField(x, y);
6532
6533       // re-crumble neighbour fields, if needed
6534       if (element == EL_INVISIBLE_SAND)
6535         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6536     }
6537   }
6538 }
6539
6540 static void RedrawAllInvisibleElementsForLenses(void)
6541 {
6542   int x, y;
6543
6544   SCAN_PLAYFIELD(x, y)
6545   {
6546     int element = Tile[x][y];
6547
6548     if (element == EL_EMC_DRIPPER &&
6549         game.lenses_time_left > 0)
6550     {
6551       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6552       TEST_DrawLevelField(x, y);
6553     }
6554     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6555              game.lenses_time_left == 0)
6556     {
6557       Tile[x][y] = EL_EMC_DRIPPER;
6558       TEST_DrawLevelField(x, y);
6559     }
6560     else if (element == EL_INVISIBLE_STEELWALL ||
6561              element == EL_INVISIBLE_WALL ||
6562              element == EL_INVISIBLE_SAND)
6563     {
6564       if (game.lenses_time_left > 0)
6565         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6566
6567       TEST_DrawLevelField(x, y);
6568
6569       // uncrumble neighbour fields, if needed
6570       if (element == EL_INVISIBLE_SAND)
6571         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6572     }
6573     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6574              element == EL_INVISIBLE_WALL_ACTIVE ||
6575              element == EL_INVISIBLE_SAND_ACTIVE)
6576     {
6577       if (game.lenses_time_left == 0)
6578         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6579
6580       TEST_DrawLevelField(x, y);
6581
6582       // re-crumble neighbour fields, if needed
6583       if (element == EL_INVISIBLE_SAND)
6584         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6585     }
6586   }
6587 }
6588
6589 static void RedrawAllInvisibleElementsForMagnifier(void)
6590 {
6591   int x, y;
6592
6593   SCAN_PLAYFIELD(x, y)
6594   {
6595     int element = Tile[x][y];
6596
6597     if (element == EL_EMC_FAKE_GRASS &&
6598         game.magnify_time_left > 0)
6599     {
6600       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6601       TEST_DrawLevelField(x, y);
6602     }
6603     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6604              game.magnify_time_left == 0)
6605     {
6606       Tile[x][y] = EL_EMC_FAKE_GRASS;
6607       TEST_DrawLevelField(x, y);
6608     }
6609     else if (IS_GATE_GRAY(element) &&
6610              game.magnify_time_left > 0)
6611     {
6612       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6613                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6614                     IS_EM_GATE_GRAY(element) ?
6615                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6616                     IS_EMC_GATE_GRAY(element) ?
6617                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6618                     IS_DC_GATE_GRAY(element) ?
6619                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6620                     element);
6621       TEST_DrawLevelField(x, y);
6622     }
6623     else if (IS_GATE_GRAY_ACTIVE(element) &&
6624              game.magnify_time_left == 0)
6625     {
6626       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6627                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6628                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6629                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6630                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6631                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6632                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6633                     EL_DC_GATE_WHITE_GRAY :
6634                     element);
6635       TEST_DrawLevelField(x, y);
6636     }
6637   }
6638 }
6639
6640 static void ToggleLightSwitch(int x, int y)
6641 {
6642   int element = Tile[x][y];
6643
6644   game.light_time_left =
6645     (element == EL_LIGHT_SWITCH ?
6646      level.time_light * FRAMES_PER_SECOND : 0);
6647
6648   RedrawAllLightSwitchesAndInvisibleElements();
6649 }
6650
6651 static void ActivateTimegateSwitch(int x, int y)
6652 {
6653   int xx, yy;
6654
6655   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6656
6657   SCAN_PLAYFIELD(xx, yy)
6658   {
6659     int element = Tile[xx][yy];
6660
6661     if (element == EL_TIMEGATE_CLOSED ||
6662         element == EL_TIMEGATE_CLOSING)
6663     {
6664       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6665       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6666     }
6667
6668     /*
6669     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6670     {
6671       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6672       TEST_DrawLevelField(xx, yy);
6673     }
6674     */
6675
6676   }
6677
6678   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6679                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6680 }
6681
6682 static void Impact(int x, int y)
6683 {
6684   boolean last_line = (y == lev_fieldy - 1);
6685   boolean object_hit = FALSE;
6686   boolean impact = (last_line || object_hit);
6687   int element = Tile[x][y];
6688   int smashed = EL_STEELWALL;
6689
6690   if (!last_line)       // check if element below was hit
6691   {
6692     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6693       return;
6694
6695     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6696                                          MovDir[x][y + 1] != MV_DOWN ||
6697                                          MovPos[x][y + 1] <= TILEY / 2));
6698
6699     // do not smash moving elements that left the smashed field in time
6700     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6701         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6702       object_hit = FALSE;
6703
6704 #if USE_QUICKSAND_IMPACT_BUGFIX
6705     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6706     {
6707       RemoveMovingField(x, y + 1);
6708       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6709       Tile[x][y + 2] = EL_ROCK;
6710       TEST_DrawLevelField(x, y + 2);
6711
6712       object_hit = TRUE;
6713     }
6714
6715     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6716     {
6717       RemoveMovingField(x, y + 1);
6718       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6719       Tile[x][y + 2] = EL_ROCK;
6720       TEST_DrawLevelField(x, y + 2);
6721
6722       object_hit = TRUE;
6723     }
6724 #endif
6725
6726     if (object_hit)
6727       smashed = MovingOrBlocked2Element(x, y + 1);
6728
6729     impact = (last_line || object_hit);
6730   }
6731
6732   if (!last_line && smashed == EL_ACID) // element falls into acid
6733   {
6734     SplashAcid(x, y + 1);
6735     return;
6736   }
6737
6738   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6739   // only reset graphic animation if graphic really changes after impact
6740   if (impact &&
6741       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6742   {
6743     ResetGfxAnimation(x, y);
6744     TEST_DrawLevelField(x, y);
6745   }
6746
6747   if (impact && CAN_EXPLODE_IMPACT(element))
6748   {
6749     Bang(x, y);
6750     return;
6751   }
6752   else if (impact && element == EL_PEARL &&
6753            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6754   {
6755     ResetGfxAnimation(x, y);
6756
6757     Tile[x][y] = EL_PEARL_BREAKING;
6758     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6759     return;
6760   }
6761   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6762   {
6763     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6764
6765     return;
6766   }
6767
6768   if (impact && element == EL_AMOEBA_DROP)
6769   {
6770     if (object_hit && IS_PLAYER(x, y + 1))
6771       KillPlayerUnlessEnemyProtected(x, y + 1);
6772     else if (object_hit && smashed == EL_PENGUIN)
6773       Bang(x, y + 1);
6774     else
6775     {
6776       Tile[x][y] = EL_AMOEBA_GROWING;
6777       Store[x][y] = EL_AMOEBA_WET;
6778
6779       ResetRandomAnimationValue(x, y);
6780     }
6781     return;
6782   }
6783
6784   if (object_hit)               // check which object was hit
6785   {
6786     if ((CAN_PASS_MAGIC_WALL(element) && 
6787          (smashed == EL_MAGIC_WALL ||
6788           smashed == EL_BD_MAGIC_WALL)) ||
6789         (CAN_PASS_DC_MAGIC_WALL(element) &&
6790          smashed == EL_DC_MAGIC_WALL))
6791     {
6792       int xx, yy;
6793       int activated_magic_wall =
6794         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6795          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6796          EL_DC_MAGIC_WALL_ACTIVE);
6797
6798       // activate magic wall / mill
6799       SCAN_PLAYFIELD(xx, yy)
6800       {
6801         if (Tile[xx][yy] == smashed)
6802           Tile[xx][yy] = activated_magic_wall;
6803       }
6804
6805       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6806       game.magic_wall_active = TRUE;
6807
6808       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6809                             SND_MAGIC_WALL_ACTIVATING :
6810                             smashed == EL_BD_MAGIC_WALL ?
6811                             SND_BD_MAGIC_WALL_ACTIVATING :
6812                             SND_DC_MAGIC_WALL_ACTIVATING));
6813     }
6814
6815     if (IS_PLAYER(x, y + 1))
6816     {
6817       if (CAN_SMASH_PLAYER(element))
6818       {
6819         KillPlayerUnlessEnemyProtected(x, y + 1);
6820         return;
6821       }
6822     }
6823     else if (smashed == EL_PENGUIN)
6824     {
6825       if (CAN_SMASH_PLAYER(element))
6826       {
6827         Bang(x, y + 1);
6828         return;
6829       }
6830     }
6831     else if (element == EL_BD_DIAMOND)
6832     {
6833       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6834       {
6835         Bang(x, y + 1);
6836         return;
6837       }
6838     }
6839     else if (((element == EL_SP_INFOTRON ||
6840                element == EL_SP_ZONK) &&
6841               (smashed == EL_SP_SNIKSNAK ||
6842                smashed == EL_SP_ELECTRON ||
6843                smashed == EL_SP_DISK_ORANGE)) ||
6844              (element == EL_SP_INFOTRON &&
6845               smashed == EL_SP_DISK_YELLOW))
6846     {
6847       Bang(x, y + 1);
6848       return;
6849     }
6850     else if (CAN_SMASH_EVERYTHING(element))
6851     {
6852       if (IS_CLASSIC_ENEMY(smashed) ||
6853           CAN_EXPLODE_SMASHED(smashed))
6854       {
6855         Bang(x, y + 1);
6856         return;
6857       }
6858       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6859       {
6860         if (smashed == EL_LAMP ||
6861             smashed == EL_LAMP_ACTIVE)
6862         {
6863           Bang(x, y + 1);
6864           return;
6865         }
6866         else if (smashed == EL_NUT)
6867         {
6868           Tile[x][y + 1] = EL_NUT_BREAKING;
6869           PlayLevelSound(x, y, SND_NUT_BREAKING);
6870           RaiseScoreElement(EL_NUT);
6871           return;
6872         }
6873         else if (smashed == EL_PEARL)
6874         {
6875           ResetGfxAnimation(x, y);
6876
6877           Tile[x][y + 1] = EL_PEARL_BREAKING;
6878           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6879           return;
6880         }
6881         else if (smashed == EL_DIAMOND)
6882         {
6883           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6884           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6885           return;
6886         }
6887         else if (IS_BELT_SWITCH(smashed))
6888         {
6889           ToggleBeltSwitch(x, y + 1);
6890         }
6891         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6892                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6893                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6894                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6895         {
6896           ToggleSwitchgateSwitch(x, y + 1);
6897         }
6898         else if (smashed == EL_LIGHT_SWITCH ||
6899                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6900         {
6901           ToggleLightSwitch(x, y + 1);
6902         }
6903         else
6904         {
6905           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6906
6907           CheckElementChangeBySide(x, y + 1, smashed, element,
6908                                    CE_SWITCHED, CH_SIDE_TOP);
6909           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6910                                             CH_SIDE_TOP);
6911         }
6912       }
6913       else
6914       {
6915         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6916       }
6917     }
6918   }
6919
6920   // play sound of magic wall / mill
6921   if (!last_line &&
6922       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6923        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6924        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6925   {
6926     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6927       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6928     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6929       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6930     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6931       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6932
6933     return;
6934   }
6935
6936   // play sound of object that hits the ground
6937   if (last_line || object_hit)
6938     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6939 }
6940
6941 static void TurnRoundExt(int x, int y)
6942 {
6943   static struct
6944   {
6945     int dx, dy;
6946   } move_xy[] =
6947   {
6948     {  0,  0 },
6949     { -1,  0 },
6950     { +1,  0 },
6951     {  0,  0 },
6952     {  0, -1 },
6953     {  0,  0 }, { 0, 0 }, { 0, 0 },
6954     {  0, +1 }
6955   };
6956   static struct
6957   {
6958     int left, right, back;
6959   } turn[] =
6960   {
6961     { 0,        0,              0        },
6962     { MV_DOWN,  MV_UP,          MV_RIGHT },
6963     { MV_UP,    MV_DOWN,        MV_LEFT  },
6964     { 0,        0,              0        },
6965     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6966     { 0,        0,              0        },
6967     { 0,        0,              0        },
6968     { 0,        0,              0        },
6969     { MV_RIGHT, MV_LEFT,        MV_UP    }
6970   };
6971
6972   int element = Tile[x][y];
6973   int move_pattern = element_info[element].move_pattern;
6974
6975   int old_move_dir = MovDir[x][y];
6976   int left_dir  = turn[old_move_dir].left;
6977   int right_dir = turn[old_move_dir].right;
6978   int back_dir  = turn[old_move_dir].back;
6979
6980   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6981   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6982   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6983   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6984
6985   int left_x  = x + left_dx,  left_y  = y + left_dy;
6986   int right_x = x + right_dx, right_y = y + right_dy;
6987   int move_x  = x + move_dx,  move_y  = y + move_dy;
6988
6989   int xx, yy;
6990
6991   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6992   {
6993     TestIfBadThingTouchesOtherBadThing(x, y);
6994
6995     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6996       MovDir[x][y] = right_dir;
6997     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998       MovDir[x][y] = left_dir;
6999
7000     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7001       MovDelay[x][y] = 9;
7002     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7003       MovDelay[x][y] = 1;
7004   }
7005   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7010       MovDir[x][y] = left_dir;
7011     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7012       MovDir[x][y] = right_dir;
7013
7014     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7017       MovDelay[x][y] = 1;
7018   }
7019   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7020   {
7021     TestIfBadThingTouchesOtherBadThing(x, y);
7022
7023     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7024       MovDir[x][y] = left_dir;
7025     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7026       MovDir[x][y] = right_dir;
7027
7028     if (MovDir[x][y] != old_move_dir)
7029       MovDelay[x][y] = 9;
7030   }
7031   else if (element == EL_YAMYAM)
7032   {
7033     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7034     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7035
7036     if (can_turn_left && can_turn_right)
7037       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7038     else if (can_turn_left)
7039       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7040     else if (can_turn_right)
7041       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7042     else
7043       MovDir[x][y] = back_dir;
7044
7045     MovDelay[x][y] = 16 + 16 * RND(3);
7046   }
7047   else if (element == EL_DARK_YAMYAM)
7048   {
7049     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7050                                                          left_x, left_y);
7051     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7052                                                          right_x, right_y);
7053
7054     if (can_turn_left && can_turn_right)
7055       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7056     else if (can_turn_left)
7057       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7058     else if (can_turn_right)
7059       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7060     else
7061       MovDir[x][y] = back_dir;
7062
7063     MovDelay[x][y] = 16 + 16 * RND(3);
7064   }
7065   else if (element == EL_PACMAN)
7066   {
7067     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7068     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7069
7070     if (can_turn_left && can_turn_right)
7071       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7072     else if (can_turn_left)
7073       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7074     else if (can_turn_right)
7075       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7076     else
7077       MovDir[x][y] = back_dir;
7078
7079     MovDelay[x][y] = 6 + RND(40);
7080   }
7081   else if (element == EL_PIG)
7082   {
7083     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7084     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7085     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7086     boolean should_turn_left, should_turn_right, should_move_on;
7087     int rnd_value = 24;
7088     int rnd = RND(rnd_value);
7089
7090     should_turn_left = (can_turn_left &&
7091                         (!can_move_on ||
7092                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7093                                                    y + back_dy + left_dy)));
7094     should_turn_right = (can_turn_right &&
7095                          (!can_move_on ||
7096                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7097                                                     y + back_dy + right_dy)));
7098     should_move_on = (can_move_on &&
7099                       (!can_turn_left ||
7100                        !can_turn_right ||
7101                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7102                                                  y + move_dy + left_dy) ||
7103                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7104                                                  y + move_dy + right_dy)));
7105
7106     if (should_turn_left || should_turn_right || should_move_on)
7107     {
7108       if (should_turn_left && should_turn_right && should_move_on)
7109         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7110                         rnd < 2 * rnd_value / 3 ? right_dir :
7111                         old_move_dir);
7112       else if (should_turn_left && should_turn_right)
7113         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7114       else if (should_turn_left && should_move_on)
7115         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7116       else if (should_turn_right && should_move_on)
7117         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7118       else if (should_turn_left)
7119         MovDir[x][y] = left_dir;
7120       else if (should_turn_right)
7121         MovDir[x][y] = right_dir;
7122       else if (should_move_on)
7123         MovDir[x][y] = old_move_dir;
7124     }
7125     else if (can_move_on && rnd > rnd_value / 8)
7126       MovDir[x][y] = old_move_dir;
7127     else if (can_turn_left && can_turn_right)
7128       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7129     else if (can_turn_left && rnd > rnd_value / 8)
7130       MovDir[x][y] = left_dir;
7131     else if (can_turn_right && rnd > rnd_value/8)
7132       MovDir[x][y] = right_dir;
7133     else
7134       MovDir[x][y] = back_dir;
7135
7136     xx = x + move_xy[MovDir[x][y]].dx;
7137     yy = y + move_xy[MovDir[x][y]].dy;
7138
7139     if (!IN_LEV_FIELD(xx, yy) ||
7140         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7141       MovDir[x][y] = old_move_dir;
7142
7143     MovDelay[x][y] = 0;
7144   }
7145   else if (element == EL_DRAGON)
7146   {
7147     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7148     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7149     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7150     int rnd_value = 24;
7151     int rnd = RND(rnd_value);
7152
7153     if (can_move_on && rnd > rnd_value / 8)
7154       MovDir[x][y] = old_move_dir;
7155     else if (can_turn_left && can_turn_right)
7156       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7157     else if (can_turn_left && rnd > rnd_value / 8)
7158       MovDir[x][y] = left_dir;
7159     else if (can_turn_right && rnd > rnd_value / 8)
7160       MovDir[x][y] = right_dir;
7161     else
7162       MovDir[x][y] = back_dir;
7163
7164     xx = x + move_xy[MovDir[x][y]].dx;
7165     yy = y + move_xy[MovDir[x][y]].dy;
7166
7167     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7168       MovDir[x][y] = old_move_dir;
7169
7170     MovDelay[x][y] = 0;
7171   }
7172   else if (element == EL_MOLE)
7173   {
7174     boolean can_move_on =
7175       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7176                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7177                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7178     if (!can_move_on)
7179     {
7180       boolean can_turn_left =
7181         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7182                               IS_AMOEBOID(Tile[left_x][left_y])));
7183
7184       boolean can_turn_right =
7185         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7186                               IS_AMOEBOID(Tile[right_x][right_y])));
7187
7188       if (can_turn_left && can_turn_right)
7189         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7190       else if (can_turn_left)
7191         MovDir[x][y] = left_dir;
7192       else
7193         MovDir[x][y] = right_dir;
7194     }
7195
7196     if (MovDir[x][y] != old_move_dir)
7197       MovDelay[x][y] = 9;
7198   }
7199   else if (element == EL_BALLOON)
7200   {
7201     MovDir[x][y] = game.wind_direction;
7202     MovDelay[x][y] = 0;
7203   }
7204   else if (element == EL_SPRING)
7205   {
7206     if (MovDir[x][y] & MV_HORIZONTAL)
7207     {
7208       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7209           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7210       {
7211         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7212         ResetGfxAnimation(move_x, move_y);
7213         TEST_DrawLevelField(move_x, move_y);
7214
7215         MovDir[x][y] = back_dir;
7216       }
7217       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7218                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7219         MovDir[x][y] = MV_NONE;
7220     }
7221
7222     MovDelay[x][y] = 0;
7223   }
7224   else if (element == EL_ROBOT ||
7225            element == EL_SATELLITE ||
7226            element == EL_PENGUIN ||
7227            element == EL_EMC_ANDROID)
7228   {
7229     int attr_x = -1, attr_y = -1;
7230
7231     if (game.all_players_gone)
7232     {
7233       attr_x = game.exit_x;
7234       attr_y = game.exit_y;
7235     }
7236     else
7237     {
7238       int i;
7239
7240       for (i = 0; i < MAX_PLAYERS; i++)
7241       {
7242         struct PlayerInfo *player = &stored_player[i];
7243         int jx = player->jx, jy = player->jy;
7244
7245         if (!player->active)
7246           continue;
7247
7248         if (attr_x == -1 ||
7249             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7250         {
7251           attr_x = jx;
7252           attr_y = jy;
7253         }
7254       }
7255     }
7256
7257     if (element == EL_ROBOT &&
7258         game.robot_wheel_x >= 0 &&
7259         game.robot_wheel_y >= 0 &&
7260         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7261          game.engine_version < VERSION_IDENT(3,1,0,0)))
7262     {
7263       attr_x = game.robot_wheel_x;
7264       attr_y = game.robot_wheel_y;
7265     }
7266
7267     if (element == EL_PENGUIN)
7268     {
7269       int i;
7270       static int xy[4][2] =
7271       {
7272         { 0, -1 },
7273         { -1, 0 },
7274         { +1, 0 },
7275         { 0, +1 }
7276       };
7277
7278       for (i = 0; i < NUM_DIRECTIONS; i++)
7279       {
7280         int ex = x + xy[i][0];
7281         int ey = y + xy[i][1];
7282
7283         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7284                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7285                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7286                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7287         {
7288           attr_x = ex;
7289           attr_y = ey;
7290           break;
7291         }
7292       }
7293     }
7294
7295     MovDir[x][y] = MV_NONE;
7296     if (attr_x < x)
7297       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7298     else if (attr_x > x)
7299       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7300     if (attr_y < y)
7301       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7302     else if (attr_y > y)
7303       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7304
7305     if (element == EL_ROBOT)
7306     {
7307       int newx, newy;
7308
7309       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7310         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7311       Moving2Blocked(x, y, &newx, &newy);
7312
7313       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7314         MovDelay[x][y] = 8 + 8 * !RND(3);
7315       else
7316         MovDelay[x][y] = 16;
7317     }
7318     else if (element == EL_PENGUIN)
7319     {
7320       int newx, newy;
7321
7322       MovDelay[x][y] = 1;
7323
7324       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7325       {
7326         boolean first_horiz = RND(2);
7327         int new_move_dir = MovDir[x][y];
7328
7329         MovDir[x][y] =
7330           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7331         Moving2Blocked(x, y, &newx, &newy);
7332
7333         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7334           return;
7335
7336         MovDir[x][y] =
7337           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7338         Moving2Blocked(x, y, &newx, &newy);
7339
7340         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7341           return;
7342
7343         MovDir[x][y] = old_move_dir;
7344         return;
7345       }
7346     }
7347     else if (element == EL_SATELLITE)
7348     {
7349       int newx, newy;
7350
7351       MovDelay[x][y] = 1;
7352
7353       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7354       {
7355         boolean first_horiz = RND(2);
7356         int new_move_dir = MovDir[x][y];
7357
7358         MovDir[x][y] =
7359           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7360         Moving2Blocked(x, y, &newx, &newy);
7361
7362         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7363           return;
7364
7365         MovDir[x][y] =
7366           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7367         Moving2Blocked(x, y, &newx, &newy);
7368
7369         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7370           return;
7371
7372         MovDir[x][y] = old_move_dir;
7373         return;
7374       }
7375     }
7376     else if (element == EL_EMC_ANDROID)
7377     {
7378       static int check_pos[16] =
7379       {
7380         -1,             //  0 => (invalid)
7381         7,              //  1 => MV_LEFT
7382         3,              //  2 => MV_RIGHT
7383         -1,             //  3 => (invalid)
7384         1,              //  4 =>            MV_UP
7385         0,              //  5 => MV_LEFT  | MV_UP
7386         2,              //  6 => MV_RIGHT | MV_UP
7387         -1,             //  7 => (invalid)
7388         5,              //  8 =>            MV_DOWN
7389         6,              //  9 => MV_LEFT  | MV_DOWN
7390         4,              // 10 => MV_RIGHT | MV_DOWN
7391         -1,             // 11 => (invalid)
7392         -1,             // 12 => (invalid)
7393         -1,             // 13 => (invalid)
7394         -1,             // 14 => (invalid)
7395         -1,             // 15 => (invalid)
7396       };
7397       static struct
7398       {
7399         int dx, dy;
7400         int dir;
7401       } check_xy[8] =
7402       {
7403         { -1, -1,       MV_LEFT  | MV_UP   },
7404         {  0, -1,                  MV_UP   },
7405         { +1, -1,       MV_RIGHT | MV_UP   },
7406         { +1,  0,       MV_RIGHT           },
7407         { +1, +1,       MV_RIGHT | MV_DOWN },
7408         {  0, +1,                  MV_DOWN },
7409         { -1, +1,       MV_LEFT  | MV_DOWN },
7410         { -1,  0,       MV_LEFT            },
7411       };
7412       int start_pos, check_order;
7413       boolean can_clone = FALSE;
7414       int i;
7415
7416       // check if there is any free field around current position
7417       for (i = 0; i < 8; i++)
7418       {
7419         int newx = x + check_xy[i].dx;
7420         int newy = y + check_xy[i].dy;
7421
7422         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7423         {
7424           can_clone = TRUE;
7425
7426           break;
7427         }
7428       }
7429
7430       if (can_clone)            // randomly find an element to clone
7431       {
7432         can_clone = FALSE;
7433
7434         start_pos = check_pos[RND(8)];
7435         check_order = (RND(2) ? -1 : +1);
7436
7437         for (i = 0; i < 8; i++)
7438         {
7439           int pos_raw = start_pos + i * check_order;
7440           int pos = (pos_raw + 8) % 8;
7441           int newx = x + check_xy[pos].dx;
7442           int newy = y + check_xy[pos].dy;
7443
7444           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7445           {
7446             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7447             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7448
7449             Store[x][y] = Tile[newx][newy];
7450
7451             can_clone = TRUE;
7452
7453             break;
7454           }
7455         }
7456       }
7457
7458       if (can_clone)            // randomly find a direction to move
7459       {
7460         can_clone = FALSE;
7461
7462         start_pos = check_pos[RND(8)];
7463         check_order = (RND(2) ? -1 : +1);
7464
7465         for (i = 0; i < 8; i++)
7466         {
7467           int pos_raw = start_pos + i * check_order;
7468           int pos = (pos_raw + 8) % 8;
7469           int newx = x + check_xy[pos].dx;
7470           int newy = y + check_xy[pos].dy;
7471           int new_move_dir = check_xy[pos].dir;
7472
7473           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7474           {
7475             MovDir[x][y] = new_move_dir;
7476             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7477
7478             can_clone = TRUE;
7479
7480             break;
7481           }
7482         }
7483       }
7484
7485       if (can_clone)            // cloning and moving successful
7486         return;
7487
7488       // cannot clone -- try to move towards player
7489
7490       start_pos = check_pos[MovDir[x][y] & 0x0f];
7491       check_order = (RND(2) ? -1 : +1);
7492
7493       for (i = 0; i < 3; i++)
7494       {
7495         // first check start_pos, then previous/next or (next/previous) pos
7496         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7497         int pos = (pos_raw + 8) % 8;
7498         int newx = x + check_xy[pos].dx;
7499         int newy = y + check_xy[pos].dy;
7500         int new_move_dir = check_xy[pos].dir;
7501
7502         if (IS_PLAYER(newx, newy))
7503           break;
7504
7505         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7506         {
7507           MovDir[x][y] = new_move_dir;
7508           MovDelay[x][y] = level.android_move_time * 8 + 1;
7509
7510           break;
7511         }
7512       }
7513     }
7514   }
7515   else if (move_pattern == MV_TURNING_LEFT ||
7516            move_pattern == MV_TURNING_RIGHT ||
7517            move_pattern == MV_TURNING_LEFT_RIGHT ||
7518            move_pattern == MV_TURNING_RIGHT_LEFT ||
7519            move_pattern == MV_TURNING_RANDOM ||
7520            move_pattern == MV_ALL_DIRECTIONS)
7521   {
7522     boolean can_turn_left =
7523       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7524     boolean can_turn_right =
7525       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7526
7527     if (element_info[element].move_stepsize == 0)       // "not moving"
7528       return;
7529
7530     if (move_pattern == MV_TURNING_LEFT)
7531       MovDir[x][y] = left_dir;
7532     else if (move_pattern == MV_TURNING_RIGHT)
7533       MovDir[x][y] = right_dir;
7534     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7535       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7536     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7537       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7538     else if (move_pattern == MV_TURNING_RANDOM)
7539       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7540                       can_turn_right && !can_turn_left ? right_dir :
7541                       RND(2) ? left_dir : right_dir);
7542     else if (can_turn_left && can_turn_right)
7543       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7544     else if (can_turn_left)
7545       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7546     else if (can_turn_right)
7547       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7548     else
7549       MovDir[x][y] = back_dir;
7550
7551     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7552   }
7553   else if (move_pattern == MV_HORIZONTAL ||
7554            move_pattern == MV_VERTICAL)
7555   {
7556     if (move_pattern & old_move_dir)
7557       MovDir[x][y] = back_dir;
7558     else if (move_pattern == MV_HORIZONTAL)
7559       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7560     else if (move_pattern == MV_VERTICAL)
7561       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7562
7563     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7564   }
7565   else if (move_pattern & MV_ANY_DIRECTION)
7566   {
7567     MovDir[x][y] = move_pattern;
7568     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7569   }
7570   else if (move_pattern & MV_WIND_DIRECTION)
7571   {
7572     MovDir[x][y] = game.wind_direction;
7573     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7574   }
7575   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7576   {
7577     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7578       MovDir[x][y] = left_dir;
7579     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7580       MovDir[x][y] = right_dir;
7581
7582     if (MovDir[x][y] != old_move_dir)
7583       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7584   }
7585   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7586   {
7587     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7588       MovDir[x][y] = right_dir;
7589     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7590       MovDir[x][y] = left_dir;
7591
7592     if (MovDir[x][y] != old_move_dir)
7593       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595   else if (move_pattern == MV_TOWARDS_PLAYER ||
7596            move_pattern == MV_AWAY_FROM_PLAYER)
7597   {
7598     int attr_x = -1, attr_y = -1;
7599     int newx, newy;
7600     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7601
7602     if (game.all_players_gone)
7603     {
7604       attr_x = game.exit_x;
7605       attr_y = game.exit_y;
7606     }
7607     else
7608     {
7609       int i;
7610
7611       for (i = 0; i < MAX_PLAYERS; i++)
7612       {
7613         struct PlayerInfo *player = &stored_player[i];
7614         int jx = player->jx, jy = player->jy;
7615
7616         if (!player->active)
7617           continue;
7618
7619         if (attr_x == -1 ||
7620             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7621         {
7622           attr_x = jx;
7623           attr_y = jy;
7624         }
7625       }
7626     }
7627
7628     MovDir[x][y] = MV_NONE;
7629     if (attr_x < x)
7630       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7631     else if (attr_x > x)
7632       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7633     if (attr_y < y)
7634       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7635     else if (attr_y > y)
7636       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7637
7638     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7639
7640     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7641     {
7642       boolean first_horiz = RND(2);
7643       int new_move_dir = MovDir[x][y];
7644
7645       if (element_info[element].move_stepsize == 0)     // "not moving"
7646       {
7647         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7648         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7649
7650         return;
7651       }
7652
7653       MovDir[x][y] =
7654         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7655       Moving2Blocked(x, y, &newx, &newy);
7656
7657       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7658         return;
7659
7660       MovDir[x][y] =
7661         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7662       Moving2Blocked(x, y, &newx, &newy);
7663
7664       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7665         return;
7666
7667       MovDir[x][y] = old_move_dir;
7668     }
7669   }
7670   else if (move_pattern == MV_WHEN_PUSHED ||
7671            move_pattern == MV_WHEN_DROPPED)
7672   {
7673     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7674       MovDir[x][y] = MV_NONE;
7675
7676     MovDelay[x][y] = 0;
7677   }
7678   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7679   {
7680     static int test_xy[7][2] =
7681     {
7682       { 0, -1 },
7683       { -1, 0 },
7684       { +1, 0 },
7685       { 0, +1 },
7686       { 0, -1 },
7687       { -1, 0 },
7688       { +1, 0 },
7689     };
7690     static int test_dir[7] =
7691     {
7692       MV_UP,
7693       MV_LEFT,
7694       MV_RIGHT,
7695       MV_DOWN,
7696       MV_UP,
7697       MV_LEFT,
7698       MV_RIGHT,
7699     };
7700     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7701     int move_preference = -1000000;     // start with very low preference
7702     int new_move_dir = MV_NONE;
7703     int start_test = RND(4);
7704     int i;
7705
7706     for (i = 0; i < NUM_DIRECTIONS; i++)
7707     {
7708       int move_dir = test_dir[start_test + i];
7709       int move_dir_preference;
7710
7711       xx = x + test_xy[start_test + i][0];
7712       yy = y + test_xy[start_test + i][1];
7713
7714       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7715           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7716       {
7717         new_move_dir = move_dir;
7718
7719         break;
7720       }
7721
7722       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7723         continue;
7724
7725       move_dir_preference = -1 * RunnerVisit[xx][yy];
7726       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7727         move_dir_preference = PlayerVisit[xx][yy];
7728
7729       if (move_dir_preference > move_preference)
7730       {
7731         // prefer field that has not been visited for the longest time
7732         move_preference = move_dir_preference;
7733         new_move_dir = move_dir;
7734       }
7735       else if (move_dir_preference == move_preference &&
7736                move_dir == old_move_dir)
7737       {
7738         // prefer last direction when all directions are preferred equally
7739         move_preference = move_dir_preference;
7740         new_move_dir = move_dir;
7741       }
7742     }
7743
7744     MovDir[x][y] = new_move_dir;
7745     if (old_move_dir != new_move_dir)
7746       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7747   }
7748 }
7749
7750 static void TurnRound(int x, int y)
7751 {
7752   int direction = MovDir[x][y];
7753
7754   TurnRoundExt(x, y);
7755
7756   GfxDir[x][y] = MovDir[x][y];
7757
7758   if (direction != MovDir[x][y])
7759     GfxFrame[x][y] = 0;
7760
7761   if (MovDelay[x][y])
7762     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7763
7764   ResetGfxFrame(x, y);
7765 }
7766
7767 static boolean JustBeingPushed(int x, int y)
7768 {
7769   int i;
7770
7771   for (i = 0; i < MAX_PLAYERS; i++)
7772   {
7773     struct PlayerInfo *player = &stored_player[i];
7774
7775     if (player->active && player->is_pushing && player->MovPos)
7776     {
7777       int next_jx = player->jx + (player->jx - player->last_jx);
7778       int next_jy = player->jy + (player->jy - player->last_jy);
7779
7780       if (x == next_jx && y == next_jy)
7781         return TRUE;
7782     }
7783   }
7784
7785   return FALSE;
7786 }
7787
7788 static void StartMoving(int x, int y)
7789 {
7790   boolean started_moving = FALSE;       // some elements can fall _and_ move
7791   int element = Tile[x][y];
7792
7793   if (Stop[x][y])
7794     return;
7795
7796   if (MovDelay[x][y] == 0)
7797     GfxAction[x][y] = ACTION_DEFAULT;
7798
7799   if (CAN_FALL(element) && y < lev_fieldy - 1)
7800   {
7801     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7802         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7803       if (JustBeingPushed(x, y))
7804         return;
7805
7806     if (element == EL_QUICKSAND_FULL)
7807     {
7808       if (IS_FREE(x, y + 1))
7809       {
7810         InitMovingField(x, y, MV_DOWN);
7811         started_moving = TRUE;
7812
7813         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7814 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7815         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7816           Store[x][y] = EL_ROCK;
7817 #else
7818         Store[x][y] = EL_ROCK;
7819 #endif
7820
7821         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7822       }
7823       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7824       {
7825         if (!MovDelay[x][y])
7826         {
7827           MovDelay[x][y] = TILEY + 1;
7828
7829           ResetGfxAnimation(x, y);
7830           ResetGfxAnimation(x, y + 1);
7831         }
7832
7833         if (MovDelay[x][y])
7834         {
7835           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7836           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7837
7838           MovDelay[x][y]--;
7839           if (MovDelay[x][y])
7840             return;
7841         }
7842
7843         Tile[x][y] = EL_QUICKSAND_EMPTY;
7844         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7845         Store[x][y + 1] = Store[x][y];
7846         Store[x][y] = 0;
7847
7848         PlayLevelSoundAction(x, y, ACTION_FILLING);
7849       }
7850       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7851       {
7852         if (!MovDelay[x][y])
7853         {
7854           MovDelay[x][y] = TILEY + 1;
7855
7856           ResetGfxAnimation(x, y);
7857           ResetGfxAnimation(x, y + 1);
7858         }
7859
7860         if (MovDelay[x][y])
7861         {
7862           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7863           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7864
7865           MovDelay[x][y]--;
7866           if (MovDelay[x][y])
7867             return;
7868         }
7869
7870         Tile[x][y] = EL_QUICKSAND_EMPTY;
7871         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7872         Store[x][y + 1] = Store[x][y];
7873         Store[x][y] = 0;
7874
7875         PlayLevelSoundAction(x, y, ACTION_FILLING);
7876       }
7877     }
7878     else if (element == EL_QUICKSAND_FAST_FULL)
7879     {
7880       if (IS_FREE(x, y + 1))
7881       {
7882         InitMovingField(x, y, MV_DOWN);
7883         started_moving = TRUE;
7884
7885         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7886 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7887         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7888           Store[x][y] = EL_ROCK;
7889 #else
7890         Store[x][y] = EL_ROCK;
7891 #endif
7892
7893         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7894       }
7895       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7896       {
7897         if (!MovDelay[x][y])
7898         {
7899           MovDelay[x][y] = TILEY + 1;
7900
7901           ResetGfxAnimation(x, y);
7902           ResetGfxAnimation(x, y + 1);
7903         }
7904
7905         if (MovDelay[x][y])
7906         {
7907           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7908           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7909
7910           MovDelay[x][y]--;
7911           if (MovDelay[x][y])
7912             return;
7913         }
7914
7915         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7916         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7917         Store[x][y + 1] = Store[x][y];
7918         Store[x][y] = 0;
7919
7920         PlayLevelSoundAction(x, y, ACTION_FILLING);
7921       }
7922       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7923       {
7924         if (!MovDelay[x][y])
7925         {
7926           MovDelay[x][y] = TILEY + 1;
7927
7928           ResetGfxAnimation(x, y);
7929           ResetGfxAnimation(x, y + 1);
7930         }
7931
7932         if (MovDelay[x][y])
7933         {
7934           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7935           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7936
7937           MovDelay[x][y]--;
7938           if (MovDelay[x][y])
7939             return;
7940         }
7941
7942         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7943         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7944         Store[x][y + 1] = Store[x][y];
7945         Store[x][y] = 0;
7946
7947         PlayLevelSoundAction(x, y, ACTION_FILLING);
7948       }
7949     }
7950     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7951              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7952     {
7953       InitMovingField(x, y, MV_DOWN);
7954       started_moving = TRUE;
7955
7956       Tile[x][y] = EL_QUICKSAND_FILLING;
7957       Store[x][y] = element;
7958
7959       PlayLevelSoundAction(x, y, ACTION_FILLING);
7960     }
7961     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7962              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7963     {
7964       InitMovingField(x, y, MV_DOWN);
7965       started_moving = TRUE;
7966
7967       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7968       Store[x][y] = element;
7969
7970       PlayLevelSoundAction(x, y, ACTION_FILLING);
7971     }
7972     else if (element == EL_MAGIC_WALL_FULL)
7973     {
7974       if (IS_FREE(x, y + 1))
7975       {
7976         InitMovingField(x, y, MV_DOWN);
7977         started_moving = TRUE;
7978
7979         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7980         Store[x][y] = EL_CHANGED(Store[x][y]);
7981       }
7982       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7983       {
7984         if (!MovDelay[x][y])
7985           MovDelay[x][y] = TILEY / 4 + 1;
7986
7987         if (MovDelay[x][y])
7988         {
7989           MovDelay[x][y]--;
7990           if (MovDelay[x][y])
7991             return;
7992         }
7993
7994         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7995         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7996         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7997         Store[x][y] = 0;
7998       }
7999     }
8000     else if (element == EL_BD_MAGIC_WALL_FULL)
8001     {
8002       if (IS_FREE(x, y + 1))
8003       {
8004         InitMovingField(x, y, MV_DOWN);
8005         started_moving = TRUE;
8006
8007         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8008         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8009       }
8010       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8011       {
8012         if (!MovDelay[x][y])
8013           MovDelay[x][y] = TILEY / 4 + 1;
8014
8015         if (MovDelay[x][y])
8016         {
8017           MovDelay[x][y]--;
8018           if (MovDelay[x][y])
8019             return;
8020         }
8021
8022         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8023         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8024         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8025         Store[x][y] = 0;
8026       }
8027     }
8028     else if (element == EL_DC_MAGIC_WALL_FULL)
8029     {
8030       if (IS_FREE(x, y + 1))
8031       {
8032         InitMovingField(x, y, MV_DOWN);
8033         started_moving = TRUE;
8034
8035         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8036         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8037       }
8038       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8039       {
8040         if (!MovDelay[x][y])
8041           MovDelay[x][y] = TILEY / 4 + 1;
8042
8043         if (MovDelay[x][y])
8044         {
8045           MovDelay[x][y]--;
8046           if (MovDelay[x][y])
8047             return;
8048         }
8049
8050         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8051         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8052         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8053         Store[x][y] = 0;
8054       }
8055     }
8056     else if ((CAN_PASS_MAGIC_WALL(element) &&
8057               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8058                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8059              (CAN_PASS_DC_MAGIC_WALL(element) &&
8060               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8061
8062     {
8063       InitMovingField(x, y, MV_DOWN);
8064       started_moving = TRUE;
8065
8066       Tile[x][y] =
8067         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8068          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8069          EL_DC_MAGIC_WALL_FILLING);
8070       Store[x][y] = element;
8071     }
8072     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8073     {
8074       SplashAcid(x, y + 1);
8075
8076       InitMovingField(x, y, MV_DOWN);
8077       started_moving = TRUE;
8078
8079       Store[x][y] = EL_ACID;
8080     }
8081     else if (
8082              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8083               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8084              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8085               CAN_FALL(element) && WasJustFalling[x][y] &&
8086               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8087
8088              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8089               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8090               (Tile[x][y + 1] == EL_BLOCKED)))
8091     {
8092       /* this is needed for a special case not covered by calling "Impact()"
8093          from "ContinueMoving()": if an element moves to a tile directly below
8094          another element which was just falling on that tile (which was empty
8095          in the previous frame), the falling element above would just stop
8096          instead of smashing the element below (in previous version, the above
8097          element was just checked for "moving" instead of "falling", resulting
8098          in incorrect smashes caused by horizontal movement of the above
8099          element; also, the case of the player being the element to smash was
8100          simply not covered here... :-/ ) */
8101
8102       CheckCollision[x][y] = 0;
8103       CheckImpact[x][y] = 0;
8104
8105       Impact(x, y);
8106     }
8107     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8108     {
8109       if (MovDir[x][y] == MV_NONE)
8110       {
8111         InitMovingField(x, y, MV_DOWN);
8112         started_moving = TRUE;
8113       }
8114     }
8115     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8116     {
8117       if (WasJustFalling[x][y]) // prevent animation from being restarted
8118         MovDir[x][y] = MV_DOWN;
8119
8120       InitMovingField(x, y, MV_DOWN);
8121       started_moving = TRUE;
8122     }
8123     else if (element == EL_AMOEBA_DROP)
8124     {
8125       Tile[x][y] = EL_AMOEBA_GROWING;
8126       Store[x][y] = EL_AMOEBA_WET;
8127     }
8128     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8129               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8130              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8131              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8132     {
8133       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8134                                 (IS_FREE(x - 1, y + 1) ||
8135                                  Tile[x - 1][y + 1] == EL_ACID));
8136       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8137                                 (IS_FREE(x + 1, y + 1) ||
8138                                  Tile[x + 1][y + 1] == EL_ACID));
8139       boolean can_fall_any  = (can_fall_left || can_fall_right);
8140       boolean can_fall_both = (can_fall_left && can_fall_right);
8141       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8142
8143       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8144       {
8145         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8146           can_fall_right = FALSE;
8147         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8148           can_fall_left = FALSE;
8149         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8150           can_fall_right = FALSE;
8151         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8152           can_fall_left = FALSE;
8153
8154         can_fall_any  = (can_fall_left || can_fall_right);
8155         can_fall_both = FALSE;
8156       }
8157
8158       if (can_fall_both)
8159       {
8160         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8161           can_fall_right = FALSE;       // slip down on left side
8162         else
8163           can_fall_left = !(can_fall_right = RND(2));
8164
8165         can_fall_both = FALSE;
8166       }
8167
8168       if (can_fall_any)
8169       {
8170         // if not determined otherwise, prefer left side for slipping down
8171         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8172         started_moving = TRUE;
8173       }
8174     }
8175     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8176     {
8177       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8178       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8179       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8180       int belt_dir = game.belt_dir[belt_nr];
8181
8182       if ((belt_dir == MV_LEFT  && left_is_free) ||
8183           (belt_dir == MV_RIGHT && right_is_free))
8184       {
8185         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8186
8187         InitMovingField(x, y, belt_dir);
8188         started_moving = TRUE;
8189
8190         Pushed[x][y] = TRUE;
8191         Pushed[nextx][y] = TRUE;
8192
8193         GfxAction[x][y] = ACTION_DEFAULT;
8194       }
8195       else
8196       {
8197         MovDir[x][y] = 0;       // if element was moving, stop it
8198       }
8199     }
8200   }
8201
8202   // not "else if" because of elements that can fall and move (EL_SPRING)
8203   if (CAN_MOVE(element) && !started_moving)
8204   {
8205     int move_pattern = element_info[element].move_pattern;
8206     int newx, newy;
8207
8208     Moving2Blocked(x, y, &newx, &newy);
8209
8210     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8211       return;
8212
8213     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8214         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8215     {
8216       WasJustMoving[x][y] = 0;
8217       CheckCollision[x][y] = 0;
8218
8219       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8220
8221       if (Tile[x][y] != element)        // element has changed
8222         return;
8223     }
8224
8225     if (!MovDelay[x][y])        // start new movement phase
8226     {
8227       // all objects that can change their move direction after each step
8228       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8229
8230       if (element != EL_YAMYAM &&
8231           element != EL_DARK_YAMYAM &&
8232           element != EL_PACMAN &&
8233           !(move_pattern & MV_ANY_DIRECTION) &&
8234           move_pattern != MV_TURNING_LEFT &&
8235           move_pattern != MV_TURNING_RIGHT &&
8236           move_pattern != MV_TURNING_LEFT_RIGHT &&
8237           move_pattern != MV_TURNING_RIGHT_LEFT &&
8238           move_pattern != MV_TURNING_RANDOM)
8239       {
8240         TurnRound(x, y);
8241
8242         if (MovDelay[x][y] && (element == EL_BUG ||
8243                                element == EL_SPACESHIP ||
8244                                element == EL_SP_SNIKSNAK ||
8245                                element == EL_SP_ELECTRON ||
8246                                element == EL_MOLE))
8247           TEST_DrawLevelField(x, y);
8248       }
8249     }
8250
8251     if (MovDelay[x][y])         // wait some time before next movement
8252     {
8253       MovDelay[x][y]--;
8254
8255       if (element == EL_ROBOT ||
8256           element == EL_YAMYAM ||
8257           element == EL_DARK_YAMYAM)
8258       {
8259         DrawLevelElementAnimationIfNeeded(x, y, element);
8260         PlayLevelSoundAction(x, y, ACTION_WAITING);
8261       }
8262       else if (element == EL_SP_ELECTRON)
8263         DrawLevelElementAnimationIfNeeded(x, y, element);
8264       else if (element == EL_DRAGON)
8265       {
8266         int i;
8267         int dir = MovDir[x][y];
8268         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8269         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8270         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8271                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8272                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8273                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8274         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8275
8276         GfxAction[x][y] = ACTION_ATTACKING;
8277
8278         if (IS_PLAYER(x, y))
8279           DrawPlayerField(x, y);
8280         else
8281           TEST_DrawLevelField(x, y);
8282
8283         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8284
8285         for (i = 1; i <= 3; i++)
8286         {
8287           int xx = x + i * dx;
8288           int yy = y + i * dy;
8289           int sx = SCREENX(xx);
8290           int sy = SCREENY(yy);
8291           int flame_graphic = graphic + (i - 1);
8292
8293           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8294             break;
8295
8296           if (MovDelay[x][y])
8297           {
8298             int flamed = MovingOrBlocked2Element(xx, yy);
8299
8300             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8301               Bang(xx, yy);
8302             else
8303               RemoveMovingField(xx, yy);
8304
8305             ChangeDelay[xx][yy] = 0;
8306
8307             Tile[xx][yy] = EL_FLAMES;
8308
8309             if (IN_SCR_FIELD(sx, sy))
8310             {
8311               TEST_DrawLevelFieldCrumbled(xx, yy);
8312               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8313             }
8314           }
8315           else
8316           {
8317             if (Tile[xx][yy] == EL_FLAMES)
8318               Tile[xx][yy] = EL_EMPTY;
8319             TEST_DrawLevelField(xx, yy);
8320           }
8321         }
8322       }
8323
8324       if (MovDelay[x][y])       // element still has to wait some time
8325       {
8326         PlayLevelSoundAction(x, y, ACTION_WAITING);
8327
8328         return;
8329       }
8330     }
8331
8332     // now make next step
8333
8334     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8335
8336     if (DONT_COLLIDE_WITH(element) &&
8337         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8338         !PLAYER_ENEMY_PROTECTED(newx, newy))
8339     {
8340       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8341
8342       return;
8343     }
8344
8345     else if (CAN_MOVE_INTO_ACID(element) &&
8346              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8347              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8348              (MovDir[x][y] == MV_DOWN ||
8349               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8350     {
8351       SplashAcid(newx, newy);
8352       Store[x][y] = EL_ACID;
8353     }
8354     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8355     {
8356       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8357           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8358           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8359           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8360       {
8361         RemoveField(x, y);
8362         TEST_DrawLevelField(x, y);
8363
8364         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8365         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8366           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8367
8368         game.friends_still_needed--;
8369         if (!game.friends_still_needed &&
8370             !game.GameOver &&
8371             game.all_players_gone)
8372           LevelSolved();
8373
8374         return;
8375       }
8376       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8377       {
8378         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8379           TEST_DrawLevelField(newx, newy);
8380         else
8381           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8382       }
8383       else if (!IS_FREE(newx, newy))
8384       {
8385         GfxAction[x][y] = ACTION_WAITING;
8386
8387         if (IS_PLAYER(x, y))
8388           DrawPlayerField(x, y);
8389         else
8390           TEST_DrawLevelField(x, y);
8391
8392         return;
8393       }
8394     }
8395     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8396     {
8397       if (IS_FOOD_PIG(Tile[newx][newy]))
8398       {
8399         if (IS_MOVING(newx, newy))
8400           RemoveMovingField(newx, newy);
8401         else
8402         {
8403           Tile[newx][newy] = EL_EMPTY;
8404           TEST_DrawLevelField(newx, newy);
8405         }
8406
8407         PlayLevelSound(x, y, SND_PIG_DIGGING);
8408       }
8409       else if (!IS_FREE(newx, newy))
8410       {
8411         if (IS_PLAYER(x, y))
8412           DrawPlayerField(x, y);
8413         else
8414           TEST_DrawLevelField(x, y);
8415
8416         return;
8417       }
8418     }
8419     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8420     {
8421       if (Store[x][y] != EL_EMPTY)
8422       {
8423         boolean can_clone = FALSE;
8424         int xx, yy;
8425
8426         // check if element to clone is still there
8427         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8428         {
8429           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8430           {
8431             can_clone = TRUE;
8432
8433             break;
8434           }
8435         }
8436
8437         // cannot clone or target field not free anymore -- do not clone
8438         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8439           Store[x][y] = EL_EMPTY;
8440       }
8441
8442       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8443       {
8444         if (IS_MV_DIAGONAL(MovDir[x][y]))
8445         {
8446           int diagonal_move_dir = MovDir[x][y];
8447           int stored = Store[x][y];
8448           int change_delay = 8;
8449           int graphic;
8450
8451           // android is moving diagonally
8452
8453           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8454
8455           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8456           GfxElement[x][y] = EL_EMC_ANDROID;
8457           GfxAction[x][y] = ACTION_SHRINKING;
8458           GfxDir[x][y] = diagonal_move_dir;
8459           ChangeDelay[x][y] = change_delay;
8460
8461           if (Store[x][y] == EL_EMPTY)
8462             Store[x][y] = GfxElementEmpty[x][y];
8463
8464           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8465                                    GfxDir[x][y]);
8466
8467           DrawLevelGraphicAnimation(x, y, graphic);
8468           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8469
8470           if (Tile[newx][newy] == EL_ACID)
8471           {
8472             SplashAcid(newx, newy);
8473
8474             return;
8475           }
8476
8477           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8478
8479           Store[newx][newy] = EL_EMC_ANDROID;
8480           GfxElement[newx][newy] = EL_EMC_ANDROID;
8481           GfxAction[newx][newy] = ACTION_GROWING;
8482           GfxDir[newx][newy] = diagonal_move_dir;
8483           ChangeDelay[newx][newy] = change_delay;
8484
8485           graphic = el_act_dir2img(GfxElement[newx][newy],
8486                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8487
8488           DrawLevelGraphicAnimation(newx, newy, graphic);
8489           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8490
8491           return;
8492         }
8493         else
8494         {
8495           Tile[newx][newy] = EL_EMPTY;
8496           TEST_DrawLevelField(newx, newy);
8497
8498           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8499         }
8500       }
8501       else if (!IS_FREE(newx, newy))
8502       {
8503         return;
8504       }
8505     }
8506     else if (IS_CUSTOM_ELEMENT(element) &&
8507              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8508     {
8509       if (!DigFieldByCE(newx, newy, element))
8510         return;
8511
8512       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8513       {
8514         RunnerVisit[x][y] = FrameCounter;
8515         PlayerVisit[x][y] /= 8;         // expire player visit path
8516       }
8517     }
8518     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8519     {
8520       if (!IS_FREE(newx, newy))
8521       {
8522         if (IS_PLAYER(x, y))
8523           DrawPlayerField(x, y);
8524         else
8525           TEST_DrawLevelField(x, y);
8526
8527         return;
8528       }
8529       else
8530       {
8531         boolean wanna_flame = !RND(10);
8532         int dx = newx - x, dy = newy - y;
8533         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8534         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8535         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8536                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8537         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8538                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8539
8540         if ((wanna_flame ||
8541              IS_CLASSIC_ENEMY(element1) ||
8542              IS_CLASSIC_ENEMY(element2)) &&
8543             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8544             element1 != EL_FLAMES && element2 != EL_FLAMES)
8545         {
8546           ResetGfxAnimation(x, y);
8547           GfxAction[x][y] = ACTION_ATTACKING;
8548
8549           if (IS_PLAYER(x, y))
8550             DrawPlayerField(x, y);
8551           else
8552             TEST_DrawLevelField(x, y);
8553
8554           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8555
8556           MovDelay[x][y] = 50;
8557
8558           Tile[newx][newy] = EL_FLAMES;
8559           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8560             Tile[newx1][newy1] = EL_FLAMES;
8561           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8562             Tile[newx2][newy2] = EL_FLAMES;
8563
8564           return;
8565         }
8566       }
8567     }
8568     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8569              Tile[newx][newy] == EL_DIAMOND)
8570     {
8571       if (IS_MOVING(newx, newy))
8572         RemoveMovingField(newx, newy);
8573       else
8574       {
8575         Tile[newx][newy] = EL_EMPTY;
8576         TEST_DrawLevelField(newx, newy);
8577       }
8578
8579       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8580     }
8581     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8582              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8583     {
8584       if (AmoebaNr[newx][newy])
8585       {
8586         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8587         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8588             Tile[newx][newy] == EL_BD_AMOEBA)
8589           AmoebaCnt[AmoebaNr[newx][newy]]--;
8590       }
8591
8592       if (IS_MOVING(newx, newy))
8593       {
8594         RemoveMovingField(newx, newy);
8595       }
8596       else
8597       {
8598         Tile[newx][newy] = EL_EMPTY;
8599         TEST_DrawLevelField(newx, newy);
8600       }
8601
8602       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8603     }
8604     else if ((element == EL_PACMAN || element == EL_MOLE)
8605              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8606     {
8607       if (AmoebaNr[newx][newy])
8608       {
8609         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8610         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8611             Tile[newx][newy] == EL_BD_AMOEBA)
8612           AmoebaCnt[AmoebaNr[newx][newy]]--;
8613       }
8614
8615       if (element == EL_MOLE)
8616       {
8617         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8618         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8619
8620         ResetGfxAnimation(x, y);
8621         GfxAction[x][y] = ACTION_DIGGING;
8622         TEST_DrawLevelField(x, y);
8623
8624         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8625
8626         return;                         // wait for shrinking amoeba
8627       }
8628       else      // element == EL_PACMAN
8629       {
8630         Tile[newx][newy] = EL_EMPTY;
8631         TEST_DrawLevelField(newx, newy);
8632         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8633       }
8634     }
8635     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8636              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8637               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8638     {
8639       // wait for shrinking amoeba to completely disappear
8640       return;
8641     }
8642     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8643     {
8644       // object was running against a wall
8645
8646       TurnRound(x, y);
8647
8648       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8649         DrawLevelElementAnimation(x, y, element);
8650
8651       if (DONT_TOUCH(element))
8652         TestIfBadThingTouchesPlayer(x, y);
8653
8654       return;
8655     }
8656
8657     InitMovingField(x, y, MovDir[x][y]);
8658
8659     PlayLevelSoundAction(x, y, ACTION_MOVING);
8660   }
8661
8662   if (MovDir[x][y])
8663     ContinueMoving(x, y);
8664 }
8665
8666 void ContinueMoving(int x, int y)
8667 {
8668   int element = Tile[x][y];
8669   struct ElementInfo *ei = &element_info[element];
8670   int direction = MovDir[x][y];
8671   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8672   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8673   int newx = x + dx, newy = y + dy;
8674   int stored = Store[x][y];
8675   int stored_new = Store[newx][newy];
8676   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8677   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8678   boolean last_line = (newy == lev_fieldy - 1);
8679   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8680
8681   if (pushed_by_player)         // special case: moving object pushed by player
8682   {
8683     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8684   }
8685   else if (use_step_delay)      // special case: moving object has step delay
8686   {
8687     if (!MovDelay[x][y])
8688       MovPos[x][y] += getElementMoveStepsize(x, y);
8689
8690     if (MovDelay[x][y])
8691       MovDelay[x][y]--;
8692     else
8693       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8694
8695     if (MovDelay[x][y])
8696     {
8697       TEST_DrawLevelField(x, y);
8698
8699       return;   // element is still waiting
8700     }
8701   }
8702   else                          // normal case: generically moving object
8703   {
8704     MovPos[x][y] += getElementMoveStepsize(x, y);
8705   }
8706
8707   if (ABS(MovPos[x][y]) < TILEX)
8708   {
8709     TEST_DrawLevelField(x, y);
8710
8711     return;     // element is still moving
8712   }
8713
8714   // element reached destination field
8715
8716   Tile[x][y] = EL_EMPTY;
8717   Tile[newx][newy] = element;
8718   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8719
8720   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8721   {
8722     element = Tile[newx][newy] = EL_ACID;
8723   }
8724   else if (element == EL_MOLE)
8725   {
8726     Tile[x][y] = EL_SAND;
8727
8728     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8729   }
8730   else if (element == EL_QUICKSAND_FILLING)
8731   {
8732     element = Tile[newx][newy] = get_next_element(element);
8733     Store[newx][newy] = Store[x][y];
8734   }
8735   else if (element == EL_QUICKSAND_EMPTYING)
8736   {
8737     Tile[x][y] = get_next_element(element);
8738     element = Tile[newx][newy] = Store[x][y];
8739   }
8740   else if (element == EL_QUICKSAND_FAST_FILLING)
8741   {
8742     element = Tile[newx][newy] = get_next_element(element);
8743     Store[newx][newy] = Store[x][y];
8744   }
8745   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8746   {
8747     Tile[x][y] = get_next_element(element);
8748     element = Tile[newx][newy] = Store[x][y];
8749   }
8750   else if (element == EL_MAGIC_WALL_FILLING)
8751   {
8752     element = Tile[newx][newy] = get_next_element(element);
8753     if (!game.magic_wall_active)
8754       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8755     Store[newx][newy] = Store[x][y];
8756   }
8757   else if (element == EL_MAGIC_WALL_EMPTYING)
8758   {
8759     Tile[x][y] = get_next_element(element);
8760     if (!game.magic_wall_active)
8761       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8762     element = Tile[newx][newy] = Store[x][y];
8763
8764     InitField(newx, newy, FALSE);
8765   }
8766   else if (element == EL_BD_MAGIC_WALL_FILLING)
8767   {
8768     element = Tile[newx][newy] = get_next_element(element);
8769     if (!game.magic_wall_active)
8770       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8771     Store[newx][newy] = Store[x][y];
8772   }
8773   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8774   {
8775     Tile[x][y] = get_next_element(element);
8776     if (!game.magic_wall_active)
8777       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8778     element = Tile[newx][newy] = Store[x][y];
8779
8780     InitField(newx, newy, FALSE);
8781   }
8782   else if (element == EL_DC_MAGIC_WALL_FILLING)
8783   {
8784     element = Tile[newx][newy] = get_next_element(element);
8785     if (!game.magic_wall_active)
8786       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8787     Store[newx][newy] = Store[x][y];
8788   }
8789   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8790   {
8791     Tile[x][y] = get_next_element(element);
8792     if (!game.magic_wall_active)
8793       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8794     element = Tile[newx][newy] = Store[x][y];
8795
8796     InitField(newx, newy, FALSE);
8797   }
8798   else if (element == EL_AMOEBA_DROPPING)
8799   {
8800     Tile[x][y] = get_next_element(element);
8801     element = Tile[newx][newy] = Store[x][y];
8802   }
8803   else if (element == EL_SOKOBAN_OBJECT)
8804   {
8805     if (Back[x][y])
8806       Tile[x][y] = Back[x][y];
8807
8808     if (Back[newx][newy])
8809       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8810
8811     Back[x][y] = Back[newx][newy] = 0;
8812   }
8813
8814   Store[x][y] = EL_EMPTY;
8815   MovPos[x][y] = 0;
8816   MovDir[x][y] = 0;
8817   MovDelay[x][y] = 0;
8818
8819   MovDelay[newx][newy] = 0;
8820
8821   if (CAN_CHANGE_OR_HAS_ACTION(element))
8822   {
8823     // copy element change control values to new field
8824     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8825     ChangePage[newx][newy]  = ChangePage[x][y];
8826     ChangeCount[newx][newy] = ChangeCount[x][y];
8827     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8828   }
8829
8830   CustomValue[newx][newy] = CustomValue[x][y];
8831
8832   ChangeDelay[x][y] = 0;
8833   ChangePage[x][y] = -1;
8834   ChangeCount[x][y] = 0;
8835   ChangeEvent[x][y] = -1;
8836
8837   CustomValue[x][y] = 0;
8838
8839   // copy animation control values to new field
8840   GfxFrame[newx][newy]  = GfxFrame[x][y];
8841   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8842   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8843   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8844
8845   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8846
8847   // some elements can leave other elements behind after moving
8848   if (ei->move_leave_element != EL_EMPTY &&
8849       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8850       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8851   {
8852     int move_leave_element = ei->move_leave_element;
8853
8854     // this makes it possible to leave the removed element again
8855     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8856       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8857
8858     Tile[x][y] = move_leave_element;
8859
8860     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8861       MovDir[x][y] = direction;
8862
8863     InitField(x, y, FALSE);
8864
8865     if (GFX_CRUMBLED(Tile[x][y]))
8866       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8867
8868     if (IS_PLAYER_ELEMENT(move_leave_element))
8869       RelocatePlayer(x, y, move_leave_element);
8870   }
8871
8872   // do this after checking for left-behind element
8873   ResetGfxAnimation(x, y);      // reset animation values for old field
8874
8875   if (!CAN_MOVE(element) ||
8876       (CAN_FALL(element) && direction == MV_DOWN &&
8877        (element == EL_SPRING ||
8878         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8879         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8880     GfxDir[x][y] = MovDir[newx][newy] = 0;
8881
8882   TEST_DrawLevelField(x, y);
8883   TEST_DrawLevelField(newx, newy);
8884
8885   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8886
8887   // prevent pushed element from moving on in pushed direction
8888   if (pushed_by_player && CAN_MOVE(element) &&
8889       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8890       !(element_info[element].move_pattern & direction))
8891     TurnRound(newx, newy);
8892
8893   // prevent elements on conveyor belt from moving on in last direction
8894   if (pushed_by_conveyor && CAN_FALL(element) &&
8895       direction & MV_HORIZONTAL)
8896     MovDir[newx][newy] = 0;
8897
8898   if (!pushed_by_player)
8899   {
8900     int nextx = newx + dx, nexty = newy + dy;
8901     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8902
8903     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8904
8905     if (CAN_FALL(element) && direction == MV_DOWN)
8906       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8907
8908     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8909       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8910
8911     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8912       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8913   }
8914
8915   if (DONT_TOUCH(element))      // object may be nasty to player or others
8916   {
8917     TestIfBadThingTouchesPlayer(newx, newy);
8918     TestIfBadThingTouchesFriend(newx, newy);
8919
8920     if (!IS_CUSTOM_ELEMENT(element))
8921       TestIfBadThingTouchesOtherBadThing(newx, newy);
8922   }
8923   else if (element == EL_PENGUIN)
8924     TestIfFriendTouchesBadThing(newx, newy);
8925
8926   if (DONT_GET_HIT_BY(element))
8927   {
8928     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8929   }
8930
8931   // give the player one last chance (one more frame) to move away
8932   if (CAN_FALL(element) && direction == MV_DOWN &&
8933       (last_line || (!IS_FREE(x, newy + 1) &&
8934                      (!IS_PLAYER(x, newy + 1) ||
8935                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8936     Impact(x, newy);
8937
8938   if (pushed_by_player && !game.use_change_when_pushing_bug)
8939   {
8940     int push_side = MV_DIR_OPPOSITE(direction);
8941     struct PlayerInfo *player = PLAYERINFO(x, y);
8942
8943     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8944                                player->index_bit, push_side);
8945     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8946                                         player->index_bit, push_side);
8947   }
8948
8949   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8950     MovDelay[newx][newy] = 1;
8951
8952   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8953
8954   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8955   TestIfElementHitsCustomElement(newx, newy, direction);
8956   TestIfPlayerTouchesCustomElement(newx, newy);
8957   TestIfElementTouchesCustomElement(newx, newy);
8958
8959   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8960       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8961     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8962                              MV_DIR_OPPOSITE(direction));
8963 }
8964
8965 int AmoebaNeighbourNr(int ax, int ay)
8966 {
8967   int i;
8968   int element = Tile[ax][ay];
8969   int group_nr = 0;
8970   static int xy[4][2] =
8971   {
8972     { 0, -1 },
8973     { -1, 0 },
8974     { +1, 0 },
8975     { 0, +1 }
8976   };
8977
8978   for (i = 0; i < NUM_DIRECTIONS; i++)
8979   {
8980     int x = ax + xy[i][0];
8981     int y = ay + xy[i][1];
8982
8983     if (!IN_LEV_FIELD(x, y))
8984       continue;
8985
8986     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8987       group_nr = AmoebaNr[x][y];
8988   }
8989
8990   return group_nr;
8991 }
8992
8993 static void AmoebaMerge(int ax, int ay)
8994 {
8995   int i, x, y, xx, yy;
8996   int new_group_nr = AmoebaNr[ax][ay];
8997   static int xy[4][2] =
8998   {
8999     { 0, -1 },
9000     { -1, 0 },
9001     { +1, 0 },
9002     { 0, +1 }
9003   };
9004
9005   if (new_group_nr == 0)
9006     return;
9007
9008   for (i = 0; i < NUM_DIRECTIONS; i++)
9009   {
9010     x = ax + xy[i][0];
9011     y = ay + xy[i][1];
9012
9013     if (!IN_LEV_FIELD(x, y))
9014       continue;
9015
9016     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9017          Tile[x][y] == EL_BD_AMOEBA ||
9018          Tile[x][y] == EL_AMOEBA_DEAD) &&
9019         AmoebaNr[x][y] != new_group_nr)
9020     {
9021       int old_group_nr = AmoebaNr[x][y];
9022
9023       if (old_group_nr == 0)
9024         return;
9025
9026       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9027       AmoebaCnt[old_group_nr] = 0;
9028       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9029       AmoebaCnt2[old_group_nr] = 0;
9030
9031       SCAN_PLAYFIELD(xx, yy)
9032       {
9033         if (AmoebaNr[xx][yy] == old_group_nr)
9034           AmoebaNr[xx][yy] = new_group_nr;
9035       }
9036     }
9037   }
9038 }
9039
9040 void AmoebaToDiamond(int ax, int ay)
9041 {
9042   int i, x, y;
9043
9044   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9045   {
9046     int group_nr = AmoebaNr[ax][ay];
9047
9048 #ifdef DEBUG
9049     if (group_nr == 0)
9050     {
9051       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9052       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9053
9054       return;
9055     }
9056 #endif
9057
9058     SCAN_PLAYFIELD(x, y)
9059     {
9060       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9061       {
9062         AmoebaNr[x][y] = 0;
9063         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9064       }
9065     }
9066
9067     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9068                             SND_AMOEBA_TURNING_TO_GEM :
9069                             SND_AMOEBA_TURNING_TO_ROCK));
9070     Bang(ax, ay);
9071   }
9072   else
9073   {
9074     static int xy[4][2] =
9075     {
9076       { 0, -1 },
9077       { -1, 0 },
9078       { +1, 0 },
9079       { 0, +1 }
9080     };
9081
9082     for (i = 0; i < NUM_DIRECTIONS; i++)
9083     {
9084       x = ax + xy[i][0];
9085       y = ay + xy[i][1];
9086
9087       if (!IN_LEV_FIELD(x, y))
9088         continue;
9089
9090       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9091       {
9092         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9093                               SND_AMOEBA_TURNING_TO_GEM :
9094                               SND_AMOEBA_TURNING_TO_ROCK));
9095         Bang(x, y);
9096       }
9097     }
9098   }
9099 }
9100
9101 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9102 {
9103   int x, y;
9104   int group_nr = AmoebaNr[ax][ay];
9105   boolean done = FALSE;
9106
9107 #ifdef DEBUG
9108   if (group_nr == 0)
9109   {
9110     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9111     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9112
9113     return;
9114   }
9115 #endif
9116
9117   SCAN_PLAYFIELD(x, y)
9118   {
9119     if (AmoebaNr[x][y] == group_nr &&
9120         (Tile[x][y] == EL_AMOEBA_DEAD ||
9121          Tile[x][y] == EL_BD_AMOEBA ||
9122          Tile[x][y] == EL_AMOEBA_GROWING))
9123     {
9124       AmoebaNr[x][y] = 0;
9125       Tile[x][y] = new_element;
9126       InitField(x, y, FALSE);
9127       TEST_DrawLevelField(x, y);
9128       done = TRUE;
9129     }
9130   }
9131
9132   if (done)
9133     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9134                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9135                             SND_BD_AMOEBA_TURNING_TO_GEM));
9136 }
9137
9138 static void AmoebaGrowing(int x, int y)
9139 {
9140   static unsigned int sound_delay = 0;
9141   static unsigned int sound_delay_value = 0;
9142
9143   if (!MovDelay[x][y])          // start new growing cycle
9144   {
9145     MovDelay[x][y] = 7;
9146
9147     if (DelayReached(&sound_delay, sound_delay_value))
9148     {
9149       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9150       sound_delay_value = 30;
9151     }
9152   }
9153
9154   if (MovDelay[x][y])           // wait some time before growing bigger
9155   {
9156     MovDelay[x][y]--;
9157     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9158     {
9159       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9160                                            6 - MovDelay[x][y]);
9161
9162       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9163     }
9164
9165     if (!MovDelay[x][y])
9166     {
9167       Tile[x][y] = Store[x][y];
9168       Store[x][y] = 0;
9169       TEST_DrawLevelField(x, y);
9170     }
9171   }
9172 }
9173
9174 static void AmoebaShrinking(int x, int y)
9175 {
9176   static unsigned int sound_delay = 0;
9177   static unsigned int sound_delay_value = 0;
9178
9179   if (!MovDelay[x][y])          // start new shrinking cycle
9180   {
9181     MovDelay[x][y] = 7;
9182
9183     if (DelayReached(&sound_delay, sound_delay_value))
9184       sound_delay_value = 30;
9185   }
9186
9187   if (MovDelay[x][y])           // wait some time before shrinking
9188   {
9189     MovDelay[x][y]--;
9190     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9191     {
9192       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9193                                            6 - MovDelay[x][y]);
9194
9195       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9196     }
9197
9198     if (!MovDelay[x][y])
9199     {
9200       Tile[x][y] = EL_EMPTY;
9201       TEST_DrawLevelField(x, y);
9202
9203       // don't let mole enter this field in this cycle;
9204       // (give priority to objects falling to this field from above)
9205       Stop[x][y] = TRUE;
9206     }
9207   }
9208 }
9209
9210 static void AmoebaReproduce(int ax, int ay)
9211 {
9212   int i;
9213   int element = Tile[ax][ay];
9214   int graphic = el2img(element);
9215   int newax = ax, neway = ay;
9216   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9217   static int xy[4][2] =
9218   {
9219     { 0, -1 },
9220     { -1, 0 },
9221     { +1, 0 },
9222     { 0, +1 }
9223   };
9224
9225   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9226   {
9227     Tile[ax][ay] = EL_AMOEBA_DEAD;
9228     TEST_DrawLevelField(ax, ay);
9229     return;
9230   }
9231
9232   if (IS_ANIMATED(graphic))
9233     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9234
9235   if (!MovDelay[ax][ay])        // start making new amoeba field
9236     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9237
9238   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9239   {
9240     MovDelay[ax][ay]--;
9241     if (MovDelay[ax][ay])
9242       return;
9243   }
9244
9245   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9246   {
9247     int start = RND(4);
9248     int x = ax + xy[start][0];
9249     int y = ay + xy[start][1];
9250
9251     if (!IN_LEV_FIELD(x, y))
9252       return;
9253
9254     if (IS_FREE(x, y) ||
9255         CAN_GROW_INTO(Tile[x][y]) ||
9256         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9257         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9258     {
9259       newax = x;
9260       neway = y;
9261     }
9262
9263     if (newax == ax && neway == ay)
9264       return;
9265   }
9266   else                          // normal or "filled" (BD style) amoeba
9267   {
9268     int start = RND(4);
9269     boolean waiting_for_player = FALSE;
9270
9271     for (i = 0; i < NUM_DIRECTIONS; i++)
9272     {
9273       int j = (start + i) % 4;
9274       int x = ax + xy[j][0];
9275       int y = ay + xy[j][1];
9276
9277       if (!IN_LEV_FIELD(x, y))
9278         continue;
9279
9280       if (IS_FREE(x, y) ||
9281           CAN_GROW_INTO(Tile[x][y]) ||
9282           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9283           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9284       {
9285         newax = x;
9286         neway = y;
9287         break;
9288       }
9289       else if (IS_PLAYER(x, y))
9290         waiting_for_player = TRUE;
9291     }
9292
9293     if (newax == ax && neway == ay)             // amoeba cannot grow
9294     {
9295       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9296       {
9297         Tile[ax][ay] = EL_AMOEBA_DEAD;
9298         TEST_DrawLevelField(ax, ay);
9299         AmoebaCnt[AmoebaNr[ax][ay]]--;
9300
9301         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9302         {
9303           if (element == EL_AMOEBA_FULL)
9304             AmoebaToDiamond(ax, ay);
9305           else if (element == EL_BD_AMOEBA)
9306             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9307         }
9308       }
9309       return;
9310     }
9311     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9312     {
9313       // amoeba gets larger by growing in some direction
9314
9315       int new_group_nr = AmoebaNr[ax][ay];
9316
9317 #ifdef DEBUG
9318   if (new_group_nr == 0)
9319   {
9320     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9321           newax, neway);
9322     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9323
9324     return;
9325   }
9326 #endif
9327
9328       AmoebaNr[newax][neway] = new_group_nr;
9329       AmoebaCnt[new_group_nr]++;
9330       AmoebaCnt2[new_group_nr]++;
9331
9332       // if amoeba touches other amoeba(s) after growing, unify them
9333       AmoebaMerge(newax, neway);
9334
9335       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9336       {
9337         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9338         return;
9339       }
9340     }
9341   }
9342
9343   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9344       (neway == lev_fieldy - 1 && newax != ax))
9345   {
9346     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9347     Store[newax][neway] = element;
9348   }
9349   else if (neway == ay || element == EL_EMC_DRIPPER)
9350   {
9351     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9352
9353     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9354   }
9355   else
9356   {
9357     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9358     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9359     Store[ax][ay] = EL_AMOEBA_DROP;
9360     ContinueMoving(ax, ay);
9361     return;
9362   }
9363
9364   TEST_DrawLevelField(newax, neway);
9365 }
9366
9367 static void Life(int ax, int ay)
9368 {
9369   int x1, y1, x2, y2;
9370   int life_time = 40;
9371   int element = Tile[ax][ay];
9372   int graphic = el2img(element);
9373   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9374                          level.biomaze);
9375   boolean changed = FALSE;
9376
9377   if (IS_ANIMATED(graphic))
9378     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9379
9380   if (Stop[ax][ay])
9381     return;
9382
9383   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9384     MovDelay[ax][ay] = life_time;
9385
9386   if (MovDelay[ax][ay])         // wait some time before next cycle
9387   {
9388     MovDelay[ax][ay]--;
9389     if (MovDelay[ax][ay])
9390       return;
9391   }
9392
9393   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9394   {
9395     int xx = ax+x1, yy = ay+y1;
9396     int old_element = Tile[xx][yy];
9397     int num_neighbours = 0;
9398
9399     if (!IN_LEV_FIELD(xx, yy))
9400       continue;
9401
9402     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9403     {
9404       int x = xx+x2, y = yy+y2;
9405
9406       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9407         continue;
9408
9409       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9410       boolean is_neighbour = FALSE;
9411
9412       if (level.use_life_bugs)
9413         is_neighbour =
9414           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9415            (IS_FREE(x, y)                             &&  Stop[x][y]));
9416       else
9417         is_neighbour =
9418           (Last[x][y] == element || is_player_cell);
9419
9420       if (is_neighbour)
9421         num_neighbours++;
9422     }
9423
9424     boolean is_free = FALSE;
9425
9426     if (level.use_life_bugs)
9427       is_free = (IS_FREE(xx, yy));
9428     else
9429       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9430
9431     if (xx == ax && yy == ay)           // field in the middle
9432     {
9433       if (num_neighbours < life_parameter[0] ||
9434           num_neighbours > life_parameter[1])
9435       {
9436         Tile[xx][yy] = EL_EMPTY;
9437         if (Tile[xx][yy] != old_element)
9438           TEST_DrawLevelField(xx, yy);
9439         Stop[xx][yy] = TRUE;
9440         changed = TRUE;
9441       }
9442     }
9443     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9444     {                                   // free border field
9445       if (num_neighbours >= life_parameter[2] &&
9446           num_neighbours <= life_parameter[3])
9447       {
9448         Tile[xx][yy] = element;
9449         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9450         if (Tile[xx][yy] != old_element)
9451           TEST_DrawLevelField(xx, yy);
9452         Stop[xx][yy] = TRUE;
9453         changed = TRUE;
9454       }
9455     }
9456   }
9457
9458   if (changed)
9459     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9460                    SND_GAME_OF_LIFE_GROWING);
9461 }
9462
9463 static void InitRobotWheel(int x, int y)
9464 {
9465   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9466 }
9467
9468 static void RunRobotWheel(int x, int y)
9469 {
9470   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9471 }
9472
9473 static void StopRobotWheel(int x, int y)
9474 {
9475   if (game.robot_wheel_x == x &&
9476       game.robot_wheel_y == y)
9477   {
9478     game.robot_wheel_x = -1;
9479     game.robot_wheel_y = -1;
9480     game.robot_wheel_active = FALSE;
9481   }
9482 }
9483
9484 static void InitTimegateWheel(int x, int y)
9485 {
9486   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9487 }
9488
9489 static void RunTimegateWheel(int x, int y)
9490 {
9491   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9492 }
9493
9494 static void InitMagicBallDelay(int x, int y)
9495 {
9496   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9497 }
9498
9499 static void ActivateMagicBall(int bx, int by)
9500 {
9501   int x, y;
9502
9503   if (level.ball_random)
9504   {
9505     int pos_border = RND(8);    // select one of the eight border elements
9506     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9507     int xx = pos_content % 3;
9508     int yy = pos_content / 3;
9509
9510     x = bx - 1 + xx;
9511     y = by - 1 + yy;
9512
9513     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9514       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9515   }
9516   else
9517   {
9518     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9519     {
9520       int xx = x - bx + 1;
9521       int yy = y - by + 1;
9522
9523       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9524         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9525     }
9526   }
9527
9528   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9529 }
9530
9531 static void CheckExit(int x, int y)
9532 {
9533   if (game.gems_still_needed > 0 ||
9534       game.sokoban_fields_still_needed > 0 ||
9535       game.sokoban_objects_still_needed > 0 ||
9536       game.lights_still_needed > 0)
9537   {
9538     int element = Tile[x][y];
9539     int graphic = el2img(element);
9540
9541     if (IS_ANIMATED(graphic))
9542       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9543
9544     return;
9545   }
9546
9547   // do not re-open exit door closed after last player
9548   if (game.all_players_gone)
9549     return;
9550
9551   Tile[x][y] = EL_EXIT_OPENING;
9552
9553   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9554 }
9555
9556 static void CheckExitEM(int x, int y)
9557 {
9558   if (game.gems_still_needed > 0 ||
9559       game.sokoban_fields_still_needed > 0 ||
9560       game.sokoban_objects_still_needed > 0 ||
9561       game.lights_still_needed > 0)
9562   {
9563     int element = Tile[x][y];
9564     int graphic = el2img(element);
9565
9566     if (IS_ANIMATED(graphic))
9567       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9568
9569     return;
9570   }
9571
9572   // do not re-open exit door closed after last player
9573   if (game.all_players_gone)
9574     return;
9575
9576   Tile[x][y] = EL_EM_EXIT_OPENING;
9577
9578   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9579 }
9580
9581 static void CheckExitSteel(int x, int y)
9582 {
9583   if (game.gems_still_needed > 0 ||
9584       game.sokoban_fields_still_needed > 0 ||
9585       game.sokoban_objects_still_needed > 0 ||
9586       game.lights_still_needed > 0)
9587   {
9588     int element = Tile[x][y];
9589     int graphic = el2img(element);
9590
9591     if (IS_ANIMATED(graphic))
9592       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9593
9594     return;
9595   }
9596
9597   // do not re-open exit door closed after last player
9598   if (game.all_players_gone)
9599     return;
9600
9601   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9602
9603   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9604 }
9605
9606 static void CheckExitSteelEM(int x, int y)
9607 {
9608   if (game.gems_still_needed > 0 ||
9609       game.sokoban_fields_still_needed > 0 ||
9610       game.sokoban_objects_still_needed > 0 ||
9611       game.lights_still_needed > 0)
9612   {
9613     int element = Tile[x][y];
9614     int graphic = el2img(element);
9615
9616     if (IS_ANIMATED(graphic))
9617       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9618
9619     return;
9620   }
9621
9622   // do not re-open exit door closed after last player
9623   if (game.all_players_gone)
9624     return;
9625
9626   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9627
9628   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9629 }
9630
9631 static void CheckExitSP(int x, int y)
9632 {
9633   if (game.gems_still_needed > 0)
9634   {
9635     int element = Tile[x][y];
9636     int graphic = el2img(element);
9637
9638     if (IS_ANIMATED(graphic))
9639       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9640
9641     return;
9642   }
9643
9644   // do not re-open exit door closed after last player
9645   if (game.all_players_gone)
9646     return;
9647
9648   Tile[x][y] = EL_SP_EXIT_OPENING;
9649
9650   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9651 }
9652
9653 static void CloseAllOpenTimegates(void)
9654 {
9655   int x, y;
9656
9657   SCAN_PLAYFIELD(x, y)
9658   {
9659     int element = Tile[x][y];
9660
9661     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9662     {
9663       Tile[x][y] = EL_TIMEGATE_CLOSING;
9664
9665       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9666     }
9667   }
9668 }
9669
9670 static void DrawTwinkleOnField(int x, int y)
9671 {
9672   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9673     return;
9674
9675   if (Tile[x][y] == EL_BD_DIAMOND)
9676     return;
9677
9678   if (MovDelay[x][y] == 0)      // next animation frame
9679     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9680
9681   if (MovDelay[x][y] != 0)      // wait some time before next frame
9682   {
9683     MovDelay[x][y]--;
9684
9685     DrawLevelElementAnimation(x, y, Tile[x][y]);
9686
9687     if (MovDelay[x][y] != 0)
9688     {
9689       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9690                                            10 - MovDelay[x][y]);
9691
9692       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9693     }
9694   }
9695 }
9696
9697 static void MauerWaechst(int x, int y)
9698 {
9699   int delay = 6;
9700
9701   if (!MovDelay[x][y])          // next animation frame
9702     MovDelay[x][y] = 3 * delay;
9703
9704   if (MovDelay[x][y])           // wait some time before next frame
9705   {
9706     MovDelay[x][y]--;
9707
9708     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9709     {
9710       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9711       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9712
9713       DrawLevelGraphic(x, y, graphic, frame);
9714     }
9715
9716     if (!MovDelay[x][y])
9717     {
9718       if (MovDir[x][y] == MV_LEFT)
9719       {
9720         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9721           TEST_DrawLevelField(x - 1, y);
9722       }
9723       else if (MovDir[x][y] == MV_RIGHT)
9724       {
9725         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9726           TEST_DrawLevelField(x + 1, y);
9727       }
9728       else if (MovDir[x][y] == MV_UP)
9729       {
9730         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9731           TEST_DrawLevelField(x, y - 1);
9732       }
9733       else
9734       {
9735         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9736           TEST_DrawLevelField(x, y + 1);
9737       }
9738
9739       Tile[x][y] = Store[x][y];
9740       Store[x][y] = 0;
9741       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9742       TEST_DrawLevelField(x, y);
9743     }
9744   }
9745 }
9746
9747 static void MauerAbleger(int ax, int ay)
9748 {
9749   int element = Tile[ax][ay];
9750   int graphic = el2img(element);
9751   boolean oben_frei = FALSE, unten_frei = FALSE;
9752   boolean links_frei = FALSE, rechts_frei = FALSE;
9753   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9754   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9755   boolean new_wall = FALSE;
9756
9757   if (IS_ANIMATED(graphic))
9758     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9759
9760   if (!MovDelay[ax][ay])        // start building new wall
9761     MovDelay[ax][ay] = 6;
9762
9763   if (MovDelay[ax][ay])         // wait some time before building new wall
9764   {
9765     MovDelay[ax][ay]--;
9766     if (MovDelay[ax][ay])
9767       return;
9768   }
9769
9770   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9771     oben_frei = TRUE;
9772   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9773     unten_frei = TRUE;
9774   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9775     links_frei = TRUE;
9776   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9777     rechts_frei = TRUE;
9778
9779   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9780       element == EL_EXPANDABLE_WALL_ANY)
9781   {
9782     if (oben_frei)
9783     {
9784       Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING;
9785       Store[ax][ay - 1] = element;
9786       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9787       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9788         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9789       new_wall = TRUE;
9790     }
9791     if (unten_frei)
9792     {
9793       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9794       Store[ax][ay + 1] = element;
9795       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9796       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9797         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9798       new_wall = TRUE;
9799     }
9800   }
9801
9802   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9803       element == EL_EXPANDABLE_WALL_ANY ||
9804       element == EL_EXPANDABLE_WALL ||
9805       element == EL_BD_EXPANDABLE_WALL)
9806   {
9807     if (links_frei)
9808     {
9809       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9810       Store[ax - 1][ay] = element;
9811       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9812       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9813         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9814       new_wall = TRUE;
9815     }
9816
9817     if (rechts_frei)
9818     {
9819       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9820       Store[ax + 1][ay] = element;
9821       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9822       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9823         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9824       new_wall = TRUE;
9825     }
9826   }
9827
9828   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9829     TEST_DrawLevelField(ax, ay);
9830
9831   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9832     oben_massiv = TRUE;
9833   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9834     unten_massiv = TRUE;
9835   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9836     links_massiv = TRUE;
9837   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9838     rechts_massiv = TRUE;
9839
9840   if (((oben_massiv && unten_massiv) ||
9841        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9842        element == EL_EXPANDABLE_WALL) &&
9843       ((links_massiv && rechts_massiv) ||
9844        element == EL_EXPANDABLE_WALL_VERTICAL))
9845     Tile[ax][ay] = EL_WALL;
9846
9847   if (new_wall)
9848     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9849 }
9850
9851 static void MauerAblegerStahl(int ax, int ay)
9852 {
9853   int element = Tile[ax][ay];
9854   int graphic = el2img(element);
9855   boolean oben_frei = FALSE, unten_frei = FALSE;
9856   boolean links_frei = FALSE, rechts_frei = FALSE;
9857   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9858   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9859   boolean new_wall = FALSE;
9860
9861   if (IS_ANIMATED(graphic))
9862     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9863
9864   if (!MovDelay[ax][ay])        // start building new wall
9865     MovDelay[ax][ay] = 6;
9866
9867   if (MovDelay[ax][ay])         // wait some time before building new wall
9868   {
9869     MovDelay[ax][ay]--;
9870     if (MovDelay[ax][ay])
9871       return;
9872   }
9873
9874   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9875     oben_frei = TRUE;
9876   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9877     unten_frei = TRUE;
9878   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9879     links_frei = TRUE;
9880   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9881     rechts_frei = TRUE;
9882
9883   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9884       element == EL_EXPANDABLE_STEELWALL_ANY)
9885   {
9886     if (oben_frei)
9887     {
9888       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9889       Store[ax][ay - 1] = element;
9890       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9891       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9892         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9893       new_wall = TRUE;
9894     }
9895     if (unten_frei)
9896     {
9897       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9898       Store[ax][ay + 1] = element;
9899       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9900       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9901         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9902       new_wall = TRUE;
9903     }
9904   }
9905
9906   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9907       element == EL_EXPANDABLE_STEELWALL_ANY)
9908   {
9909     if (links_frei)
9910     {
9911       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9912       Store[ax - 1][ay] = element;
9913       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9914       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9915         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9916       new_wall = TRUE;
9917     }
9918
9919     if (rechts_frei)
9920     {
9921       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9922       Store[ax + 1][ay] = element;
9923       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9924       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9925         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9926       new_wall = TRUE;
9927     }
9928   }
9929
9930   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9931     oben_massiv = TRUE;
9932   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9933     unten_massiv = TRUE;
9934   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9935     links_massiv = TRUE;
9936   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9937     rechts_massiv = TRUE;
9938
9939   if (((oben_massiv && unten_massiv) ||
9940        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9941       ((links_massiv && rechts_massiv) ||
9942        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9943     Tile[ax][ay] = EL_STEELWALL;
9944
9945   if (new_wall)
9946     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9947 }
9948
9949 static void CheckForDragon(int x, int y)
9950 {
9951   int i, j;
9952   boolean dragon_found = FALSE;
9953   static int xy[4][2] =
9954   {
9955     { 0, -1 },
9956     { -1, 0 },
9957     { +1, 0 },
9958     { 0, +1 }
9959   };
9960
9961   for (i = 0; i < NUM_DIRECTIONS; i++)
9962   {
9963     for (j = 0; j < 4; j++)
9964     {
9965       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9966
9967       if (IN_LEV_FIELD(xx, yy) &&
9968           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9969       {
9970         if (Tile[xx][yy] == EL_DRAGON)
9971           dragon_found = TRUE;
9972       }
9973       else
9974         break;
9975     }
9976   }
9977
9978   if (!dragon_found)
9979   {
9980     for (i = 0; i < NUM_DIRECTIONS; i++)
9981     {
9982       for (j = 0; j < 3; j++)
9983       {
9984         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9985   
9986         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9987         {
9988           Tile[xx][yy] = EL_EMPTY;
9989           TEST_DrawLevelField(xx, yy);
9990         }
9991         else
9992           break;
9993       }
9994     }
9995   }
9996 }
9997
9998 static void InitBuggyBase(int x, int y)
9999 {
10000   int element = Tile[x][y];
10001   int activating_delay = FRAMES_PER_SECOND / 4;
10002
10003   ChangeDelay[x][y] =
10004     (element == EL_SP_BUGGY_BASE ?
10005      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10006      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10007      activating_delay :
10008      element == EL_SP_BUGGY_BASE_ACTIVE ?
10009      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10010 }
10011
10012 static void WarnBuggyBase(int x, int y)
10013 {
10014   int i;
10015   static int xy[4][2] =
10016   {
10017     { 0, -1 },
10018     { -1, 0 },
10019     { +1, 0 },
10020     { 0, +1 }
10021   };
10022
10023   for (i = 0; i < NUM_DIRECTIONS; i++)
10024   {
10025     int xx = x + xy[i][0];
10026     int yy = y + xy[i][1];
10027
10028     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10029     {
10030       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10031
10032       break;
10033     }
10034   }
10035 }
10036
10037 static void InitTrap(int x, int y)
10038 {
10039   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10040 }
10041
10042 static void ActivateTrap(int x, int y)
10043 {
10044   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10045 }
10046
10047 static void ChangeActiveTrap(int x, int y)
10048 {
10049   int graphic = IMG_TRAP_ACTIVE;
10050
10051   // if new animation frame was drawn, correct crumbled sand border
10052   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10053     TEST_DrawLevelFieldCrumbled(x, y);
10054 }
10055
10056 static int getSpecialActionElement(int element, int number, int base_element)
10057 {
10058   return (element != EL_EMPTY ? element :
10059           number != -1 ? base_element + number - 1 :
10060           EL_EMPTY);
10061 }
10062
10063 static int getModifiedActionNumber(int value_old, int operator, int operand,
10064                                    int value_min, int value_max)
10065 {
10066   int value_new = (operator == CA_MODE_SET      ? operand :
10067                    operator == CA_MODE_ADD      ? value_old + operand :
10068                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10069                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10070                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10071                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10072                    value_old);
10073
10074   return (value_new < value_min ? value_min :
10075           value_new > value_max ? value_max :
10076           value_new);
10077 }
10078
10079 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10080 {
10081   struct ElementInfo *ei = &element_info[element];
10082   struct ElementChangeInfo *change = &ei->change_page[page];
10083   int target_element = change->target_element;
10084   int action_type = change->action_type;
10085   int action_mode = change->action_mode;
10086   int action_arg = change->action_arg;
10087   int action_element = change->action_element;
10088   int i;
10089
10090   if (!change->has_action)
10091     return;
10092
10093   // ---------- determine action paramater values -----------------------------
10094
10095   int level_time_value =
10096     (level.time > 0 ? TimeLeft :
10097      TimePlayed);
10098
10099   int action_arg_element_raw =
10100     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10101      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10102      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10103      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10104      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10105      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10106      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10107      EL_EMPTY);
10108   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10109
10110   int action_arg_direction =
10111     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10112      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10113      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10114      change->actual_trigger_side :
10115      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10116      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10117      MV_NONE);
10118
10119   int action_arg_number_min =
10120     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10121      CA_ARG_MIN);
10122
10123   int action_arg_number_max =
10124     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10125      action_type == CA_SET_LEVEL_GEMS ? 999 :
10126      action_type == CA_SET_LEVEL_TIME ? 9999 :
10127      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10128      action_type == CA_SET_CE_VALUE ? 9999 :
10129      action_type == CA_SET_CE_SCORE ? 9999 :
10130      CA_ARG_MAX);
10131
10132   int action_arg_number_reset =
10133     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10134      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10135      action_type == CA_SET_LEVEL_TIME ? level.time :
10136      action_type == CA_SET_LEVEL_SCORE ? 0 :
10137      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10138      action_type == CA_SET_CE_SCORE ? 0 :
10139      0);
10140
10141   int action_arg_number =
10142     (action_arg <= CA_ARG_MAX ? action_arg :
10143      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10144      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10145      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10146      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10147      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10148      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10149      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10150      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10151      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10152      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10153      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10154      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10155      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10156      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10157      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10158      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10159      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10160      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10161      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10162      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10163      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10164      -1);
10165
10166   int action_arg_number_old =
10167     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10168      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10169      action_type == CA_SET_LEVEL_SCORE ? game.score :
10170      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10171      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10172      0);
10173
10174   int action_arg_number_new =
10175     getModifiedActionNumber(action_arg_number_old,
10176                             action_mode, action_arg_number,
10177                             action_arg_number_min, action_arg_number_max);
10178
10179   int trigger_player_bits =
10180     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10181      change->actual_trigger_player_bits : change->trigger_player);
10182
10183   int action_arg_player_bits =
10184     (action_arg >= CA_ARG_PLAYER_1 &&
10185      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10186      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10187      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10188      PLAYER_BITS_ANY);
10189
10190   // ---------- execute action  -----------------------------------------------
10191
10192   switch (action_type)
10193   {
10194     case CA_NO_ACTION:
10195     {
10196       return;
10197     }
10198
10199     // ---------- level actions  ----------------------------------------------
10200
10201     case CA_RESTART_LEVEL:
10202     {
10203       game.restart_level = TRUE;
10204
10205       break;
10206     }
10207
10208     case CA_SHOW_ENVELOPE:
10209     {
10210       int element = getSpecialActionElement(action_arg_element,
10211                                             action_arg_number, EL_ENVELOPE_1);
10212
10213       if (IS_ENVELOPE(element))
10214         local_player->show_envelope = element;
10215
10216       break;
10217     }
10218
10219     case CA_SET_LEVEL_TIME:
10220     {
10221       if (level.time > 0)       // only modify limited time value
10222       {
10223         TimeLeft = action_arg_number_new;
10224
10225         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10226
10227         DisplayGameControlValues();
10228
10229         if (!TimeLeft && setup.time_limit)
10230           for (i = 0; i < MAX_PLAYERS; i++)
10231             KillPlayer(&stored_player[i]);
10232       }
10233
10234       break;
10235     }
10236
10237     case CA_SET_LEVEL_SCORE:
10238     {
10239       game.score = action_arg_number_new;
10240
10241       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10242
10243       DisplayGameControlValues();
10244
10245       break;
10246     }
10247
10248     case CA_SET_LEVEL_GEMS:
10249     {
10250       game.gems_still_needed = action_arg_number_new;
10251
10252       game.snapshot.collected_item = TRUE;
10253
10254       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10255
10256       DisplayGameControlValues();
10257
10258       break;
10259     }
10260
10261     case CA_SET_LEVEL_WIND:
10262     {
10263       game.wind_direction = action_arg_direction;
10264
10265       break;
10266     }
10267
10268     case CA_SET_LEVEL_RANDOM_SEED:
10269     {
10270       // ensure that setting a new random seed while playing is predictable
10271       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10272
10273       break;
10274     }
10275
10276     // ---------- player actions  ---------------------------------------------
10277
10278     case CA_MOVE_PLAYER:
10279     case CA_MOVE_PLAYER_NEW:
10280     {
10281       // automatically move to the next field in specified direction
10282       for (i = 0; i < MAX_PLAYERS; i++)
10283         if (trigger_player_bits & (1 << i))
10284           if (action_type == CA_MOVE_PLAYER ||
10285               stored_player[i].MovPos == 0)
10286             stored_player[i].programmed_action = action_arg_direction;
10287
10288       break;
10289     }
10290
10291     case CA_EXIT_PLAYER:
10292     {
10293       for (i = 0; i < MAX_PLAYERS; i++)
10294         if (action_arg_player_bits & (1 << i))
10295           ExitPlayer(&stored_player[i]);
10296
10297       if (game.players_still_needed == 0)
10298         LevelSolved();
10299
10300       break;
10301     }
10302
10303     case CA_KILL_PLAYER:
10304     {
10305       for (i = 0; i < MAX_PLAYERS; i++)
10306         if (action_arg_player_bits & (1 << i))
10307           KillPlayer(&stored_player[i]);
10308
10309       break;
10310     }
10311
10312     case CA_SET_PLAYER_KEYS:
10313     {
10314       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10315       int element = getSpecialActionElement(action_arg_element,
10316                                             action_arg_number, EL_KEY_1);
10317
10318       if (IS_KEY(element))
10319       {
10320         for (i = 0; i < MAX_PLAYERS; i++)
10321         {
10322           if (trigger_player_bits & (1 << i))
10323           {
10324             stored_player[i].key[KEY_NR(element)] = key_state;
10325
10326             DrawGameDoorValues();
10327           }
10328         }
10329       }
10330
10331       break;
10332     }
10333
10334     case CA_SET_PLAYER_SPEED:
10335     {
10336       for (i = 0; i < MAX_PLAYERS; i++)
10337       {
10338         if (trigger_player_bits & (1 << i))
10339         {
10340           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10341
10342           if (action_arg == CA_ARG_SPEED_FASTER &&
10343               stored_player[i].cannot_move)
10344           {
10345             action_arg_number = STEPSIZE_VERY_SLOW;
10346           }
10347           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10348                    action_arg == CA_ARG_SPEED_FASTER)
10349           {
10350             action_arg_number = 2;
10351             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10352                            CA_MODE_MULTIPLY);
10353           }
10354           else if (action_arg == CA_ARG_NUMBER_RESET)
10355           {
10356             action_arg_number = level.initial_player_stepsize[i];
10357           }
10358
10359           move_stepsize =
10360             getModifiedActionNumber(move_stepsize,
10361                                     action_mode,
10362                                     action_arg_number,
10363                                     action_arg_number_min,
10364                                     action_arg_number_max);
10365
10366           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10367         }
10368       }
10369
10370       break;
10371     }
10372
10373     case CA_SET_PLAYER_SHIELD:
10374     {
10375       for (i = 0; i < MAX_PLAYERS; i++)
10376       {
10377         if (trigger_player_bits & (1 << i))
10378         {
10379           if (action_arg == CA_ARG_SHIELD_OFF)
10380           {
10381             stored_player[i].shield_normal_time_left = 0;
10382             stored_player[i].shield_deadly_time_left = 0;
10383           }
10384           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10385           {
10386             stored_player[i].shield_normal_time_left = 999999;
10387           }
10388           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10389           {
10390             stored_player[i].shield_normal_time_left = 999999;
10391             stored_player[i].shield_deadly_time_left = 999999;
10392           }
10393         }
10394       }
10395
10396       break;
10397     }
10398
10399     case CA_SET_PLAYER_GRAVITY:
10400     {
10401       for (i = 0; i < MAX_PLAYERS; i++)
10402       {
10403         if (trigger_player_bits & (1 << i))
10404         {
10405           stored_player[i].gravity =
10406             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10407              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10408              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10409              stored_player[i].gravity);
10410         }
10411       }
10412
10413       break;
10414     }
10415
10416     case CA_SET_PLAYER_ARTWORK:
10417     {
10418       for (i = 0; i < MAX_PLAYERS; i++)
10419       {
10420         if (trigger_player_bits & (1 << i))
10421         {
10422           int artwork_element = action_arg_element;
10423
10424           if (action_arg == CA_ARG_ELEMENT_RESET)
10425             artwork_element =
10426               (level.use_artwork_element[i] ? level.artwork_element[i] :
10427                stored_player[i].element_nr);
10428
10429           if (stored_player[i].artwork_element != artwork_element)
10430             stored_player[i].Frame = 0;
10431
10432           stored_player[i].artwork_element = artwork_element;
10433
10434           SetPlayerWaiting(&stored_player[i], FALSE);
10435
10436           // set number of special actions for bored and sleeping animation
10437           stored_player[i].num_special_action_bored =
10438             get_num_special_action(artwork_element,
10439                                    ACTION_BORING_1, ACTION_BORING_LAST);
10440           stored_player[i].num_special_action_sleeping =
10441             get_num_special_action(artwork_element,
10442                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10443         }
10444       }
10445
10446       break;
10447     }
10448
10449     case CA_SET_PLAYER_INVENTORY:
10450     {
10451       for (i = 0; i < MAX_PLAYERS; i++)
10452       {
10453         struct PlayerInfo *player = &stored_player[i];
10454         int j, k;
10455
10456         if (trigger_player_bits & (1 << i))
10457         {
10458           int inventory_element = action_arg_element;
10459
10460           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10461               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10462               action_arg == CA_ARG_ELEMENT_ACTION)
10463           {
10464             int element = inventory_element;
10465             int collect_count = element_info[element].collect_count_initial;
10466
10467             if (!IS_CUSTOM_ELEMENT(element))
10468               collect_count = 1;
10469
10470             if (collect_count == 0)
10471               player->inventory_infinite_element = element;
10472             else
10473               for (k = 0; k < collect_count; k++)
10474                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10475                   player->inventory_element[player->inventory_size++] =
10476                     element;
10477           }
10478           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10479                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10480                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10481           {
10482             if (player->inventory_infinite_element != EL_UNDEFINED &&
10483                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10484                                      action_arg_element_raw))
10485               player->inventory_infinite_element = EL_UNDEFINED;
10486
10487             for (k = 0, j = 0; j < player->inventory_size; j++)
10488             {
10489               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10490                                         action_arg_element_raw))
10491                 player->inventory_element[k++] = player->inventory_element[j];
10492             }
10493
10494             player->inventory_size = k;
10495           }
10496           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10497           {
10498             if (player->inventory_size > 0)
10499             {
10500               for (j = 0; j < player->inventory_size - 1; j++)
10501                 player->inventory_element[j] = player->inventory_element[j + 1];
10502
10503               player->inventory_size--;
10504             }
10505           }
10506           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10507           {
10508             if (player->inventory_size > 0)
10509               player->inventory_size--;
10510           }
10511           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10512           {
10513             player->inventory_infinite_element = EL_UNDEFINED;
10514             player->inventory_size = 0;
10515           }
10516           else if (action_arg == CA_ARG_INVENTORY_RESET)
10517           {
10518             player->inventory_infinite_element = EL_UNDEFINED;
10519             player->inventory_size = 0;
10520
10521             if (level.use_initial_inventory[i])
10522             {
10523               for (j = 0; j < level.initial_inventory_size[i]; j++)
10524               {
10525                 int element = level.initial_inventory_content[i][j];
10526                 int collect_count = element_info[element].collect_count_initial;
10527
10528                 if (!IS_CUSTOM_ELEMENT(element))
10529                   collect_count = 1;
10530
10531                 if (collect_count == 0)
10532                   player->inventory_infinite_element = element;
10533                 else
10534                   for (k = 0; k < collect_count; k++)
10535                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10536                       player->inventory_element[player->inventory_size++] =
10537                         element;
10538               }
10539             }
10540           }
10541         }
10542       }
10543
10544       break;
10545     }
10546
10547     // ---------- CE actions  -------------------------------------------------
10548
10549     case CA_SET_CE_VALUE:
10550     {
10551       int last_ce_value = CustomValue[x][y];
10552
10553       CustomValue[x][y] = action_arg_number_new;
10554
10555       if (CustomValue[x][y] != last_ce_value)
10556       {
10557         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10558         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10559
10560         if (CustomValue[x][y] == 0)
10561         {
10562           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10563           ChangeCount[x][y] = 0;        // allow at least one more change
10564
10565           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10566           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10567         }
10568       }
10569
10570       break;
10571     }
10572
10573     case CA_SET_CE_SCORE:
10574     {
10575       int last_ce_score = ei->collect_score;
10576
10577       ei->collect_score = action_arg_number_new;
10578
10579       if (ei->collect_score != last_ce_score)
10580       {
10581         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10582         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10583
10584         if (ei->collect_score == 0)
10585         {
10586           int xx, yy;
10587
10588           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10589           ChangeCount[x][y] = 0;        // allow at least one more change
10590
10591           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10592           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10593
10594           /*
10595             This is a very special case that seems to be a mixture between
10596             CheckElementChange() and CheckTriggeredElementChange(): while
10597             the first one only affects single elements that are triggered
10598             directly, the second one affects multiple elements in the playfield
10599             that are triggered indirectly by another element. This is a third
10600             case: Changing the CE score always affects multiple identical CEs,
10601             so every affected CE must be checked, not only the single CE for
10602             which the CE score was changed in the first place (as every instance
10603             of that CE shares the same CE score, and therefore also can change)!
10604           */
10605           SCAN_PLAYFIELD(xx, yy)
10606           {
10607             if (Tile[xx][yy] == element)
10608               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10609                                  CE_SCORE_GETS_ZERO);
10610           }
10611         }
10612       }
10613
10614       break;
10615     }
10616
10617     case CA_SET_CE_ARTWORK:
10618     {
10619       int artwork_element = action_arg_element;
10620       boolean reset_frame = FALSE;
10621       int xx, yy;
10622
10623       if (action_arg == CA_ARG_ELEMENT_RESET)
10624         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10625                            element);
10626
10627       if (ei->gfx_element != artwork_element)
10628         reset_frame = TRUE;
10629
10630       ei->gfx_element = artwork_element;
10631
10632       SCAN_PLAYFIELD(xx, yy)
10633       {
10634         if (Tile[xx][yy] == element)
10635         {
10636           if (reset_frame)
10637           {
10638             ResetGfxAnimation(xx, yy);
10639             ResetRandomAnimationValue(xx, yy);
10640           }
10641
10642           TEST_DrawLevelField(xx, yy);
10643         }
10644       }
10645
10646       break;
10647     }
10648
10649     // ---------- engine actions  ---------------------------------------------
10650
10651     case CA_SET_ENGINE_SCAN_MODE:
10652     {
10653       InitPlayfieldScanMode(action_arg);
10654
10655       break;
10656     }
10657
10658     default:
10659       break;
10660   }
10661 }
10662
10663 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10664 {
10665   int old_element = Tile[x][y];
10666   int new_element = GetElementFromGroupElement(element);
10667   int previous_move_direction = MovDir[x][y];
10668   int last_ce_value = CustomValue[x][y];
10669   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10670   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10671   boolean add_player_onto_element = (new_element_is_player &&
10672                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10673                                      IS_WALKABLE(old_element));
10674
10675   if (!add_player_onto_element)
10676   {
10677     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10678       RemoveMovingField(x, y);
10679     else
10680       RemoveField(x, y);
10681
10682     Tile[x][y] = new_element;
10683
10684     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10685       MovDir[x][y] = previous_move_direction;
10686
10687     if (element_info[new_element].use_last_ce_value)
10688       CustomValue[x][y] = last_ce_value;
10689
10690     InitField_WithBug1(x, y, FALSE);
10691
10692     new_element = Tile[x][y];   // element may have changed
10693
10694     ResetGfxAnimation(x, y);
10695     ResetRandomAnimationValue(x, y);
10696
10697     TEST_DrawLevelField(x, y);
10698
10699     if (GFX_CRUMBLED(new_element))
10700       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10701   }
10702
10703   // check if element under the player changes from accessible to unaccessible
10704   // (needed for special case of dropping element which then changes)
10705   // (must be checked after creating new element for walkable group elements)
10706   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10707       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10708   {
10709     Bang(x, y);
10710
10711     return;
10712   }
10713
10714   // "ChangeCount" not set yet to allow "entered by player" change one time
10715   if (new_element_is_player)
10716     RelocatePlayer(x, y, new_element);
10717
10718   if (is_change)
10719     ChangeCount[x][y]++;        // count number of changes in the same frame
10720
10721   TestIfBadThingTouchesPlayer(x, y);
10722   TestIfPlayerTouchesCustomElement(x, y);
10723   TestIfElementTouchesCustomElement(x, y);
10724 }
10725
10726 static void CreateField(int x, int y, int element)
10727 {
10728   CreateFieldExt(x, y, element, FALSE);
10729 }
10730
10731 static void CreateElementFromChange(int x, int y, int element)
10732 {
10733   element = GET_VALID_RUNTIME_ELEMENT(element);
10734
10735   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10736   {
10737     int old_element = Tile[x][y];
10738
10739     // prevent changed element from moving in same engine frame
10740     // unless both old and new element can either fall or move
10741     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10742         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10743       Stop[x][y] = TRUE;
10744   }
10745
10746   CreateFieldExt(x, y, element, TRUE);
10747 }
10748
10749 static boolean ChangeElement(int x, int y, int element, int page)
10750 {
10751   struct ElementInfo *ei = &element_info[element];
10752   struct ElementChangeInfo *change = &ei->change_page[page];
10753   int ce_value = CustomValue[x][y];
10754   int ce_score = ei->collect_score;
10755   int target_element;
10756   int old_element = Tile[x][y];
10757
10758   // always use default change event to prevent running into a loop
10759   if (ChangeEvent[x][y] == -1)
10760     ChangeEvent[x][y] = CE_DELAY;
10761
10762   if (ChangeEvent[x][y] == CE_DELAY)
10763   {
10764     // reset actual trigger element, trigger player and action element
10765     change->actual_trigger_element = EL_EMPTY;
10766     change->actual_trigger_player = EL_EMPTY;
10767     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10768     change->actual_trigger_side = CH_SIDE_NONE;
10769     change->actual_trigger_ce_value = 0;
10770     change->actual_trigger_ce_score = 0;
10771   }
10772
10773   // do not change elements more than a specified maximum number of changes
10774   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10775     return FALSE;
10776
10777   ChangeCount[x][y]++;          // count number of changes in the same frame
10778
10779   if (change->explode)
10780   {
10781     Bang(x, y);
10782
10783     return TRUE;
10784   }
10785
10786   if (change->use_target_content)
10787   {
10788     boolean complete_replace = TRUE;
10789     boolean can_replace[3][3];
10790     int xx, yy;
10791
10792     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10793     {
10794       boolean is_empty;
10795       boolean is_walkable;
10796       boolean is_diggable;
10797       boolean is_collectible;
10798       boolean is_removable;
10799       boolean is_destructible;
10800       int ex = x + xx - 1;
10801       int ey = y + yy - 1;
10802       int content_element = change->target_content.e[xx][yy];
10803       int e;
10804
10805       can_replace[xx][yy] = TRUE;
10806
10807       if (ex == x && ey == y)   // do not check changing element itself
10808         continue;
10809
10810       if (content_element == EL_EMPTY_SPACE)
10811       {
10812         can_replace[xx][yy] = FALSE;    // do not replace border with space
10813
10814         continue;
10815       }
10816
10817       if (!IN_LEV_FIELD(ex, ey))
10818       {
10819         can_replace[xx][yy] = FALSE;
10820         complete_replace = FALSE;
10821
10822         continue;
10823       }
10824
10825       e = Tile[ex][ey];
10826
10827       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10828         e = MovingOrBlocked2Element(ex, ey);
10829
10830       is_empty = (IS_FREE(ex, ey) ||
10831                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10832
10833       is_walkable     = (is_empty || IS_WALKABLE(e));
10834       is_diggable     = (is_empty || IS_DIGGABLE(e));
10835       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10836       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10837       is_removable    = (is_diggable || is_collectible);
10838
10839       can_replace[xx][yy] =
10840         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10841           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10842           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10843           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10844           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10845           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10846          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10847
10848       if (!can_replace[xx][yy])
10849         complete_replace = FALSE;
10850     }
10851
10852     if (!change->only_if_complete || complete_replace)
10853     {
10854       boolean something_has_changed = FALSE;
10855
10856       if (change->only_if_complete && change->use_random_replace &&
10857           RND(100) < change->random_percentage)
10858         return FALSE;
10859
10860       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10861       {
10862         int ex = x + xx - 1;
10863         int ey = y + yy - 1;
10864         int content_element;
10865
10866         if (can_replace[xx][yy] && (!change->use_random_replace ||
10867                                     RND(100) < change->random_percentage))
10868         {
10869           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10870             RemoveMovingField(ex, ey);
10871
10872           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10873
10874           content_element = change->target_content.e[xx][yy];
10875           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10876                                               ce_value, ce_score);
10877
10878           CreateElementFromChange(ex, ey, target_element);
10879
10880           something_has_changed = TRUE;
10881
10882           // for symmetry reasons, freeze newly created border elements
10883           if (ex != x || ey != y)
10884             Stop[ex][ey] = TRUE;        // no more moving in this frame
10885         }
10886       }
10887
10888       if (something_has_changed)
10889       {
10890         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10891         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10892       }
10893     }
10894   }
10895   else
10896   {
10897     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10898                                         ce_value, ce_score);
10899
10900     if (element == EL_DIAGONAL_GROWING ||
10901         element == EL_DIAGONAL_SHRINKING)
10902     {
10903       target_element = Store[x][y];
10904
10905       Store[x][y] = EL_EMPTY;
10906     }
10907
10908     // special case: element changes to player (and may be kept if walkable)
10909     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10910       CreateElementFromChange(x, y, EL_EMPTY);
10911
10912     CreateElementFromChange(x, y, target_element);
10913
10914     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10915     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10916   }
10917
10918   // this uses direct change before indirect change
10919   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10920
10921   return TRUE;
10922 }
10923
10924 static void HandleElementChange(int x, int y, int page)
10925 {
10926   int element = MovingOrBlocked2Element(x, y);
10927   struct ElementInfo *ei = &element_info[element];
10928   struct ElementChangeInfo *change = &ei->change_page[page];
10929   boolean handle_action_before_change = FALSE;
10930
10931 #ifdef DEBUG
10932   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10933       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10934   {
10935     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10936           x, y, element, element_info[element].token_name);
10937     Debug("game:playing:HandleElementChange", "This should never happen!");
10938   }
10939 #endif
10940
10941   // this can happen with classic bombs on walkable, changing elements
10942   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10943   {
10944     return;
10945   }
10946
10947   if (ChangeDelay[x][y] == 0)           // initialize element change
10948   {
10949     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10950
10951     if (change->can_change)
10952     {
10953       // !!! not clear why graphic animation should be reset at all here !!!
10954       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10955       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10956
10957       /*
10958         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10959
10960         When using an animation frame delay of 1 (this only happens with
10961         "sp_zonk.moving.left/right" in the classic graphics), the default
10962         (non-moving) animation shows wrong animation frames (while the
10963         moving animation, like "sp_zonk.moving.left/right", is correct,
10964         so this graphical bug never shows up with the classic graphics).
10965         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10966         be drawn instead of the correct frames 0,1,2,3. This is caused by
10967         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10968         an element change: First when the change delay ("ChangeDelay[][]")
10969         counter has reached zero after decrementing, then a second time in
10970         the next frame (after "GfxFrame[][]" was already incremented) when
10971         "ChangeDelay[][]" is reset to the initial delay value again.
10972
10973         This causes frame 0 to be drawn twice, while the last frame won't
10974         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10975
10976         As some animations may already be cleverly designed around this bug
10977         (at least the "Snake Bite" snake tail animation does this), it cannot
10978         simply be fixed here without breaking such existing animations.
10979         Unfortunately, it cannot easily be detected if a graphics set was
10980         designed "before" or "after" the bug was fixed. As a workaround,
10981         a new graphics set option "game.graphics_engine_version" was added
10982         to be able to specify the game's major release version for which the
10983         graphics set was designed, which can then be used to decide if the
10984         bugfix should be used (version 4 and above) or not (version 3 or
10985         below, or if no version was specified at all, as with old sets).
10986
10987         (The wrong/fixed animation frames can be tested with the test level set
10988         "test_gfxframe" and level "000", which contains a specially prepared
10989         custom element at level position (x/y) == (11/9) which uses the zonk
10990         animation mentioned above. Using "game.graphics_engine_version: 4"
10991         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10992         This can also be seen from the debug output for this test element.)
10993       */
10994
10995       // when a custom element is about to change (for example by change delay),
10996       // do not reset graphic animation when the custom element is moving
10997       if (game.graphics_engine_version < 4 &&
10998           !IS_MOVING(x, y))
10999       {
11000         ResetGfxAnimation(x, y);
11001         ResetRandomAnimationValue(x, y);
11002       }
11003
11004       if (change->pre_change_function)
11005         change->pre_change_function(x, y);
11006     }
11007   }
11008
11009   ChangeDelay[x][y]--;
11010
11011   if (ChangeDelay[x][y] != 0)           // continue element change
11012   {
11013     if (change->can_change)
11014     {
11015       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11016
11017       if (IS_ANIMATED(graphic))
11018         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11019
11020       if (change->change_function)
11021         change->change_function(x, y);
11022     }
11023   }
11024   else                                  // finish element change
11025   {
11026     if (ChangePage[x][y] != -1)         // remember page from delayed change
11027     {
11028       page = ChangePage[x][y];
11029       ChangePage[x][y] = -1;
11030
11031       change = &ei->change_page[page];
11032     }
11033
11034     if (IS_MOVING(x, y))                // never change a running system ;-)
11035     {
11036       ChangeDelay[x][y] = 1;            // try change after next move step
11037       ChangePage[x][y] = page;          // remember page to use for change
11038
11039       return;
11040     }
11041
11042     // special case: set new level random seed before changing element
11043     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11044       handle_action_before_change = TRUE;
11045
11046     if (change->has_action && handle_action_before_change)
11047       ExecuteCustomElementAction(x, y, element, page);
11048
11049     if (change->can_change)
11050     {
11051       if (ChangeElement(x, y, element, page))
11052       {
11053         if (change->post_change_function)
11054           change->post_change_function(x, y);
11055       }
11056     }
11057
11058     if (change->has_action && !handle_action_before_change)
11059       ExecuteCustomElementAction(x, y, element, page);
11060   }
11061 }
11062
11063 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11064                                               int trigger_element,
11065                                               int trigger_event,
11066                                               int trigger_player,
11067                                               int trigger_side,
11068                                               int trigger_page)
11069 {
11070   boolean change_done_any = FALSE;
11071   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11072   int i;
11073
11074   if (!(trigger_events[trigger_element][trigger_event]))
11075     return FALSE;
11076
11077   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11078
11079   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11080   {
11081     int element = EL_CUSTOM_START + i;
11082     boolean change_done = FALSE;
11083     int p;
11084
11085     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11086         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11087       continue;
11088
11089     for (p = 0; p < element_info[element].num_change_pages; p++)
11090     {
11091       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11092
11093       if (change->can_change_or_has_action &&
11094           change->has_event[trigger_event] &&
11095           change->trigger_side & trigger_side &&
11096           change->trigger_player & trigger_player &&
11097           change->trigger_page & trigger_page_bits &&
11098           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11099       {
11100         change->actual_trigger_element = trigger_element;
11101         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11102         change->actual_trigger_player_bits = trigger_player;
11103         change->actual_trigger_side = trigger_side;
11104         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11105         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11106
11107         if ((change->can_change && !change_done) || change->has_action)
11108         {
11109           int x, y;
11110
11111           SCAN_PLAYFIELD(x, y)
11112           {
11113             if (Tile[x][y] == element)
11114             {
11115               if (change->can_change && !change_done)
11116               {
11117                 // if element already changed in this frame, not only prevent
11118                 // another element change (checked in ChangeElement()), but
11119                 // also prevent additional element actions for this element
11120
11121                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11122                     !level.use_action_after_change_bug)
11123                   continue;
11124
11125                 ChangeDelay[x][y] = 1;
11126                 ChangeEvent[x][y] = trigger_event;
11127
11128                 HandleElementChange(x, y, p);
11129               }
11130               else if (change->has_action)
11131               {
11132                 // if element already changed in this frame, not only prevent
11133                 // another element change (checked in ChangeElement()), but
11134                 // also prevent additional element actions for this element
11135
11136                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11137                     !level.use_action_after_change_bug)
11138                   continue;
11139
11140                 ExecuteCustomElementAction(x, y, element, p);
11141                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11142               }
11143             }
11144           }
11145
11146           if (change->can_change)
11147           {
11148             change_done = TRUE;
11149             change_done_any = TRUE;
11150           }
11151         }
11152       }
11153     }
11154   }
11155
11156   RECURSION_LOOP_DETECTION_END();
11157
11158   return change_done_any;
11159 }
11160
11161 static boolean CheckElementChangeExt(int x, int y,
11162                                      int element,
11163                                      int trigger_element,
11164                                      int trigger_event,
11165                                      int trigger_player,
11166                                      int trigger_side)
11167 {
11168   boolean change_done = FALSE;
11169   int p;
11170
11171   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11172       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11173     return FALSE;
11174
11175   if (Tile[x][y] == EL_BLOCKED)
11176   {
11177     Blocked2Moving(x, y, &x, &y);
11178     element = Tile[x][y];
11179   }
11180
11181   // check if element has already changed or is about to change after moving
11182   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11183        Tile[x][y] != element) ||
11184
11185       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11186        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11187         ChangePage[x][y] != -1)))
11188     return FALSE;
11189
11190   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11191
11192   for (p = 0; p < element_info[element].num_change_pages; p++)
11193   {
11194     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11195
11196     /* check trigger element for all events where the element that is checked
11197        for changing interacts with a directly adjacent element -- this is
11198        different to element changes that affect other elements to change on the
11199        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11200     boolean check_trigger_element =
11201       (trigger_event == CE_NEXT_TO_X ||
11202        trigger_event == CE_TOUCHING_X ||
11203        trigger_event == CE_HITTING_X ||
11204        trigger_event == CE_HIT_BY_X ||
11205        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11206
11207     if (change->can_change_or_has_action &&
11208         change->has_event[trigger_event] &&
11209         change->trigger_side & trigger_side &&
11210         change->trigger_player & trigger_player &&
11211         (!check_trigger_element ||
11212          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11213     {
11214       change->actual_trigger_element = trigger_element;
11215       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11216       change->actual_trigger_player_bits = trigger_player;
11217       change->actual_trigger_side = trigger_side;
11218       change->actual_trigger_ce_value = CustomValue[x][y];
11219       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11220
11221       // special case: trigger element not at (x,y) position for some events
11222       if (check_trigger_element)
11223       {
11224         static struct
11225         {
11226           int dx, dy;
11227         } move_xy[] =
11228           {
11229             {  0,  0 },
11230             { -1,  0 },
11231             { +1,  0 },
11232             {  0,  0 },
11233             {  0, -1 },
11234             {  0,  0 }, { 0, 0 }, { 0, 0 },
11235             {  0, +1 }
11236           };
11237
11238         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11239         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11240
11241         change->actual_trigger_ce_value = CustomValue[xx][yy];
11242         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11243       }
11244
11245       if (change->can_change && !change_done)
11246       {
11247         ChangeDelay[x][y] = 1;
11248         ChangeEvent[x][y] = trigger_event;
11249
11250         HandleElementChange(x, y, p);
11251
11252         change_done = TRUE;
11253       }
11254       else if (change->has_action)
11255       {
11256         ExecuteCustomElementAction(x, y, element, p);
11257         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11258       }
11259     }
11260   }
11261
11262   RECURSION_LOOP_DETECTION_END();
11263
11264   return change_done;
11265 }
11266
11267 static void PlayPlayerSound(struct PlayerInfo *player)
11268 {
11269   int jx = player->jx, jy = player->jy;
11270   int sound_element = player->artwork_element;
11271   int last_action = player->last_action_waiting;
11272   int action = player->action_waiting;
11273
11274   if (player->is_waiting)
11275   {
11276     if (action != last_action)
11277       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11278     else
11279       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11280   }
11281   else
11282   {
11283     if (action != last_action)
11284       StopSound(element_info[sound_element].sound[last_action]);
11285
11286     if (last_action == ACTION_SLEEPING)
11287       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11288   }
11289 }
11290
11291 static void PlayAllPlayersSound(void)
11292 {
11293   int i;
11294
11295   for (i = 0; i < MAX_PLAYERS; i++)
11296     if (stored_player[i].active)
11297       PlayPlayerSound(&stored_player[i]);
11298 }
11299
11300 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11301 {
11302   boolean last_waiting = player->is_waiting;
11303   int move_dir = player->MovDir;
11304
11305   player->dir_waiting = move_dir;
11306   player->last_action_waiting = player->action_waiting;
11307
11308   if (is_waiting)
11309   {
11310     if (!last_waiting)          // not waiting -> waiting
11311     {
11312       player->is_waiting = TRUE;
11313
11314       player->frame_counter_bored =
11315         FrameCounter +
11316         game.player_boring_delay_fixed +
11317         GetSimpleRandom(game.player_boring_delay_random);
11318       player->frame_counter_sleeping =
11319         FrameCounter +
11320         game.player_sleeping_delay_fixed +
11321         GetSimpleRandom(game.player_sleeping_delay_random);
11322
11323       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11324     }
11325
11326     if (game.player_sleeping_delay_fixed +
11327         game.player_sleeping_delay_random > 0 &&
11328         player->anim_delay_counter == 0 &&
11329         player->post_delay_counter == 0 &&
11330         FrameCounter >= player->frame_counter_sleeping)
11331       player->is_sleeping = TRUE;
11332     else if (game.player_boring_delay_fixed +
11333              game.player_boring_delay_random > 0 &&
11334              FrameCounter >= player->frame_counter_bored)
11335       player->is_bored = TRUE;
11336
11337     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11338                               player->is_bored ? ACTION_BORING :
11339                               ACTION_WAITING);
11340
11341     if (player->is_sleeping && player->use_murphy)
11342     {
11343       // special case for sleeping Murphy when leaning against non-free tile
11344
11345       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11346           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11347            !IS_MOVING(player->jx - 1, player->jy)))
11348         move_dir = MV_LEFT;
11349       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11350                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11351                 !IS_MOVING(player->jx + 1, player->jy)))
11352         move_dir = MV_RIGHT;
11353       else
11354         player->is_sleeping = FALSE;
11355
11356       player->dir_waiting = move_dir;
11357     }
11358
11359     if (player->is_sleeping)
11360     {
11361       if (player->num_special_action_sleeping > 0)
11362       {
11363         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11364         {
11365           int last_special_action = player->special_action_sleeping;
11366           int num_special_action = player->num_special_action_sleeping;
11367           int special_action =
11368             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11369              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11370              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11371              last_special_action + 1 : ACTION_SLEEPING);
11372           int special_graphic =
11373             el_act_dir2img(player->artwork_element, special_action, move_dir);
11374
11375           player->anim_delay_counter =
11376             graphic_info[special_graphic].anim_delay_fixed +
11377             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11378           player->post_delay_counter =
11379             graphic_info[special_graphic].post_delay_fixed +
11380             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11381
11382           player->special_action_sleeping = special_action;
11383         }
11384
11385         if (player->anim_delay_counter > 0)
11386         {
11387           player->action_waiting = player->special_action_sleeping;
11388           player->anim_delay_counter--;
11389         }
11390         else if (player->post_delay_counter > 0)
11391         {
11392           player->post_delay_counter--;
11393         }
11394       }
11395     }
11396     else if (player->is_bored)
11397     {
11398       if (player->num_special_action_bored > 0)
11399       {
11400         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11401         {
11402           int special_action =
11403             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11404           int special_graphic =
11405             el_act_dir2img(player->artwork_element, special_action, move_dir);
11406
11407           player->anim_delay_counter =
11408             graphic_info[special_graphic].anim_delay_fixed +
11409             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11410           player->post_delay_counter =
11411             graphic_info[special_graphic].post_delay_fixed +
11412             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11413
11414           player->special_action_bored = special_action;
11415         }
11416
11417         if (player->anim_delay_counter > 0)
11418         {
11419           player->action_waiting = player->special_action_bored;
11420           player->anim_delay_counter--;
11421         }
11422         else if (player->post_delay_counter > 0)
11423         {
11424           player->post_delay_counter--;
11425         }
11426       }
11427     }
11428   }
11429   else if (last_waiting)        // waiting -> not waiting
11430   {
11431     player->is_waiting = FALSE;
11432     player->is_bored = FALSE;
11433     player->is_sleeping = FALSE;
11434
11435     player->frame_counter_bored = -1;
11436     player->frame_counter_sleeping = -1;
11437
11438     player->anim_delay_counter = 0;
11439     player->post_delay_counter = 0;
11440
11441     player->dir_waiting = player->MovDir;
11442     player->action_waiting = ACTION_DEFAULT;
11443
11444     player->special_action_bored = ACTION_DEFAULT;
11445     player->special_action_sleeping = ACTION_DEFAULT;
11446   }
11447 }
11448
11449 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11450 {
11451   if ((!player->is_moving  && player->was_moving) ||
11452       (player->MovPos == 0 && player->was_moving) ||
11453       (player->is_snapping && !player->was_snapping) ||
11454       (player->is_dropping && !player->was_dropping))
11455   {
11456     if (!CheckSaveEngineSnapshotToList())
11457       return;
11458
11459     player->was_moving = FALSE;
11460     player->was_snapping = TRUE;
11461     player->was_dropping = TRUE;
11462   }
11463   else
11464   {
11465     if (player->is_moving)
11466       player->was_moving = TRUE;
11467
11468     if (!player->is_snapping)
11469       player->was_snapping = FALSE;
11470
11471     if (!player->is_dropping)
11472       player->was_dropping = FALSE;
11473   }
11474
11475   static struct MouseActionInfo mouse_action_last = { 0 };
11476   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11477   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11478
11479   if (new_released)
11480     CheckSaveEngineSnapshotToList();
11481
11482   mouse_action_last = mouse_action;
11483 }
11484
11485 static void CheckSingleStepMode(struct PlayerInfo *player)
11486 {
11487   if (tape.single_step && tape.recording && !tape.pausing)
11488   {
11489     // as it is called "single step mode", just return to pause mode when the
11490     // player stopped moving after one tile (or never starts moving at all)
11491     // (reverse logic needed here in case single step mode used in team mode)
11492     if (player->is_moving ||
11493         player->is_pushing ||
11494         player->is_dropping_pressed ||
11495         player->effective_mouse_action.button)
11496       game.enter_single_step_mode = FALSE;
11497   }
11498
11499   CheckSaveEngineSnapshot(player);
11500 }
11501
11502 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11503 {
11504   int left      = player_action & JOY_LEFT;
11505   int right     = player_action & JOY_RIGHT;
11506   int up        = player_action & JOY_UP;
11507   int down      = player_action & JOY_DOWN;
11508   int button1   = player_action & JOY_BUTTON_1;
11509   int button2   = player_action & JOY_BUTTON_2;
11510   int dx        = (left ? -1 : right ? 1 : 0);
11511   int dy        = (up   ? -1 : down  ? 1 : 0);
11512
11513   if (!player->active || tape.pausing)
11514     return 0;
11515
11516   if (player_action)
11517   {
11518     if (button1)
11519       SnapField(player, dx, dy);
11520     else
11521     {
11522       if (button2)
11523         DropElement(player);
11524
11525       MovePlayer(player, dx, dy);
11526     }
11527
11528     CheckSingleStepMode(player);
11529
11530     SetPlayerWaiting(player, FALSE);
11531
11532     return player_action;
11533   }
11534   else
11535   {
11536     // no actions for this player (no input at player's configured device)
11537
11538     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11539     SnapField(player, 0, 0);
11540     CheckGravityMovementWhenNotMoving(player);
11541
11542     if (player->MovPos == 0)
11543       SetPlayerWaiting(player, TRUE);
11544
11545     if (player->MovPos == 0)    // needed for tape.playing
11546       player->is_moving = FALSE;
11547
11548     player->is_dropping = FALSE;
11549     player->is_dropping_pressed = FALSE;
11550     player->drop_pressed_delay = 0;
11551
11552     CheckSingleStepMode(player);
11553
11554     return 0;
11555   }
11556 }
11557
11558 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11559                                          byte *tape_action)
11560 {
11561   if (!tape.use_mouse_actions)
11562     return;
11563
11564   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11565   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11566   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11567 }
11568
11569 static void SetTapeActionFromMouseAction(byte *tape_action,
11570                                          struct MouseActionInfo *mouse_action)
11571 {
11572   if (!tape.use_mouse_actions)
11573     return;
11574
11575   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11576   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11577   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11578 }
11579
11580 static void CheckLevelSolved(void)
11581 {
11582   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11583   {
11584     if (game_em.level_solved &&
11585         !game_em.game_over)                             // game won
11586     {
11587       LevelSolved();
11588
11589       game_em.game_over = TRUE;
11590
11591       game.all_players_gone = TRUE;
11592     }
11593
11594     if (game_em.game_over)                              // game lost
11595       game.all_players_gone = TRUE;
11596   }
11597   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11598   {
11599     if (game_sp.level_solved &&
11600         !game_sp.game_over)                             // game won
11601     {
11602       LevelSolved();
11603
11604       game_sp.game_over = TRUE;
11605
11606       game.all_players_gone = TRUE;
11607     }
11608
11609     if (game_sp.game_over)                              // game lost
11610       game.all_players_gone = TRUE;
11611   }
11612   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11613   {
11614     if (game_mm.level_solved &&
11615         !game_mm.game_over)                             // game won
11616     {
11617       LevelSolved();
11618
11619       game_mm.game_over = TRUE;
11620
11621       game.all_players_gone = TRUE;
11622     }
11623
11624     if (game_mm.game_over)                              // game lost
11625       game.all_players_gone = TRUE;
11626   }
11627 }
11628
11629 static void CheckLevelTime_StepCounter(void)
11630 {
11631   int i;
11632
11633   TimePlayed++;
11634
11635   if (TimeLeft > 0)
11636   {
11637     TimeLeft--;
11638
11639     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11640       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11641
11642     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11643
11644     DisplayGameControlValues();
11645
11646     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11647       for (i = 0; i < MAX_PLAYERS; i++)
11648         KillPlayer(&stored_player[i]);
11649   }
11650   else if (game.no_time_limit && !game.all_players_gone)
11651   {
11652     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11653
11654     DisplayGameControlValues();
11655   }
11656 }
11657
11658 static void CheckLevelTime(void)
11659 {
11660   int i;
11661
11662   if (TimeFrames >= FRAMES_PER_SECOND)
11663   {
11664     TimeFrames = 0;
11665     TapeTime++;
11666
11667     for (i = 0; i < MAX_PLAYERS; i++)
11668     {
11669       struct PlayerInfo *player = &stored_player[i];
11670
11671       if (SHIELD_ON(player))
11672       {
11673         player->shield_normal_time_left--;
11674
11675         if (player->shield_deadly_time_left > 0)
11676           player->shield_deadly_time_left--;
11677       }
11678     }
11679
11680     if (!game.LevelSolved && !level.use_step_counter)
11681     {
11682       TimePlayed++;
11683
11684       if (TimeLeft > 0)
11685       {
11686         TimeLeft--;
11687
11688         if (TimeLeft <= 10 && setup.time_limit)
11689           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11690
11691         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11692            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11693
11694         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11695
11696         if (!TimeLeft && setup.time_limit)
11697         {
11698           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11699             game_em.lev->killed_out_of_time = TRUE;
11700           else
11701             for (i = 0; i < MAX_PLAYERS; i++)
11702               KillPlayer(&stored_player[i]);
11703         }
11704       }
11705       else if (game.no_time_limit && !game.all_players_gone)
11706       {
11707         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11708       }
11709
11710       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11711     }
11712
11713     if (tape.recording || tape.playing)
11714       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11715   }
11716
11717   if (tape.recording || tape.playing)
11718     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11719
11720   UpdateAndDisplayGameControlValues();
11721 }
11722
11723 void AdvanceFrameAndPlayerCounters(int player_nr)
11724 {
11725   int i;
11726
11727   // advance frame counters (global frame counter and time frame counter)
11728   FrameCounter++;
11729   TimeFrames++;
11730
11731   // advance player counters (counters for move delay, move animation etc.)
11732   for (i = 0; i < MAX_PLAYERS; i++)
11733   {
11734     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11735     int move_delay_value = stored_player[i].move_delay_value;
11736     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11737
11738     if (!advance_player_counters)       // not all players may be affected
11739       continue;
11740
11741     if (move_frames == 0)       // less than one move per game frame
11742     {
11743       int stepsize = TILEX / move_delay_value;
11744       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11745       int count = (stored_player[i].is_moving ?
11746                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11747
11748       if (count % delay == 0)
11749         move_frames = 1;
11750     }
11751
11752     stored_player[i].Frame += move_frames;
11753
11754     if (stored_player[i].MovPos != 0)
11755       stored_player[i].StepFrame += move_frames;
11756
11757     if (stored_player[i].move_delay > 0)
11758       stored_player[i].move_delay--;
11759
11760     // due to bugs in previous versions, counter must count up, not down
11761     if (stored_player[i].push_delay != -1)
11762       stored_player[i].push_delay++;
11763
11764     if (stored_player[i].drop_delay > 0)
11765       stored_player[i].drop_delay--;
11766
11767     if (stored_player[i].is_dropping_pressed)
11768       stored_player[i].drop_pressed_delay++;
11769   }
11770 }
11771
11772 void StartGameActions(boolean init_network_game, boolean record_tape,
11773                       int random_seed)
11774 {
11775   unsigned int new_random_seed = InitRND(random_seed);
11776
11777   if (record_tape)
11778     TapeStartRecording(new_random_seed);
11779
11780   if (setup.auto_pause_on_start && !tape.pausing)
11781     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11782
11783   if (init_network_game)
11784   {
11785     SendToServer_LevelFile();
11786     SendToServer_StartPlaying();
11787
11788     return;
11789   }
11790
11791   InitGame();
11792 }
11793
11794 static void GameActionsExt(void)
11795 {
11796 #if 0
11797   static unsigned int game_frame_delay = 0;
11798 #endif
11799   unsigned int game_frame_delay_value;
11800   byte *recorded_player_action;
11801   byte summarized_player_action = 0;
11802   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11803   int i;
11804
11805   // detect endless loops, caused by custom element programming
11806   if (recursion_loop_detected && recursion_loop_depth == 0)
11807   {
11808     char *message = getStringCat3("Internal Error! Element ",
11809                                   EL_NAME(recursion_loop_element),
11810                                   " caused endless loop! Quit the game?");
11811
11812     Warn("element '%s' caused endless loop in game engine",
11813          EL_NAME(recursion_loop_element));
11814
11815     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11816
11817     recursion_loop_detected = FALSE;    // if game should be continued
11818
11819     free(message);
11820
11821     return;
11822   }
11823
11824   if (game.restart_level)
11825     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11826
11827   CheckLevelSolved();
11828
11829   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11830     GameWon();
11831
11832   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11833     TapeStop();
11834
11835   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11836     return;
11837
11838   game_frame_delay_value =
11839     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11840
11841   if (tape.playing && tape.warp_forward && !tape.pausing)
11842     game_frame_delay_value = 0;
11843
11844   SetVideoFrameDelay(game_frame_delay_value);
11845
11846   // (de)activate virtual buttons depending on current game status
11847   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11848   {
11849     if (game.all_players_gone)  // if no players there to be controlled anymore
11850       SetOverlayActive(FALSE);
11851     else if (!tape.playing)     // if game continues after tape stopped playing
11852       SetOverlayActive(TRUE);
11853   }
11854
11855 #if 0
11856 #if 0
11857   // ---------- main game synchronization point ----------
11858
11859   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11860
11861   Debug("game:playing:skip", "skip == %d", skip);
11862
11863 #else
11864   // ---------- main game synchronization point ----------
11865
11866   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11867 #endif
11868 #endif
11869
11870   if (network_playing && !network_player_action_received)
11871   {
11872     // try to get network player actions in time
11873
11874     // last chance to get network player actions without main loop delay
11875     HandleNetworking();
11876
11877     // game was quit by network peer
11878     if (game_status != GAME_MODE_PLAYING)
11879       return;
11880
11881     // check if network player actions still missing and game still running
11882     if (!network_player_action_received && !checkGameEnded())
11883       return;           // failed to get network player actions in time
11884
11885     // do not yet reset "network_player_action_received" (for tape.pausing)
11886   }
11887
11888   if (tape.pausing)
11889     return;
11890
11891   // at this point we know that we really continue executing the game
11892
11893   network_player_action_received = FALSE;
11894
11895   // when playing tape, read previously recorded player input from tape data
11896   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11897
11898   local_player->effective_mouse_action = local_player->mouse_action;
11899
11900   if (recorded_player_action != NULL)
11901     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11902                                  recorded_player_action);
11903
11904   // TapePlayAction() may return NULL when toggling to "pause before death"
11905   if (tape.pausing)
11906     return;
11907
11908   if (tape.set_centered_player)
11909   {
11910     game.centered_player_nr_next = tape.centered_player_nr_next;
11911     game.set_centered_player = TRUE;
11912   }
11913
11914   for (i = 0; i < MAX_PLAYERS; i++)
11915   {
11916     summarized_player_action |= stored_player[i].action;
11917
11918     if (!network_playing && (game.team_mode || tape.playing))
11919       stored_player[i].effective_action = stored_player[i].action;
11920   }
11921
11922   if (network_playing && !checkGameEnded())
11923     SendToServer_MovePlayer(summarized_player_action);
11924
11925   // summarize all actions at local players mapped input device position
11926   // (this allows using different input devices in single player mode)
11927   if (!network.enabled && !game.team_mode)
11928     stored_player[map_player_action[local_player->index_nr]].effective_action =
11929       summarized_player_action;
11930
11931   // summarize all actions at centered player in local team mode
11932   if (tape.recording &&
11933       setup.team_mode && !network.enabled &&
11934       setup.input_on_focus &&
11935       game.centered_player_nr != -1)
11936   {
11937     for (i = 0; i < MAX_PLAYERS; i++)
11938       stored_player[map_player_action[i]].effective_action =
11939         (i == game.centered_player_nr ? summarized_player_action : 0);
11940   }
11941
11942   if (recorded_player_action != NULL)
11943     for (i = 0; i < MAX_PLAYERS; i++)
11944       stored_player[i].effective_action = recorded_player_action[i];
11945
11946   for (i = 0; i < MAX_PLAYERS; i++)
11947   {
11948     tape_action[i] = stored_player[i].effective_action;
11949
11950     /* (this may happen in the RND game engine if a player was not present on
11951        the playfield on level start, but appeared later from a custom element */
11952     if (setup.team_mode &&
11953         tape.recording &&
11954         tape_action[i] &&
11955         !tape.player_participates[i])
11956       tape.player_participates[i] = TRUE;
11957   }
11958
11959   SetTapeActionFromMouseAction(tape_action,
11960                                &local_player->effective_mouse_action);
11961
11962   // only record actions from input devices, but not programmed actions
11963   if (tape.recording)
11964     TapeRecordAction(tape_action);
11965
11966   // remember if game was played (especially after tape stopped playing)
11967   if (!tape.playing && summarized_player_action)
11968     game.GamePlayed = TRUE;
11969
11970 #if USE_NEW_PLAYER_ASSIGNMENTS
11971   // !!! also map player actions in single player mode !!!
11972   // if (game.team_mode)
11973   if (1)
11974   {
11975     byte mapped_action[MAX_PLAYERS];
11976
11977 #if DEBUG_PLAYER_ACTIONS
11978     for (i = 0; i < MAX_PLAYERS; i++)
11979       DebugContinued("", "%d, ", stored_player[i].effective_action);
11980 #endif
11981
11982     for (i = 0; i < MAX_PLAYERS; i++)
11983       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11984
11985     for (i = 0; i < MAX_PLAYERS; i++)
11986       stored_player[i].effective_action = mapped_action[i];
11987
11988 #if DEBUG_PLAYER_ACTIONS
11989     DebugContinued("", "=> ");
11990     for (i = 0; i < MAX_PLAYERS; i++)
11991       DebugContinued("", "%d, ", stored_player[i].effective_action);
11992     DebugContinued("game:playing:player", "\n");
11993 #endif
11994   }
11995 #if DEBUG_PLAYER_ACTIONS
11996   else
11997   {
11998     for (i = 0; i < MAX_PLAYERS; i++)
11999       DebugContinued("", "%d, ", stored_player[i].effective_action);
12000     DebugContinued("game:playing:player", "\n");
12001   }
12002 #endif
12003 #endif
12004
12005   for (i = 0; i < MAX_PLAYERS; i++)
12006   {
12007     // allow engine snapshot in case of changed movement attempt
12008     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12009         (stored_player[i].effective_action & KEY_MOTION))
12010       game.snapshot.changed_action = TRUE;
12011
12012     // allow engine snapshot in case of snapping/dropping attempt
12013     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12014         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12015       game.snapshot.changed_action = TRUE;
12016
12017     game.snapshot.last_action[i] = stored_player[i].effective_action;
12018   }
12019
12020   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12021   {
12022     GameActions_EM_Main();
12023   }
12024   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12025   {
12026     GameActions_SP_Main();
12027   }
12028   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12029   {
12030     GameActions_MM_Main();
12031   }
12032   else
12033   {
12034     GameActions_RND_Main();
12035   }
12036
12037   BlitScreenToBitmap(backbuffer);
12038
12039   CheckLevelSolved();
12040   CheckLevelTime();
12041
12042   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12043
12044   if (global.show_frames_per_second)
12045   {
12046     static unsigned int fps_counter = 0;
12047     static int fps_frames = 0;
12048     unsigned int fps_delay_ms = Counter() - fps_counter;
12049
12050     fps_frames++;
12051
12052     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12053     {
12054       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12055
12056       fps_frames = 0;
12057       fps_counter = Counter();
12058
12059       // always draw FPS to screen after FPS value was updated
12060       redraw_mask |= REDRAW_FPS;
12061     }
12062
12063     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12064     if (GetDrawDeactivationMask() == REDRAW_NONE)
12065       redraw_mask |= REDRAW_FPS;
12066   }
12067 }
12068
12069 static void GameActions_CheckSaveEngineSnapshot(void)
12070 {
12071   if (!game.snapshot.save_snapshot)
12072     return;
12073
12074   // clear flag for saving snapshot _before_ saving snapshot
12075   game.snapshot.save_snapshot = FALSE;
12076
12077   SaveEngineSnapshotToList();
12078 }
12079
12080 void GameActions(void)
12081 {
12082   GameActionsExt();
12083
12084   GameActions_CheckSaveEngineSnapshot();
12085 }
12086
12087 void GameActions_EM_Main(void)
12088 {
12089   byte effective_action[MAX_PLAYERS];
12090   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12091   int i;
12092
12093   for (i = 0; i < MAX_PLAYERS; i++)
12094     effective_action[i] = stored_player[i].effective_action;
12095
12096   GameActions_EM(effective_action, warp_mode);
12097 }
12098
12099 void GameActions_SP_Main(void)
12100 {
12101   byte effective_action[MAX_PLAYERS];
12102   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12103   int i;
12104
12105   for (i = 0; i < MAX_PLAYERS; i++)
12106     effective_action[i] = stored_player[i].effective_action;
12107
12108   GameActions_SP(effective_action, warp_mode);
12109
12110   for (i = 0; i < MAX_PLAYERS; i++)
12111   {
12112     if (stored_player[i].force_dropping)
12113       stored_player[i].action |= KEY_BUTTON_DROP;
12114
12115     stored_player[i].force_dropping = FALSE;
12116   }
12117 }
12118
12119 void GameActions_MM_Main(void)
12120 {
12121   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12122
12123   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12124 }
12125
12126 void GameActions_RND_Main(void)
12127 {
12128   GameActions_RND();
12129 }
12130
12131 void GameActions_RND(void)
12132 {
12133   static struct MouseActionInfo mouse_action_last = { 0 };
12134   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12135   int magic_wall_x = 0, magic_wall_y = 0;
12136   int i, x, y, element, graphic, last_gfx_frame;
12137
12138   InitPlayfieldScanModeVars();
12139
12140   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12141   {
12142     SCAN_PLAYFIELD(x, y)
12143     {
12144       ChangeCount[x][y] = 0;
12145       ChangeEvent[x][y] = -1;
12146     }
12147   }
12148
12149   if (game.set_centered_player)
12150   {
12151     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12152
12153     // switching to "all players" only possible if all players fit to screen
12154     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12155     {
12156       game.centered_player_nr_next = game.centered_player_nr;
12157       game.set_centered_player = FALSE;
12158     }
12159
12160     // do not switch focus to non-existing (or non-active) player
12161     if (game.centered_player_nr_next >= 0 &&
12162         !stored_player[game.centered_player_nr_next].active)
12163     {
12164       game.centered_player_nr_next = game.centered_player_nr;
12165       game.set_centered_player = FALSE;
12166     }
12167   }
12168
12169   if (game.set_centered_player &&
12170       ScreenMovPos == 0)        // screen currently aligned at tile position
12171   {
12172     int sx, sy;
12173
12174     if (game.centered_player_nr_next == -1)
12175     {
12176       setScreenCenteredToAllPlayers(&sx, &sy);
12177     }
12178     else
12179     {
12180       sx = stored_player[game.centered_player_nr_next].jx;
12181       sy = stored_player[game.centered_player_nr_next].jy;
12182     }
12183
12184     game.centered_player_nr = game.centered_player_nr_next;
12185     game.set_centered_player = FALSE;
12186
12187     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12188     DrawGameDoorValues();
12189   }
12190
12191   // check single step mode (set flag and clear again if any player is active)
12192   game.enter_single_step_mode =
12193     (tape.single_step && tape.recording && !tape.pausing);
12194
12195   for (i = 0; i < MAX_PLAYERS; i++)
12196   {
12197     int actual_player_action = stored_player[i].effective_action;
12198
12199 #if 1
12200     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12201        - rnd_equinox_tetrachloride 048
12202        - rnd_equinox_tetrachloride_ii 096
12203        - rnd_emanuel_schmieg 002
12204        - doctor_sloan_ww 001, 020
12205     */
12206     if (stored_player[i].MovPos == 0)
12207       CheckGravityMovement(&stored_player[i]);
12208 #endif
12209
12210     // overwrite programmed action with tape action
12211     if (stored_player[i].programmed_action)
12212       actual_player_action = stored_player[i].programmed_action;
12213
12214     PlayerActions(&stored_player[i], actual_player_action);
12215
12216     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12217   }
12218
12219   // single step pause mode may already have been toggled by "ScrollPlayer()"
12220   if (game.enter_single_step_mode && !tape.pausing)
12221     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12222
12223   ScrollScreen(NULL, SCROLL_GO_ON);
12224
12225   /* for backwards compatibility, the following code emulates a fixed bug that
12226      occured when pushing elements (causing elements that just made their last
12227      pushing step to already (if possible) make their first falling step in the
12228      same game frame, which is bad); this code is also needed to use the famous
12229      "spring push bug" which is used in older levels and might be wanted to be
12230      used also in newer levels, but in this case the buggy pushing code is only
12231      affecting the "spring" element and no other elements */
12232
12233   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12234   {
12235     for (i = 0; i < MAX_PLAYERS; i++)
12236     {
12237       struct PlayerInfo *player = &stored_player[i];
12238       int x = player->jx;
12239       int y = player->jy;
12240
12241       if (player->active && player->is_pushing && player->is_moving &&
12242           IS_MOVING(x, y) &&
12243           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12244            Tile[x][y] == EL_SPRING))
12245       {
12246         ContinueMoving(x, y);
12247
12248         // continue moving after pushing (this is actually a bug)
12249         if (!IS_MOVING(x, y))
12250           Stop[x][y] = FALSE;
12251       }
12252     }
12253   }
12254
12255   SCAN_PLAYFIELD(x, y)
12256   {
12257     Last[x][y] = Tile[x][y];
12258
12259     ChangeCount[x][y] = 0;
12260     ChangeEvent[x][y] = -1;
12261
12262     // this must be handled before main playfield loop
12263     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12264     {
12265       MovDelay[x][y]--;
12266       if (MovDelay[x][y] <= 0)
12267         RemoveField(x, y);
12268     }
12269
12270     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12271     {
12272       MovDelay[x][y]--;
12273       if (MovDelay[x][y] <= 0)
12274       {
12275         int element = Store[x][y];
12276         int move_direction = MovDir[x][y];
12277         int player_index_bit = Store2[x][y];
12278
12279         Store[x][y] = 0;
12280         Store2[x][y] = 0;
12281
12282         RemoveField(x, y);
12283         TEST_DrawLevelField(x, y);
12284
12285         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12286
12287         if (IS_ENVELOPE(element))
12288           local_player->show_envelope = element;
12289       }
12290     }
12291
12292 #if DEBUG
12293     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12294     {
12295       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12296             x, y);
12297       Debug("game:playing:GameActions_RND", "This should never happen!");
12298
12299       ChangePage[x][y] = -1;
12300     }
12301 #endif
12302
12303     Stop[x][y] = FALSE;
12304     if (WasJustMoving[x][y] > 0)
12305       WasJustMoving[x][y]--;
12306     if (WasJustFalling[x][y] > 0)
12307       WasJustFalling[x][y]--;
12308     if (CheckCollision[x][y] > 0)
12309       CheckCollision[x][y]--;
12310     if (CheckImpact[x][y] > 0)
12311       CheckImpact[x][y]--;
12312
12313     GfxFrame[x][y]++;
12314
12315     /* reset finished pushing action (not done in ContinueMoving() to allow
12316        continuous pushing animation for elements with zero push delay) */
12317     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12318     {
12319       ResetGfxAnimation(x, y);
12320       TEST_DrawLevelField(x, y);
12321     }
12322
12323 #if DEBUG
12324     if (IS_BLOCKED(x, y))
12325     {
12326       int oldx, oldy;
12327
12328       Blocked2Moving(x, y, &oldx, &oldy);
12329       if (!IS_MOVING(oldx, oldy))
12330       {
12331         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12332         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12333         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12334         Debug("game:playing:GameActions_RND", "This should never happen!");
12335       }
12336     }
12337 #endif
12338   }
12339
12340   if (mouse_action.button)
12341   {
12342     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12343     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12344
12345     x = mouse_action.lx;
12346     y = mouse_action.ly;
12347     element = Tile[x][y];
12348
12349     if (new_button)
12350     {
12351       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12352       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12353                                          ch_button);
12354     }
12355
12356     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12357     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12358                                        ch_button);
12359
12360     if (level.use_step_counter)
12361     {
12362       boolean counted_click = FALSE;
12363
12364       // element clicked that can change when clicked/pressed
12365       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12366           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12367            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12368         counted_click = TRUE;
12369
12370       // element clicked that can trigger change when clicked/pressed
12371       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12372           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12373         counted_click = TRUE;
12374
12375       if (new_button && counted_click)
12376         CheckLevelTime_StepCounter();
12377     }
12378   }
12379
12380   SCAN_PLAYFIELD(x, y)
12381   {
12382     element = Tile[x][y];
12383     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12384     last_gfx_frame = GfxFrame[x][y];
12385
12386     if (element == EL_EMPTY)
12387       graphic = el2img(GfxElementEmpty[x][y]);
12388
12389     ResetGfxFrame(x, y);
12390
12391     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12392       DrawLevelGraphicAnimation(x, y, graphic);
12393
12394     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12395         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12396       ResetRandomAnimationValue(x, y);
12397
12398     SetRandomAnimationValue(x, y);
12399
12400     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12401
12402     if (IS_INACTIVE(element))
12403     {
12404       if (IS_ANIMATED(graphic))
12405         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12406
12407       continue;
12408     }
12409
12410     // this may take place after moving, so 'element' may have changed
12411     if (IS_CHANGING(x, y) &&
12412         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12413     {
12414       int page = element_info[element].event_page_nr[CE_DELAY];
12415
12416       HandleElementChange(x, y, page);
12417
12418       element = Tile[x][y];
12419       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12420     }
12421
12422     CheckNextToConditions(x, y);
12423
12424     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12425     {
12426       StartMoving(x, y);
12427
12428       element = Tile[x][y];
12429       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12430
12431       if (IS_ANIMATED(graphic) &&
12432           !IS_MOVING(x, y) &&
12433           !Stop[x][y])
12434         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12435
12436       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12437         TEST_DrawTwinkleOnField(x, y);
12438     }
12439     else if (element == EL_ACID)
12440     {
12441       if (!Stop[x][y])
12442         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12443     }
12444     else if ((element == EL_EXIT_OPEN ||
12445               element == EL_EM_EXIT_OPEN ||
12446               element == EL_SP_EXIT_OPEN ||
12447               element == EL_STEEL_EXIT_OPEN ||
12448               element == EL_EM_STEEL_EXIT_OPEN ||
12449               element == EL_SP_TERMINAL ||
12450               element == EL_SP_TERMINAL_ACTIVE ||
12451               element == EL_EXTRA_TIME ||
12452               element == EL_SHIELD_NORMAL ||
12453               element == EL_SHIELD_DEADLY) &&
12454              IS_ANIMATED(graphic))
12455       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12456     else if (IS_MOVING(x, y))
12457       ContinueMoving(x, y);
12458     else if (IS_ACTIVE_BOMB(element))
12459       CheckDynamite(x, y);
12460     else if (element == EL_AMOEBA_GROWING)
12461       AmoebaGrowing(x, y);
12462     else if (element == EL_AMOEBA_SHRINKING)
12463       AmoebaShrinking(x, y);
12464
12465 #if !USE_NEW_AMOEBA_CODE
12466     else if (IS_AMOEBALIVE(element))
12467       AmoebaReproduce(x, y);
12468 #endif
12469
12470     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12471       Life(x, y);
12472     else if (element == EL_EXIT_CLOSED)
12473       CheckExit(x, y);
12474     else if (element == EL_EM_EXIT_CLOSED)
12475       CheckExitEM(x, y);
12476     else if (element == EL_STEEL_EXIT_CLOSED)
12477       CheckExitSteel(x, y);
12478     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12479       CheckExitSteelEM(x, y);
12480     else if (element == EL_SP_EXIT_CLOSED)
12481       CheckExitSP(x, y);
12482     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12483              element == EL_EXPANDABLE_STEELWALL_GROWING)
12484       MauerWaechst(x, y);
12485     else if (element == EL_EXPANDABLE_WALL ||
12486              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12487              element == EL_EXPANDABLE_WALL_VERTICAL ||
12488              element == EL_EXPANDABLE_WALL_ANY ||
12489              element == EL_BD_EXPANDABLE_WALL)
12490       MauerAbleger(x, y);
12491     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12492              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12493              element == EL_EXPANDABLE_STEELWALL_ANY)
12494       MauerAblegerStahl(x, y);
12495     else if (element == EL_FLAMES)
12496       CheckForDragon(x, y);
12497     else if (element == EL_EXPLOSION)
12498       ; // drawing of correct explosion animation is handled separately
12499     else if (element == EL_ELEMENT_SNAPPING ||
12500              element == EL_DIAGONAL_SHRINKING ||
12501              element == EL_DIAGONAL_GROWING)
12502     {
12503       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12504
12505       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12506     }
12507     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12508       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12509
12510     if (IS_BELT_ACTIVE(element))
12511       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12512
12513     if (game.magic_wall_active)
12514     {
12515       int jx = local_player->jx, jy = local_player->jy;
12516
12517       // play the element sound at the position nearest to the player
12518       if ((element == EL_MAGIC_WALL_FULL ||
12519            element == EL_MAGIC_WALL_ACTIVE ||
12520            element == EL_MAGIC_WALL_EMPTYING ||
12521            element == EL_BD_MAGIC_WALL_FULL ||
12522            element == EL_BD_MAGIC_WALL_ACTIVE ||
12523            element == EL_BD_MAGIC_WALL_EMPTYING ||
12524            element == EL_DC_MAGIC_WALL_FULL ||
12525            element == EL_DC_MAGIC_WALL_ACTIVE ||
12526            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12527           ABS(x - jx) + ABS(y - jy) <
12528           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12529       {
12530         magic_wall_x = x;
12531         magic_wall_y = y;
12532       }
12533     }
12534   }
12535
12536 #if USE_NEW_AMOEBA_CODE
12537   // new experimental amoeba growth stuff
12538   if (!(FrameCounter % 8))
12539   {
12540     static unsigned int random = 1684108901;
12541
12542     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12543     {
12544       x = RND(lev_fieldx);
12545       y = RND(lev_fieldy);
12546       element = Tile[x][y];
12547
12548       if (!IS_PLAYER(x,y) &&
12549           (element == EL_EMPTY ||
12550            CAN_GROW_INTO(element) ||
12551            element == EL_QUICKSAND_EMPTY ||
12552            element == EL_QUICKSAND_FAST_EMPTY ||
12553            element == EL_ACID_SPLASH_LEFT ||
12554            element == EL_ACID_SPLASH_RIGHT))
12555       {
12556         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12557             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12558             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12559             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12560           Tile[x][y] = EL_AMOEBA_DROP;
12561       }
12562
12563       random = random * 129 + 1;
12564     }
12565   }
12566 #endif
12567
12568   game.explosions_delayed = FALSE;
12569
12570   SCAN_PLAYFIELD(x, y)
12571   {
12572     element = Tile[x][y];
12573
12574     if (ExplodeField[x][y])
12575       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12576     else if (element == EL_EXPLOSION)
12577       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12578
12579     ExplodeField[x][y] = EX_TYPE_NONE;
12580   }
12581
12582   game.explosions_delayed = TRUE;
12583
12584   if (game.magic_wall_active)
12585   {
12586     if (!(game.magic_wall_time_left % 4))
12587     {
12588       int element = Tile[magic_wall_x][magic_wall_y];
12589
12590       if (element == EL_BD_MAGIC_WALL_FULL ||
12591           element == EL_BD_MAGIC_WALL_ACTIVE ||
12592           element == EL_BD_MAGIC_WALL_EMPTYING)
12593         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12594       else if (element == EL_DC_MAGIC_WALL_FULL ||
12595                element == EL_DC_MAGIC_WALL_ACTIVE ||
12596                element == EL_DC_MAGIC_WALL_EMPTYING)
12597         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12598       else
12599         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12600     }
12601
12602     if (game.magic_wall_time_left > 0)
12603     {
12604       game.magic_wall_time_left--;
12605
12606       if (!game.magic_wall_time_left)
12607       {
12608         SCAN_PLAYFIELD(x, y)
12609         {
12610           element = Tile[x][y];
12611
12612           if (element == EL_MAGIC_WALL_ACTIVE ||
12613               element == EL_MAGIC_WALL_FULL)
12614           {
12615             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12616             TEST_DrawLevelField(x, y);
12617           }
12618           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12619                    element == EL_BD_MAGIC_WALL_FULL)
12620           {
12621             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12622             TEST_DrawLevelField(x, y);
12623           }
12624           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12625                    element == EL_DC_MAGIC_WALL_FULL)
12626           {
12627             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12628             TEST_DrawLevelField(x, y);
12629           }
12630         }
12631
12632         game.magic_wall_active = FALSE;
12633       }
12634     }
12635   }
12636
12637   if (game.light_time_left > 0)
12638   {
12639     game.light_time_left--;
12640
12641     if (game.light_time_left == 0)
12642       RedrawAllLightSwitchesAndInvisibleElements();
12643   }
12644
12645   if (game.timegate_time_left > 0)
12646   {
12647     game.timegate_time_left--;
12648
12649     if (game.timegate_time_left == 0)
12650       CloseAllOpenTimegates();
12651   }
12652
12653   if (game.lenses_time_left > 0)
12654   {
12655     game.lenses_time_left--;
12656
12657     if (game.lenses_time_left == 0)
12658       RedrawAllInvisibleElementsForLenses();
12659   }
12660
12661   if (game.magnify_time_left > 0)
12662   {
12663     game.magnify_time_left--;
12664
12665     if (game.magnify_time_left == 0)
12666       RedrawAllInvisibleElementsForMagnifier();
12667   }
12668
12669   for (i = 0; i < MAX_PLAYERS; i++)
12670   {
12671     struct PlayerInfo *player = &stored_player[i];
12672
12673     if (SHIELD_ON(player))
12674     {
12675       if (player->shield_deadly_time_left)
12676         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12677       else if (player->shield_normal_time_left)
12678         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12679     }
12680   }
12681
12682 #if USE_DELAYED_GFX_REDRAW
12683   SCAN_PLAYFIELD(x, y)
12684   {
12685     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12686     {
12687       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12688          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12689
12690       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12691         DrawLevelField(x, y);
12692
12693       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12694         DrawLevelFieldCrumbled(x, y);
12695
12696       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12697         DrawLevelFieldCrumbledNeighbours(x, y);
12698
12699       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12700         DrawTwinkleOnField(x, y);
12701     }
12702
12703     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12704   }
12705 #endif
12706
12707   DrawAllPlayers();
12708   PlayAllPlayersSound();
12709
12710   for (i = 0; i < MAX_PLAYERS; i++)
12711   {
12712     struct PlayerInfo *player = &stored_player[i];
12713
12714     if (player->show_envelope != 0 && (!player->active ||
12715                                        player->MovPos == 0))
12716     {
12717       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12718
12719       player->show_envelope = 0;
12720     }
12721   }
12722
12723   // use random number generator in every frame to make it less predictable
12724   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12725     RND(1);
12726
12727   mouse_action_last = mouse_action;
12728 }
12729
12730 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12731 {
12732   int min_x = x, min_y = y, max_x = x, max_y = y;
12733   int scr_fieldx = getScreenFieldSizeX();
12734   int scr_fieldy = getScreenFieldSizeY();
12735   int i;
12736
12737   for (i = 0; i < MAX_PLAYERS; i++)
12738   {
12739     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12740
12741     if (!stored_player[i].active || &stored_player[i] == player)
12742       continue;
12743
12744     min_x = MIN(min_x, jx);
12745     min_y = MIN(min_y, jy);
12746     max_x = MAX(max_x, jx);
12747     max_y = MAX(max_y, jy);
12748   }
12749
12750   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12751 }
12752
12753 static boolean AllPlayersInVisibleScreen(void)
12754 {
12755   int i;
12756
12757   for (i = 0; i < MAX_PLAYERS; i++)
12758   {
12759     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12760
12761     if (!stored_player[i].active)
12762       continue;
12763
12764     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12765       return FALSE;
12766   }
12767
12768   return TRUE;
12769 }
12770
12771 void ScrollLevel(int dx, int dy)
12772 {
12773   int scroll_offset = 2 * TILEX_VAR;
12774   int x, y;
12775
12776   BlitBitmap(drawto_field, drawto_field,
12777              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12778              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12779              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12780              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12781              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12782              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12783
12784   if (dx != 0)
12785   {
12786     x = (dx == 1 ? BX1 : BX2);
12787     for (y = BY1; y <= BY2; y++)
12788       DrawScreenField(x, y);
12789   }
12790
12791   if (dy != 0)
12792   {
12793     y = (dy == 1 ? BY1 : BY2);
12794     for (x = BX1; x <= BX2; x++)
12795       DrawScreenField(x, y);
12796   }
12797
12798   redraw_mask |= REDRAW_FIELD;
12799 }
12800
12801 static boolean canFallDown(struct PlayerInfo *player)
12802 {
12803   int jx = player->jx, jy = player->jy;
12804
12805   return (IN_LEV_FIELD(jx, jy + 1) &&
12806           (IS_FREE(jx, jy + 1) ||
12807            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12808           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12809           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12810 }
12811
12812 static boolean canPassField(int x, int y, int move_dir)
12813 {
12814   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12815   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12816   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12817   int nextx = x + dx;
12818   int nexty = y + dy;
12819   int element = Tile[x][y];
12820
12821   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12822           !CAN_MOVE(element) &&
12823           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12824           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12825           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12826 }
12827
12828 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12829 {
12830   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12831   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12832   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12833   int newx = x + dx;
12834   int newy = y + dy;
12835
12836   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12837           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12838           (IS_DIGGABLE(Tile[newx][newy]) ||
12839            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12840            canPassField(newx, newy, move_dir)));
12841 }
12842
12843 static void CheckGravityMovement(struct PlayerInfo *player)
12844 {
12845   if (player->gravity && !player->programmed_action)
12846   {
12847     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12848     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12849     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12850     int jx = player->jx, jy = player->jy;
12851     boolean player_is_moving_to_valid_field =
12852       (!player_is_snapping &&
12853        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12854         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12855     boolean player_can_fall_down = canFallDown(player);
12856
12857     if (player_can_fall_down &&
12858         !player_is_moving_to_valid_field)
12859       player->programmed_action = MV_DOWN;
12860   }
12861 }
12862
12863 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12864 {
12865   return CheckGravityMovement(player);
12866
12867   if (player->gravity && !player->programmed_action)
12868   {
12869     int jx = player->jx, jy = player->jy;
12870     boolean field_under_player_is_free =
12871       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12872     boolean player_is_standing_on_valid_field =
12873       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12874        (IS_WALKABLE(Tile[jx][jy]) &&
12875         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12876
12877     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12878       player->programmed_action = MV_DOWN;
12879   }
12880 }
12881
12882 /*
12883   MovePlayerOneStep()
12884   -----------------------------------------------------------------------------
12885   dx, dy:               direction (non-diagonal) to try to move the player to
12886   real_dx, real_dy:     direction as read from input device (can be diagonal)
12887 */
12888
12889 boolean MovePlayerOneStep(struct PlayerInfo *player,
12890                           int dx, int dy, int real_dx, int real_dy)
12891 {
12892   int jx = player->jx, jy = player->jy;
12893   int new_jx = jx + dx, new_jy = jy + dy;
12894   int can_move;
12895   boolean player_can_move = !player->cannot_move;
12896
12897   if (!player->active || (!dx && !dy))
12898     return MP_NO_ACTION;
12899
12900   player->MovDir = (dx < 0 ? MV_LEFT :
12901                     dx > 0 ? MV_RIGHT :
12902                     dy < 0 ? MV_UP :
12903                     dy > 0 ? MV_DOWN :  MV_NONE);
12904
12905   if (!IN_LEV_FIELD(new_jx, new_jy))
12906     return MP_NO_ACTION;
12907
12908   if (!player_can_move)
12909   {
12910     if (player->MovPos == 0)
12911     {
12912       player->is_moving = FALSE;
12913       player->is_digging = FALSE;
12914       player->is_collecting = FALSE;
12915       player->is_snapping = FALSE;
12916       player->is_pushing = FALSE;
12917     }
12918   }
12919
12920   if (!network.enabled && game.centered_player_nr == -1 &&
12921       !AllPlayersInSight(player, new_jx, new_jy))
12922     return MP_NO_ACTION;
12923
12924   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12925   if (can_move != MP_MOVING)
12926     return can_move;
12927
12928   // check if DigField() has caused relocation of the player
12929   if (player->jx != jx || player->jy != jy)
12930     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12931
12932   StorePlayer[jx][jy] = 0;
12933   player->last_jx = jx;
12934   player->last_jy = jy;
12935   player->jx = new_jx;
12936   player->jy = new_jy;
12937   StorePlayer[new_jx][new_jy] = player->element_nr;
12938
12939   if (player->move_delay_value_next != -1)
12940   {
12941     player->move_delay_value = player->move_delay_value_next;
12942     player->move_delay_value_next = -1;
12943   }
12944
12945   player->MovPos =
12946     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12947
12948   player->step_counter++;
12949
12950   PlayerVisit[jx][jy] = FrameCounter;
12951
12952   player->is_moving = TRUE;
12953
12954 #if 1
12955   // should better be called in MovePlayer(), but this breaks some tapes
12956   ScrollPlayer(player, SCROLL_INIT);
12957 #endif
12958
12959   return MP_MOVING;
12960 }
12961
12962 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12963 {
12964   int jx = player->jx, jy = player->jy;
12965   int old_jx = jx, old_jy = jy;
12966   int moved = MP_NO_ACTION;
12967
12968   if (!player->active)
12969     return FALSE;
12970
12971   if (!dx && !dy)
12972   {
12973     if (player->MovPos == 0)
12974     {
12975       player->is_moving = FALSE;
12976       player->is_digging = FALSE;
12977       player->is_collecting = FALSE;
12978       player->is_snapping = FALSE;
12979       player->is_pushing = FALSE;
12980     }
12981
12982     return FALSE;
12983   }
12984
12985   if (player->move_delay > 0)
12986     return FALSE;
12987
12988   player->move_delay = -1;              // set to "uninitialized" value
12989
12990   // store if player is automatically moved to next field
12991   player->is_auto_moving = (player->programmed_action != MV_NONE);
12992
12993   // remove the last programmed player action
12994   player->programmed_action = 0;
12995
12996   if (player->MovPos)
12997   {
12998     // should only happen if pre-1.2 tape recordings are played
12999     // this is only for backward compatibility
13000
13001     int original_move_delay_value = player->move_delay_value;
13002
13003 #if DEBUG
13004     Debug("game:playing:MovePlayer",
13005           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13006           tape.counter);
13007 #endif
13008
13009     // scroll remaining steps with finest movement resolution
13010     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13011
13012     while (player->MovPos)
13013     {
13014       ScrollPlayer(player, SCROLL_GO_ON);
13015       ScrollScreen(NULL, SCROLL_GO_ON);
13016
13017       AdvanceFrameAndPlayerCounters(player->index_nr);
13018
13019       DrawAllPlayers();
13020       BackToFront_WithFrameDelay(0);
13021     }
13022
13023     player->move_delay_value = original_move_delay_value;
13024   }
13025
13026   player->is_active = FALSE;
13027
13028   if (player->last_move_dir & MV_HORIZONTAL)
13029   {
13030     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13031       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13032   }
13033   else
13034   {
13035     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13036       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13037   }
13038
13039   if (!moved && !player->is_active)
13040   {
13041     player->is_moving = FALSE;
13042     player->is_digging = FALSE;
13043     player->is_collecting = FALSE;
13044     player->is_snapping = FALSE;
13045     player->is_pushing = FALSE;
13046   }
13047
13048   jx = player->jx;
13049   jy = player->jy;
13050
13051   if (moved & MP_MOVING && !ScreenMovPos &&
13052       (player->index_nr == game.centered_player_nr ||
13053        game.centered_player_nr == -1))
13054   {
13055     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13056
13057     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13058     {
13059       // actual player has left the screen -- scroll in that direction
13060       if (jx != old_jx)         // player has moved horizontally
13061         scroll_x += (jx - old_jx);
13062       else                      // player has moved vertically
13063         scroll_y += (jy - old_jy);
13064     }
13065     else
13066     {
13067       int offset_raw = game.scroll_delay_value;
13068
13069       if (jx != old_jx)         // player has moved horizontally
13070       {
13071         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13072         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13073         int new_scroll_x = jx - MIDPOSX + offset_x;
13074
13075         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13076             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13077           scroll_x = new_scroll_x;
13078
13079         // don't scroll over playfield boundaries
13080         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13081
13082         // don't scroll more than one field at a time
13083         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13084
13085         // don't scroll against the player's moving direction
13086         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13087             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13088           scroll_x = old_scroll_x;
13089       }
13090       else                      // player has moved vertically
13091       {
13092         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13093         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13094         int new_scroll_y = jy - MIDPOSY + offset_y;
13095
13096         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13097             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13098           scroll_y = new_scroll_y;
13099
13100         // don't scroll over playfield boundaries
13101         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13102
13103         // don't scroll more than one field at a time
13104         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13105
13106         // don't scroll against the player's moving direction
13107         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13108             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13109           scroll_y = old_scroll_y;
13110       }
13111     }
13112
13113     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13114     {
13115       if (!network.enabled && game.centered_player_nr == -1 &&
13116           !AllPlayersInVisibleScreen())
13117       {
13118         scroll_x = old_scroll_x;
13119         scroll_y = old_scroll_y;
13120       }
13121       else
13122       {
13123         ScrollScreen(player, SCROLL_INIT);
13124         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13125       }
13126     }
13127   }
13128
13129   player->StepFrame = 0;
13130
13131   if (moved & MP_MOVING)
13132   {
13133     if (old_jx != jx && old_jy == jy)
13134       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13135     else if (old_jx == jx && old_jy != jy)
13136       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13137
13138     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13139
13140     player->last_move_dir = player->MovDir;
13141     player->is_moving = TRUE;
13142     player->is_snapping = FALSE;
13143     player->is_switching = FALSE;
13144     player->is_dropping = FALSE;
13145     player->is_dropping_pressed = FALSE;
13146     player->drop_pressed_delay = 0;
13147
13148 #if 0
13149     // should better be called here than above, but this breaks some tapes
13150     ScrollPlayer(player, SCROLL_INIT);
13151 #endif
13152   }
13153   else
13154   {
13155     CheckGravityMovementWhenNotMoving(player);
13156
13157     player->is_moving = FALSE;
13158
13159     /* at this point, the player is allowed to move, but cannot move right now
13160        (e.g. because of something blocking the way) -- ensure that the player
13161        is also allowed to move in the next frame (in old versions before 3.1.1,
13162        the player was forced to wait again for eight frames before next try) */
13163
13164     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13165       player->move_delay = 0;   // allow direct movement in the next frame
13166   }
13167
13168   if (player->move_delay == -1)         // not yet initialized by DigField()
13169     player->move_delay = player->move_delay_value;
13170
13171   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13172   {
13173     TestIfPlayerTouchesBadThing(jx, jy);
13174     TestIfPlayerTouchesCustomElement(jx, jy);
13175   }
13176
13177   if (!player->active)
13178     RemovePlayer(player);
13179
13180   return moved;
13181 }
13182
13183 void ScrollPlayer(struct PlayerInfo *player, int mode)
13184 {
13185   int jx = player->jx, jy = player->jy;
13186   int last_jx = player->last_jx, last_jy = player->last_jy;
13187   int move_stepsize = TILEX / player->move_delay_value;
13188
13189   if (!player->active)
13190     return;
13191
13192   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13193     return;
13194
13195   if (mode == SCROLL_INIT)
13196   {
13197     player->actual_frame_counter = FrameCounter;
13198     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13199
13200     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13201         Tile[last_jx][last_jy] == EL_EMPTY)
13202     {
13203       int last_field_block_delay = 0;   // start with no blocking at all
13204       int block_delay_adjustment = player->block_delay_adjustment;
13205
13206       // if player blocks last field, add delay for exactly one move
13207       if (player->block_last_field)
13208       {
13209         last_field_block_delay += player->move_delay_value;
13210
13211         // when blocking enabled, prevent moving up despite gravity
13212         if (player->gravity && player->MovDir == MV_UP)
13213           block_delay_adjustment = -1;
13214       }
13215
13216       // add block delay adjustment (also possible when not blocking)
13217       last_field_block_delay += block_delay_adjustment;
13218
13219       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13220       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13221     }
13222
13223     if (player->MovPos != 0)    // player has not yet reached destination
13224       return;
13225   }
13226   else if (!FrameReached(&player->actual_frame_counter, 1))
13227     return;
13228
13229   if (player->MovPos != 0)
13230   {
13231     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13232     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13233
13234     // before DrawPlayer() to draw correct player graphic for this case
13235     if (player->MovPos == 0)
13236       CheckGravityMovement(player);
13237   }
13238
13239   if (player->MovPos == 0)      // player reached destination field
13240   {
13241     if (player->move_delay_reset_counter > 0)
13242     {
13243       player->move_delay_reset_counter--;
13244
13245       if (player->move_delay_reset_counter == 0)
13246       {
13247         // continue with normal speed after quickly moving through gate
13248         HALVE_PLAYER_SPEED(player);
13249
13250         // be able to make the next move without delay
13251         player->move_delay = 0;
13252       }
13253     }
13254
13255     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13256         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13257         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13258         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13259         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13260         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13261         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13262         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13263     {
13264       ExitPlayer(player);
13265
13266       if (game.players_still_needed == 0 &&
13267           (game.friends_still_needed == 0 ||
13268            IS_SP_ELEMENT(Tile[jx][jy])))
13269         LevelSolved();
13270     }
13271
13272     player->last_jx = jx;
13273     player->last_jy = jy;
13274
13275     // this breaks one level: "machine", level 000
13276     {
13277       int move_direction = player->MovDir;
13278       int enter_side = MV_DIR_OPPOSITE(move_direction);
13279       int leave_side = move_direction;
13280       int old_jx = last_jx;
13281       int old_jy = last_jy;
13282       int old_element = Tile[old_jx][old_jy];
13283       int new_element = Tile[jx][jy];
13284
13285       if (IS_CUSTOM_ELEMENT(old_element))
13286         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13287                                    CE_LEFT_BY_PLAYER,
13288                                    player->index_bit, leave_side);
13289
13290       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13291                                           CE_PLAYER_LEAVES_X,
13292                                           player->index_bit, leave_side);
13293
13294       if (IS_CUSTOM_ELEMENT(new_element))
13295         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13296                                    player->index_bit, enter_side);
13297
13298       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13299                                           CE_PLAYER_ENTERS_X,
13300                                           player->index_bit, enter_side);
13301
13302       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13303                                         CE_MOVE_OF_X, move_direction);
13304     }
13305
13306     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13307     {
13308       TestIfPlayerTouchesBadThing(jx, jy);
13309       TestIfPlayerTouchesCustomElement(jx, jy);
13310
13311       /* needed because pushed element has not yet reached its destination,
13312          so it would trigger a change event at its previous field location */
13313       if (!player->is_pushing)
13314         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13315
13316       if (level.finish_dig_collect &&
13317           (player->is_digging || player->is_collecting))
13318       {
13319         int last_element = player->last_removed_element;
13320         int move_direction = player->MovDir;
13321         int enter_side = MV_DIR_OPPOSITE(move_direction);
13322         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13323                             CE_PLAYER_COLLECTS_X);
13324
13325         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13326                                             player->index_bit, enter_side);
13327
13328         player->last_removed_element = EL_UNDEFINED;
13329       }
13330
13331       if (!player->active)
13332         RemovePlayer(player);
13333     }
13334
13335     if (level.use_step_counter)
13336       CheckLevelTime_StepCounter();
13337
13338     if (tape.single_step && tape.recording && !tape.pausing &&
13339         !player->programmed_action)
13340       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13341
13342     if (!player->programmed_action)
13343       CheckSaveEngineSnapshot(player);
13344   }
13345 }
13346
13347 void ScrollScreen(struct PlayerInfo *player, int mode)
13348 {
13349   static unsigned int screen_frame_counter = 0;
13350
13351   if (mode == SCROLL_INIT)
13352   {
13353     // set scrolling step size according to actual player's moving speed
13354     ScrollStepSize = TILEX / player->move_delay_value;
13355
13356     screen_frame_counter = FrameCounter;
13357     ScreenMovDir = player->MovDir;
13358     ScreenMovPos = player->MovPos;
13359     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13360     return;
13361   }
13362   else if (!FrameReached(&screen_frame_counter, 1))
13363     return;
13364
13365   if (ScreenMovPos)
13366   {
13367     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13368     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13369     redraw_mask |= REDRAW_FIELD;
13370   }
13371   else
13372     ScreenMovDir = MV_NONE;
13373 }
13374
13375 void CheckNextToConditions(int x, int y)
13376 {
13377   int element = Tile[x][y];
13378
13379   if (IS_PLAYER(x, y))
13380     TestIfPlayerNextToCustomElement(x, y);
13381
13382   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13383       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13384     TestIfElementNextToCustomElement(x, y);
13385 }
13386
13387 void TestIfPlayerNextToCustomElement(int x, int y)
13388 {
13389   static int xy[4][2] =
13390   {
13391     { 0, -1 },
13392     { -1, 0 },
13393     { +1, 0 },
13394     { 0, +1 }
13395   };
13396   static int trigger_sides[4][2] =
13397   {
13398     // center side       border side
13399     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13400     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13401     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13402     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13403   };
13404   int i;
13405
13406   if (!IS_PLAYER(x, y))
13407     return;
13408
13409   struct PlayerInfo *player = PLAYERINFO(x, y);
13410
13411   if (player->is_moving)
13412     return;
13413
13414   for (i = 0; i < NUM_DIRECTIONS; i++)
13415   {
13416     int xx = x + xy[i][0];
13417     int yy = y + xy[i][1];
13418     int border_side = trigger_sides[i][1];
13419     int border_element;
13420
13421     if (!IN_LEV_FIELD(xx, yy))
13422       continue;
13423
13424     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13425       continue;         // center and border element not connected
13426
13427     border_element = Tile[xx][yy];
13428
13429     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13430                                player->index_bit, border_side);
13431     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13432                                         CE_PLAYER_NEXT_TO_X,
13433                                         player->index_bit, border_side);
13434
13435     /* use player element that is initially defined in the level playfield,
13436        not the player element that corresponds to the runtime player number
13437        (example: a level that contains EL_PLAYER_3 as the only player would
13438        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13439
13440     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13441                              CE_NEXT_TO_X, border_side);
13442   }
13443 }
13444
13445 void TestIfPlayerTouchesCustomElement(int x, int y)
13446 {
13447   static int xy[4][2] =
13448   {
13449     { 0, -1 },
13450     { -1, 0 },
13451     { +1, 0 },
13452     { 0, +1 }
13453   };
13454   static int trigger_sides[4][2] =
13455   {
13456     // center side       border side
13457     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13458     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13459     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13460     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13461   };
13462   static int touch_dir[4] =
13463   {
13464     MV_LEFT | MV_RIGHT,
13465     MV_UP   | MV_DOWN,
13466     MV_UP   | MV_DOWN,
13467     MV_LEFT | MV_RIGHT
13468   };
13469   int center_element = Tile[x][y];      // should always be non-moving!
13470   int i;
13471
13472   for (i = 0; i < NUM_DIRECTIONS; i++)
13473   {
13474     int xx = x + xy[i][0];
13475     int yy = y + xy[i][1];
13476     int center_side = trigger_sides[i][0];
13477     int border_side = trigger_sides[i][1];
13478     int border_element;
13479
13480     if (!IN_LEV_FIELD(xx, yy))
13481       continue;
13482
13483     if (IS_PLAYER(x, y))                // player found at center element
13484     {
13485       struct PlayerInfo *player = PLAYERINFO(x, y);
13486
13487       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13488         border_element = Tile[xx][yy];          // may be moving!
13489       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13490         border_element = Tile[xx][yy];
13491       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13492         border_element = MovingOrBlocked2Element(xx, yy);
13493       else
13494         continue;               // center and border element do not touch
13495
13496       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13497                                  player->index_bit, border_side);
13498       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13499                                           CE_PLAYER_TOUCHES_X,
13500                                           player->index_bit, border_side);
13501
13502       {
13503         /* use player element that is initially defined in the level playfield,
13504            not the player element that corresponds to the runtime player number
13505            (example: a level that contains EL_PLAYER_3 as the only player would
13506            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13507         int player_element = PLAYERINFO(x, y)->initial_element;
13508
13509         CheckElementChangeBySide(xx, yy, border_element, player_element,
13510                                  CE_TOUCHING_X, border_side);
13511       }
13512     }
13513     else if (IS_PLAYER(xx, yy))         // player found at border element
13514     {
13515       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13516
13517       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13518       {
13519         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13520           continue;             // center and border element do not touch
13521       }
13522
13523       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13524                                  player->index_bit, center_side);
13525       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13526                                           CE_PLAYER_TOUCHES_X,
13527                                           player->index_bit, center_side);
13528
13529       {
13530         /* use player element that is initially defined in the level playfield,
13531            not the player element that corresponds to the runtime player number
13532            (example: a level that contains EL_PLAYER_3 as the only player would
13533            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13534         int player_element = PLAYERINFO(xx, yy)->initial_element;
13535
13536         CheckElementChangeBySide(x, y, center_element, player_element,
13537                                  CE_TOUCHING_X, center_side);
13538       }
13539
13540       break;
13541     }
13542   }
13543 }
13544
13545 void TestIfElementNextToCustomElement(int x, int y)
13546 {
13547   static int xy[4][2] =
13548   {
13549     { 0, -1 },
13550     { -1, 0 },
13551     { +1, 0 },
13552     { 0, +1 }
13553   };
13554   static int trigger_sides[4][2] =
13555   {
13556     // center side      border side
13557     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13558     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13559     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13560     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13561   };
13562   int center_element = Tile[x][y];      // should always be non-moving!
13563   int i;
13564
13565   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13566     return;
13567
13568   for (i = 0; i < NUM_DIRECTIONS; i++)
13569   {
13570     int xx = x + xy[i][0];
13571     int yy = y + xy[i][1];
13572     int border_side = trigger_sides[i][1];
13573     int border_element;
13574
13575     if (!IN_LEV_FIELD(xx, yy))
13576       continue;
13577
13578     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13579       continue;                 // center and border element not connected
13580
13581     border_element = Tile[xx][yy];
13582
13583     // check for change of center element (but change it only once)
13584     if (CheckElementChangeBySide(x, y, center_element, border_element,
13585                                  CE_NEXT_TO_X, border_side))
13586       break;
13587   }
13588 }
13589
13590 void TestIfElementTouchesCustomElement(int x, int y)
13591 {
13592   static int xy[4][2] =
13593   {
13594     { 0, -1 },
13595     { -1, 0 },
13596     { +1, 0 },
13597     { 0, +1 }
13598   };
13599   static int trigger_sides[4][2] =
13600   {
13601     // center side      border side
13602     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13603     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13604     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13605     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13606   };
13607   static int touch_dir[4] =
13608   {
13609     MV_LEFT | MV_RIGHT,
13610     MV_UP   | MV_DOWN,
13611     MV_UP   | MV_DOWN,
13612     MV_LEFT | MV_RIGHT
13613   };
13614   boolean change_center_element = FALSE;
13615   int center_element = Tile[x][y];      // should always be non-moving!
13616   int border_element_old[NUM_DIRECTIONS];
13617   int i;
13618
13619   for (i = 0; i < NUM_DIRECTIONS; i++)
13620   {
13621     int xx = x + xy[i][0];
13622     int yy = y + xy[i][1];
13623     int border_element;
13624
13625     border_element_old[i] = -1;
13626
13627     if (!IN_LEV_FIELD(xx, yy))
13628       continue;
13629
13630     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13631       border_element = Tile[xx][yy];    // may be moving!
13632     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13633       border_element = Tile[xx][yy];
13634     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13635       border_element = MovingOrBlocked2Element(xx, yy);
13636     else
13637       continue;                 // center and border element do not touch
13638
13639     border_element_old[i] = border_element;
13640   }
13641
13642   for (i = 0; i < NUM_DIRECTIONS; i++)
13643   {
13644     int xx = x + xy[i][0];
13645     int yy = y + xy[i][1];
13646     int center_side = trigger_sides[i][0];
13647     int border_element = border_element_old[i];
13648
13649     if (border_element == -1)
13650       continue;
13651
13652     // check for change of border element
13653     CheckElementChangeBySide(xx, yy, border_element, center_element,
13654                              CE_TOUCHING_X, center_side);
13655
13656     // (center element cannot be player, so we dont have to check this here)
13657   }
13658
13659   for (i = 0; i < NUM_DIRECTIONS; i++)
13660   {
13661     int xx = x + xy[i][0];
13662     int yy = y + xy[i][1];
13663     int border_side = trigger_sides[i][1];
13664     int border_element = border_element_old[i];
13665
13666     if (border_element == -1)
13667       continue;
13668
13669     // check for change of center element (but change it only once)
13670     if (!change_center_element)
13671       change_center_element =
13672         CheckElementChangeBySide(x, y, center_element, border_element,
13673                                  CE_TOUCHING_X, border_side);
13674
13675     if (IS_PLAYER(xx, yy))
13676     {
13677       /* use player element that is initially defined in the level playfield,
13678          not the player element that corresponds to the runtime player number
13679          (example: a level that contains EL_PLAYER_3 as the only player would
13680          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13681       int player_element = PLAYERINFO(xx, yy)->initial_element;
13682
13683       CheckElementChangeBySide(x, y, center_element, player_element,
13684                                CE_TOUCHING_X, border_side);
13685     }
13686   }
13687 }
13688
13689 void TestIfElementHitsCustomElement(int x, int y, int direction)
13690 {
13691   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13692   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13693   int hitx = x + dx, hity = y + dy;
13694   int hitting_element = Tile[x][y];
13695   int touched_element;
13696
13697   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13698     return;
13699
13700   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13701                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13702
13703   if (IN_LEV_FIELD(hitx, hity))
13704   {
13705     int opposite_direction = MV_DIR_OPPOSITE(direction);
13706     int hitting_side = direction;
13707     int touched_side = opposite_direction;
13708     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13709                           MovDir[hitx][hity] != direction ||
13710                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13711
13712     object_hit = TRUE;
13713
13714     if (object_hit)
13715     {
13716       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13717                                CE_HITTING_X, touched_side);
13718
13719       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13720                                CE_HIT_BY_X, hitting_side);
13721
13722       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13723                                CE_HIT_BY_SOMETHING, opposite_direction);
13724
13725       if (IS_PLAYER(hitx, hity))
13726       {
13727         /* use player element that is initially defined in the level playfield,
13728            not the player element that corresponds to the runtime player number
13729            (example: a level that contains EL_PLAYER_3 as the only player would
13730            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13731         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13732
13733         CheckElementChangeBySide(x, y, hitting_element, player_element,
13734                                  CE_HITTING_X, touched_side);
13735       }
13736     }
13737   }
13738
13739   // "hitting something" is also true when hitting the playfield border
13740   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13741                            CE_HITTING_SOMETHING, direction);
13742 }
13743
13744 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13745 {
13746   int i, kill_x = -1, kill_y = -1;
13747
13748   int bad_element = -1;
13749   static int test_xy[4][2] =
13750   {
13751     { 0, -1 },
13752     { -1, 0 },
13753     { +1, 0 },
13754     { 0, +1 }
13755   };
13756   static int test_dir[4] =
13757   {
13758     MV_UP,
13759     MV_LEFT,
13760     MV_RIGHT,
13761     MV_DOWN
13762   };
13763
13764   for (i = 0; i < NUM_DIRECTIONS; i++)
13765   {
13766     int test_x, test_y, test_move_dir, test_element;
13767
13768     test_x = good_x + test_xy[i][0];
13769     test_y = good_y + test_xy[i][1];
13770
13771     if (!IN_LEV_FIELD(test_x, test_y))
13772       continue;
13773
13774     test_move_dir =
13775       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13776
13777     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13778
13779     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13780        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13781     */
13782     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13783         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13784     {
13785       kill_x = test_x;
13786       kill_y = test_y;
13787       bad_element = test_element;
13788
13789       break;
13790     }
13791   }
13792
13793   if (kill_x != -1 || kill_y != -1)
13794   {
13795     if (IS_PLAYER(good_x, good_y))
13796     {
13797       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13798
13799       if (player->shield_deadly_time_left > 0 &&
13800           !IS_INDESTRUCTIBLE(bad_element))
13801         Bang(kill_x, kill_y);
13802       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13803         KillPlayer(player);
13804     }
13805     else
13806       Bang(good_x, good_y);
13807   }
13808 }
13809
13810 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13811 {
13812   int i, kill_x = -1, kill_y = -1;
13813   int bad_element = Tile[bad_x][bad_y];
13814   static int test_xy[4][2] =
13815   {
13816     { 0, -1 },
13817     { -1, 0 },
13818     { +1, 0 },
13819     { 0, +1 }
13820   };
13821   static int touch_dir[4] =
13822   {
13823     MV_LEFT | MV_RIGHT,
13824     MV_UP   | MV_DOWN,
13825     MV_UP   | MV_DOWN,
13826     MV_LEFT | MV_RIGHT
13827   };
13828   static int test_dir[4] =
13829   {
13830     MV_UP,
13831     MV_LEFT,
13832     MV_RIGHT,
13833     MV_DOWN
13834   };
13835
13836   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13837     return;
13838
13839   for (i = 0; i < NUM_DIRECTIONS; i++)
13840   {
13841     int test_x, test_y, test_move_dir, test_element;
13842
13843     test_x = bad_x + test_xy[i][0];
13844     test_y = bad_y + test_xy[i][1];
13845
13846     if (!IN_LEV_FIELD(test_x, test_y))
13847       continue;
13848
13849     test_move_dir =
13850       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13851
13852     test_element = Tile[test_x][test_y];
13853
13854     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13855        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13856     */
13857     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13858         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13859     {
13860       // good thing is player or penguin that does not move away
13861       if (IS_PLAYER(test_x, test_y))
13862       {
13863         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13864
13865         if (bad_element == EL_ROBOT && player->is_moving)
13866           continue;     // robot does not kill player if he is moving
13867
13868         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13869         {
13870           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13871             continue;           // center and border element do not touch
13872         }
13873
13874         kill_x = test_x;
13875         kill_y = test_y;
13876
13877         break;
13878       }
13879       else if (test_element == EL_PENGUIN)
13880       {
13881         kill_x = test_x;
13882         kill_y = test_y;
13883
13884         break;
13885       }
13886     }
13887   }
13888
13889   if (kill_x != -1 || kill_y != -1)
13890   {
13891     if (IS_PLAYER(kill_x, kill_y))
13892     {
13893       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13894
13895       if (player->shield_deadly_time_left > 0 &&
13896           !IS_INDESTRUCTIBLE(bad_element))
13897         Bang(bad_x, bad_y);
13898       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13899         KillPlayer(player);
13900     }
13901     else
13902       Bang(kill_x, kill_y);
13903   }
13904 }
13905
13906 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13907 {
13908   int bad_element = Tile[bad_x][bad_y];
13909   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13910   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13911   int test_x = bad_x + dx, test_y = bad_y + dy;
13912   int test_move_dir, test_element;
13913   int kill_x = -1, kill_y = -1;
13914
13915   if (!IN_LEV_FIELD(test_x, test_y))
13916     return;
13917
13918   test_move_dir =
13919     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13920
13921   test_element = Tile[test_x][test_y];
13922
13923   if (test_move_dir != bad_move_dir)
13924   {
13925     // good thing can be player or penguin that does not move away
13926     if (IS_PLAYER(test_x, test_y))
13927     {
13928       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13929
13930       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13931          player as being hit when he is moving towards the bad thing, because
13932          the "get hit by" condition would be lost after the player stops) */
13933       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13934         return;         // player moves away from bad thing
13935
13936       kill_x = test_x;
13937       kill_y = test_y;
13938     }
13939     else if (test_element == EL_PENGUIN)
13940     {
13941       kill_x = test_x;
13942       kill_y = test_y;
13943     }
13944   }
13945
13946   if (kill_x != -1 || kill_y != -1)
13947   {
13948     if (IS_PLAYER(kill_x, kill_y))
13949     {
13950       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13951
13952       if (player->shield_deadly_time_left > 0 &&
13953           !IS_INDESTRUCTIBLE(bad_element))
13954         Bang(bad_x, bad_y);
13955       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13956         KillPlayer(player);
13957     }
13958     else
13959       Bang(kill_x, kill_y);
13960   }
13961 }
13962
13963 void TestIfPlayerTouchesBadThing(int x, int y)
13964 {
13965   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13966 }
13967
13968 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13969 {
13970   TestIfGoodThingHitsBadThing(x, y, move_dir);
13971 }
13972
13973 void TestIfBadThingTouchesPlayer(int x, int y)
13974 {
13975   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13976 }
13977
13978 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13979 {
13980   TestIfBadThingHitsGoodThing(x, y, move_dir);
13981 }
13982
13983 void TestIfFriendTouchesBadThing(int x, int y)
13984 {
13985   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13986 }
13987
13988 void TestIfBadThingTouchesFriend(int x, int y)
13989 {
13990   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13991 }
13992
13993 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13994 {
13995   int i, kill_x = bad_x, kill_y = bad_y;
13996   static int xy[4][2] =
13997   {
13998     { 0, -1 },
13999     { -1, 0 },
14000     { +1, 0 },
14001     { 0, +1 }
14002   };
14003
14004   for (i = 0; i < NUM_DIRECTIONS; i++)
14005   {
14006     int x, y, element;
14007
14008     x = bad_x + xy[i][0];
14009     y = bad_y + xy[i][1];
14010     if (!IN_LEV_FIELD(x, y))
14011       continue;
14012
14013     element = Tile[x][y];
14014     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14015         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14016     {
14017       kill_x = x;
14018       kill_y = y;
14019       break;
14020     }
14021   }
14022
14023   if (kill_x != bad_x || kill_y != bad_y)
14024     Bang(bad_x, bad_y);
14025 }
14026
14027 void KillPlayer(struct PlayerInfo *player)
14028 {
14029   int jx = player->jx, jy = player->jy;
14030
14031   if (!player->active)
14032     return;
14033
14034 #if 0
14035   Debug("game:playing:KillPlayer",
14036         "0: killed == %d, active == %d, reanimated == %d",
14037         player->killed, player->active, player->reanimated);
14038 #endif
14039
14040   /* the following code was introduced to prevent an infinite loop when calling
14041      -> Bang()
14042      -> CheckTriggeredElementChangeExt()
14043      -> ExecuteCustomElementAction()
14044      -> KillPlayer()
14045      -> (infinitely repeating the above sequence of function calls)
14046      which occurs when killing the player while having a CE with the setting
14047      "kill player X when explosion of <player X>"; the solution using a new
14048      field "player->killed" was chosen for backwards compatibility, although
14049      clever use of the fields "player->active" etc. would probably also work */
14050 #if 1
14051   if (player->killed)
14052     return;
14053 #endif
14054
14055   player->killed = TRUE;
14056
14057   // remove accessible field at the player's position
14058   Tile[jx][jy] = EL_EMPTY;
14059
14060   // deactivate shield (else Bang()/Explode() would not work right)
14061   player->shield_normal_time_left = 0;
14062   player->shield_deadly_time_left = 0;
14063
14064 #if 0
14065   Debug("game:playing:KillPlayer",
14066         "1: killed == %d, active == %d, reanimated == %d",
14067         player->killed, player->active, player->reanimated);
14068 #endif
14069
14070   Bang(jx, jy);
14071
14072 #if 0
14073   Debug("game:playing:KillPlayer",
14074         "2: killed == %d, active == %d, reanimated == %d",
14075         player->killed, player->active, player->reanimated);
14076 #endif
14077
14078   if (player->reanimated)       // killed player may have been reanimated
14079     player->killed = player->reanimated = FALSE;
14080   else
14081     BuryPlayer(player);
14082 }
14083
14084 static void KillPlayerUnlessEnemyProtected(int x, int y)
14085 {
14086   if (!PLAYER_ENEMY_PROTECTED(x, y))
14087     KillPlayer(PLAYERINFO(x, y));
14088 }
14089
14090 static void KillPlayerUnlessExplosionProtected(int x, int y)
14091 {
14092   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14093     KillPlayer(PLAYERINFO(x, y));
14094 }
14095
14096 void BuryPlayer(struct PlayerInfo *player)
14097 {
14098   int jx = player->jx, jy = player->jy;
14099
14100   if (!player->active)
14101     return;
14102
14103   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14104   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14105
14106   RemovePlayer(player);
14107
14108   player->buried = TRUE;
14109
14110   if (game.all_players_gone)
14111     game.GameOver = TRUE;
14112 }
14113
14114 void RemovePlayer(struct PlayerInfo *player)
14115 {
14116   int jx = player->jx, jy = player->jy;
14117   int i, found = FALSE;
14118
14119   player->present = FALSE;
14120   player->active = FALSE;
14121
14122   // required for some CE actions (even if the player is not active anymore)
14123   player->MovPos = 0;
14124
14125   if (!ExplodeField[jx][jy])
14126     StorePlayer[jx][jy] = 0;
14127
14128   if (player->is_moving)
14129     TEST_DrawLevelField(player->last_jx, player->last_jy);
14130
14131   for (i = 0; i < MAX_PLAYERS; i++)
14132     if (stored_player[i].active)
14133       found = TRUE;
14134
14135   if (!found)
14136   {
14137     game.all_players_gone = TRUE;
14138     game.GameOver = TRUE;
14139   }
14140
14141   game.exit_x = game.robot_wheel_x = jx;
14142   game.exit_y = game.robot_wheel_y = jy;
14143 }
14144
14145 void ExitPlayer(struct PlayerInfo *player)
14146 {
14147   DrawPlayer(player);   // needed here only to cleanup last field
14148   RemovePlayer(player);
14149
14150   if (game.players_still_needed > 0)
14151     game.players_still_needed--;
14152 }
14153
14154 static void SetFieldForSnapping(int x, int y, int element, int direction,
14155                                 int player_index_bit)
14156 {
14157   struct ElementInfo *ei = &element_info[element];
14158   int direction_bit = MV_DIR_TO_BIT(direction);
14159   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14160   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14161                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14162
14163   Tile[x][y] = EL_ELEMENT_SNAPPING;
14164   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14165   MovDir[x][y] = direction;
14166   Store[x][y] = element;
14167   Store2[x][y] = player_index_bit;
14168
14169   ResetGfxAnimation(x, y);
14170
14171   GfxElement[x][y] = element;
14172   GfxAction[x][y] = action;
14173   GfxDir[x][y] = direction;
14174   GfxFrame[x][y] = -1;
14175 }
14176
14177 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14178                                    int player_index_bit)
14179 {
14180   TestIfElementTouchesCustomElement(x, y);      // for empty space
14181
14182   if (level.finish_dig_collect)
14183   {
14184     int dig_side = MV_DIR_OPPOSITE(direction);
14185     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14186                         CE_PLAYER_COLLECTS_X);
14187
14188     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14189                                         player_index_bit, dig_side);
14190     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14191                                         player_index_bit, dig_side);
14192   }
14193 }
14194
14195 /*
14196   =============================================================================
14197   checkDiagonalPushing()
14198   -----------------------------------------------------------------------------
14199   check if diagonal input device direction results in pushing of object
14200   (by checking if the alternative direction is walkable, diggable, ...)
14201   =============================================================================
14202 */
14203
14204 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14205                                     int x, int y, int real_dx, int real_dy)
14206 {
14207   int jx, jy, dx, dy, xx, yy;
14208
14209   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14210     return TRUE;
14211
14212   // diagonal direction: check alternative direction
14213   jx = player->jx;
14214   jy = player->jy;
14215   dx = x - jx;
14216   dy = y - jy;
14217   xx = jx + (dx == 0 ? real_dx : 0);
14218   yy = jy + (dy == 0 ? real_dy : 0);
14219
14220   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14221 }
14222
14223 /*
14224   =============================================================================
14225   DigField()
14226   -----------------------------------------------------------------------------
14227   x, y:                 field next to player (non-diagonal) to try to dig to
14228   real_dx, real_dy:     direction as read from input device (can be diagonal)
14229   =============================================================================
14230 */
14231
14232 static int DigField(struct PlayerInfo *player,
14233                     int oldx, int oldy, int x, int y,
14234                     int real_dx, int real_dy, int mode)
14235 {
14236   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14237   boolean player_was_pushing = player->is_pushing;
14238   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14239   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14240   int jx = oldx, jy = oldy;
14241   int dx = x - jx, dy = y - jy;
14242   int nextx = x + dx, nexty = y + dy;
14243   int move_direction = (dx == -1 ? MV_LEFT  :
14244                         dx == +1 ? MV_RIGHT :
14245                         dy == -1 ? MV_UP    :
14246                         dy == +1 ? MV_DOWN  : MV_NONE);
14247   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14248   int dig_side = MV_DIR_OPPOSITE(move_direction);
14249   int old_element = Tile[jx][jy];
14250   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14251   int collect_count;
14252
14253   if (is_player)                // function can also be called by EL_PENGUIN
14254   {
14255     if (player->MovPos == 0)
14256     {
14257       player->is_digging = FALSE;
14258       player->is_collecting = FALSE;
14259     }
14260
14261     if (player->MovPos == 0)    // last pushing move finished
14262       player->is_pushing = FALSE;
14263
14264     if (mode == DF_NO_PUSH)     // player just stopped pushing
14265     {
14266       player->is_switching = FALSE;
14267       player->push_delay = -1;
14268
14269       return MP_NO_ACTION;
14270     }
14271   }
14272   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14273     old_element = Back[jx][jy];
14274
14275   // in case of element dropped at player position, check background
14276   else if (Back[jx][jy] != EL_EMPTY &&
14277            game.engine_version >= VERSION_IDENT(2,2,0,0))
14278     old_element = Back[jx][jy];
14279
14280   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14281     return MP_NO_ACTION;        // field has no opening in this direction
14282
14283   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14284     return MP_NO_ACTION;        // field has no opening in this direction
14285
14286   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14287   {
14288     SplashAcid(x, y);
14289
14290     Tile[jx][jy] = player->artwork_element;
14291     InitMovingField(jx, jy, MV_DOWN);
14292     Store[jx][jy] = EL_ACID;
14293     ContinueMoving(jx, jy);
14294     BuryPlayer(player);
14295
14296     return MP_DONT_RUN_INTO;
14297   }
14298
14299   if (player_can_move && DONT_RUN_INTO(element))
14300   {
14301     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14302
14303     return MP_DONT_RUN_INTO;
14304   }
14305
14306   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14307     return MP_NO_ACTION;
14308
14309   collect_count = element_info[element].collect_count_initial;
14310
14311   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14312     return MP_NO_ACTION;
14313
14314   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14315     player_can_move = player_can_move_or_snap;
14316
14317   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14318       game.engine_version >= VERSION_IDENT(2,2,0,0))
14319   {
14320     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14321                                player->index_bit, dig_side);
14322     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14323                                         player->index_bit, dig_side);
14324
14325     if (element == EL_DC_LANDMINE)
14326       Bang(x, y);
14327
14328     if (Tile[x][y] != element)          // field changed by snapping
14329       return MP_ACTION;
14330
14331     return MP_NO_ACTION;
14332   }
14333
14334   if (player->gravity && is_player && !player->is_auto_moving &&
14335       canFallDown(player) && move_direction != MV_DOWN &&
14336       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14337     return MP_NO_ACTION;        // player cannot walk here due to gravity
14338
14339   if (player_can_move &&
14340       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14341   {
14342     int sound_element = SND_ELEMENT(element);
14343     int sound_action = ACTION_WALKING;
14344
14345     if (IS_RND_GATE(element))
14346     {
14347       if (!player->key[RND_GATE_NR(element)])
14348         return MP_NO_ACTION;
14349     }
14350     else if (IS_RND_GATE_GRAY(element))
14351     {
14352       if (!player->key[RND_GATE_GRAY_NR(element)])
14353         return MP_NO_ACTION;
14354     }
14355     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14356     {
14357       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14358         return MP_NO_ACTION;
14359     }
14360     else if (element == EL_EXIT_OPEN ||
14361              element == EL_EM_EXIT_OPEN ||
14362              element == EL_EM_EXIT_OPENING ||
14363              element == EL_STEEL_EXIT_OPEN ||
14364              element == EL_EM_STEEL_EXIT_OPEN ||
14365              element == EL_EM_STEEL_EXIT_OPENING ||
14366              element == EL_SP_EXIT_OPEN ||
14367              element == EL_SP_EXIT_OPENING)
14368     {
14369       sound_action = ACTION_PASSING;    // player is passing exit
14370     }
14371     else if (element == EL_EMPTY)
14372     {
14373       sound_action = ACTION_MOVING;             // nothing to walk on
14374     }
14375
14376     // play sound from background or player, whatever is available
14377     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14378       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14379     else
14380       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14381   }
14382   else if (player_can_move &&
14383            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14384   {
14385     if (!ACCESS_FROM(element, opposite_direction))
14386       return MP_NO_ACTION;      // field not accessible from this direction
14387
14388     if (CAN_MOVE(element))      // only fixed elements can be passed!
14389       return MP_NO_ACTION;
14390
14391     if (IS_EM_GATE(element))
14392     {
14393       if (!player->key[EM_GATE_NR(element)])
14394         return MP_NO_ACTION;
14395     }
14396     else if (IS_EM_GATE_GRAY(element))
14397     {
14398       if (!player->key[EM_GATE_GRAY_NR(element)])
14399         return MP_NO_ACTION;
14400     }
14401     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14402     {
14403       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14404         return MP_NO_ACTION;
14405     }
14406     else if (IS_EMC_GATE(element))
14407     {
14408       if (!player->key[EMC_GATE_NR(element)])
14409         return MP_NO_ACTION;
14410     }
14411     else if (IS_EMC_GATE_GRAY(element))
14412     {
14413       if (!player->key[EMC_GATE_GRAY_NR(element)])
14414         return MP_NO_ACTION;
14415     }
14416     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14417     {
14418       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14419         return MP_NO_ACTION;
14420     }
14421     else if (element == EL_DC_GATE_WHITE ||
14422              element == EL_DC_GATE_WHITE_GRAY ||
14423              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14424     {
14425       if (player->num_white_keys == 0)
14426         return MP_NO_ACTION;
14427
14428       player->num_white_keys--;
14429     }
14430     else if (IS_SP_PORT(element))
14431     {
14432       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14433           element == EL_SP_GRAVITY_PORT_RIGHT ||
14434           element == EL_SP_GRAVITY_PORT_UP ||
14435           element == EL_SP_GRAVITY_PORT_DOWN)
14436         player->gravity = !player->gravity;
14437       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14438                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14439                element == EL_SP_GRAVITY_ON_PORT_UP ||
14440                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14441         player->gravity = TRUE;
14442       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14443                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14444                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14445                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14446         player->gravity = FALSE;
14447     }
14448
14449     // automatically move to the next field with double speed
14450     player->programmed_action = move_direction;
14451
14452     if (player->move_delay_reset_counter == 0)
14453     {
14454       player->move_delay_reset_counter = 2;     // two double speed steps
14455
14456       DOUBLE_PLAYER_SPEED(player);
14457     }
14458
14459     PlayLevelSoundAction(x, y, ACTION_PASSING);
14460   }
14461   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14462   {
14463     RemoveField(x, y);
14464
14465     if (mode != DF_SNAP)
14466     {
14467       GfxElement[x][y] = GFX_ELEMENT(element);
14468       player->is_digging = TRUE;
14469     }
14470
14471     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14472
14473     // use old behaviour for old levels (digging)
14474     if (!level.finish_dig_collect)
14475     {
14476       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14477                                           player->index_bit, dig_side);
14478
14479       // if digging triggered player relocation, finish digging tile
14480       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14481         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14482     }
14483
14484     if (mode == DF_SNAP)
14485     {
14486       if (level.block_snap_field)
14487         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14488       else
14489         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14490
14491       // use old behaviour for old levels (snapping)
14492       if (!level.finish_dig_collect)
14493         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14494                                             player->index_bit, dig_side);
14495     }
14496   }
14497   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14498   {
14499     RemoveField(x, y);
14500
14501     if (is_player && mode != DF_SNAP)
14502     {
14503       GfxElement[x][y] = element;
14504       player->is_collecting = TRUE;
14505     }
14506
14507     if (element == EL_SPEED_PILL)
14508     {
14509       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14510     }
14511     else if (element == EL_EXTRA_TIME && level.time > 0)
14512     {
14513       TimeLeft += level.extra_time;
14514
14515       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14516
14517       DisplayGameControlValues();
14518     }
14519     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14520     {
14521       player->shield_normal_time_left += level.shield_normal_time;
14522       if (element == EL_SHIELD_DEADLY)
14523         player->shield_deadly_time_left += level.shield_deadly_time;
14524     }
14525     else if (element == EL_DYNAMITE ||
14526              element == EL_EM_DYNAMITE ||
14527              element == EL_SP_DISK_RED)
14528     {
14529       if (player->inventory_size < MAX_INVENTORY_SIZE)
14530         player->inventory_element[player->inventory_size++] = element;
14531
14532       DrawGameDoorValues();
14533     }
14534     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14535     {
14536       player->dynabomb_count++;
14537       player->dynabombs_left++;
14538     }
14539     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14540     {
14541       player->dynabomb_size++;
14542     }
14543     else if (element == EL_DYNABOMB_INCREASE_POWER)
14544     {
14545       player->dynabomb_xl = TRUE;
14546     }
14547     else if (IS_KEY(element))
14548     {
14549       player->key[KEY_NR(element)] = TRUE;
14550
14551       DrawGameDoorValues();
14552     }
14553     else if (element == EL_DC_KEY_WHITE)
14554     {
14555       player->num_white_keys++;
14556
14557       // display white keys?
14558       // DrawGameDoorValues();
14559     }
14560     else if (IS_ENVELOPE(element))
14561     {
14562       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14563
14564       if (!wait_for_snapping)
14565         player->show_envelope = element;
14566     }
14567     else if (element == EL_EMC_LENSES)
14568     {
14569       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14570
14571       RedrawAllInvisibleElementsForLenses();
14572     }
14573     else if (element == EL_EMC_MAGNIFIER)
14574     {
14575       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14576
14577       RedrawAllInvisibleElementsForMagnifier();
14578     }
14579     else if (IS_DROPPABLE(element) ||
14580              IS_THROWABLE(element))     // can be collected and dropped
14581     {
14582       int i;
14583
14584       if (collect_count == 0)
14585         player->inventory_infinite_element = element;
14586       else
14587         for (i = 0; i < collect_count; i++)
14588           if (player->inventory_size < MAX_INVENTORY_SIZE)
14589             player->inventory_element[player->inventory_size++] = element;
14590
14591       DrawGameDoorValues();
14592     }
14593     else if (collect_count > 0)
14594     {
14595       game.gems_still_needed -= collect_count;
14596       if (game.gems_still_needed < 0)
14597         game.gems_still_needed = 0;
14598
14599       game.snapshot.collected_item = TRUE;
14600
14601       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14602
14603       DisplayGameControlValues();
14604     }
14605
14606     RaiseScoreElement(element);
14607     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14608
14609     // use old behaviour for old levels (collecting)
14610     if (!level.finish_dig_collect && is_player)
14611     {
14612       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14613                                           player->index_bit, dig_side);
14614
14615       // if collecting triggered player relocation, finish collecting tile
14616       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14617         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14618     }
14619
14620     if (mode == DF_SNAP)
14621     {
14622       if (level.block_snap_field)
14623         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14624       else
14625         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14626
14627       // use old behaviour for old levels (snapping)
14628       if (!level.finish_dig_collect)
14629         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14630                                             player->index_bit, dig_side);
14631     }
14632   }
14633   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14634   {
14635     if (mode == DF_SNAP && element != EL_BD_ROCK)
14636       return MP_NO_ACTION;
14637
14638     if (CAN_FALL(element) && dy)
14639       return MP_NO_ACTION;
14640
14641     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14642         !(element == EL_SPRING && level.use_spring_bug))
14643       return MP_NO_ACTION;
14644
14645     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14646         ((move_direction & MV_VERTICAL &&
14647           ((element_info[element].move_pattern & MV_LEFT &&
14648             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14649            (element_info[element].move_pattern & MV_RIGHT &&
14650             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14651          (move_direction & MV_HORIZONTAL &&
14652           ((element_info[element].move_pattern & MV_UP &&
14653             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14654            (element_info[element].move_pattern & MV_DOWN &&
14655             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14656       return MP_NO_ACTION;
14657
14658     // do not push elements already moving away faster than player
14659     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14660         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14661       return MP_NO_ACTION;
14662
14663     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14664     {
14665       if (player->push_delay_value == -1 || !player_was_pushing)
14666         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14667     }
14668     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14669     {
14670       if (player->push_delay_value == -1)
14671         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14672     }
14673     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14674     {
14675       if (!player->is_pushing)
14676         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14677     }
14678
14679     player->is_pushing = TRUE;
14680     player->is_active = TRUE;
14681
14682     if (!(IN_LEV_FIELD(nextx, nexty) &&
14683           (IS_FREE(nextx, nexty) ||
14684            (IS_SB_ELEMENT(element) &&
14685             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14686            (IS_CUSTOM_ELEMENT(element) &&
14687             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14688       return MP_NO_ACTION;
14689
14690     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14691       return MP_NO_ACTION;
14692
14693     if (player->push_delay == -1)       // new pushing; restart delay
14694       player->push_delay = 0;
14695
14696     if (player->push_delay < player->push_delay_value &&
14697         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14698         element != EL_SPRING && element != EL_BALLOON)
14699     {
14700       // make sure that there is no move delay before next try to push
14701       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14702         player->move_delay = 0;
14703
14704       return MP_NO_ACTION;
14705     }
14706
14707     if (IS_CUSTOM_ELEMENT(element) &&
14708         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14709     {
14710       if (!DigFieldByCE(nextx, nexty, element))
14711         return MP_NO_ACTION;
14712     }
14713
14714     if (IS_SB_ELEMENT(element))
14715     {
14716       boolean sokoban_task_solved = FALSE;
14717
14718       if (element == EL_SOKOBAN_FIELD_FULL)
14719       {
14720         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14721
14722         IncrementSokobanFieldsNeeded();
14723         IncrementSokobanObjectsNeeded();
14724       }
14725
14726       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14727       {
14728         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14729
14730         DecrementSokobanFieldsNeeded();
14731         DecrementSokobanObjectsNeeded();
14732
14733         // sokoban object was pushed from empty field to sokoban field
14734         if (Back[x][y] == EL_EMPTY)
14735           sokoban_task_solved = TRUE;
14736       }
14737
14738       Tile[x][y] = EL_SOKOBAN_OBJECT;
14739
14740       if (Back[x][y] == Back[nextx][nexty])
14741         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14742       else if (Back[x][y] != 0)
14743         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14744                                     ACTION_EMPTYING);
14745       else
14746         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14747                                     ACTION_FILLING);
14748
14749       if (sokoban_task_solved &&
14750           game.sokoban_fields_still_needed == 0 &&
14751           game.sokoban_objects_still_needed == 0 &&
14752           level.auto_exit_sokoban)
14753       {
14754         game.players_still_needed = 0;
14755
14756         LevelSolved();
14757
14758         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14759       }
14760     }
14761     else
14762       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14763
14764     InitMovingField(x, y, move_direction);
14765     GfxAction[x][y] = ACTION_PUSHING;
14766
14767     if (mode == DF_SNAP)
14768       ContinueMoving(x, y);
14769     else
14770       MovPos[x][y] = (dx != 0 ? dx : dy);
14771
14772     Pushed[x][y] = TRUE;
14773     Pushed[nextx][nexty] = TRUE;
14774
14775     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14776       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14777     else
14778       player->push_delay_value = -1;    // get new value later
14779
14780     // check for element change _after_ element has been pushed
14781     if (game.use_change_when_pushing_bug)
14782     {
14783       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14784                                  player->index_bit, dig_side);
14785       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14786                                           player->index_bit, dig_side);
14787     }
14788   }
14789   else if (IS_SWITCHABLE(element))
14790   {
14791     if (PLAYER_SWITCHING(player, x, y))
14792     {
14793       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14794                                           player->index_bit, dig_side);
14795
14796       return MP_ACTION;
14797     }
14798
14799     player->is_switching = TRUE;
14800     player->switch_x = x;
14801     player->switch_y = y;
14802
14803     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14804
14805     if (element == EL_ROBOT_WHEEL)
14806     {
14807       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14808
14809       game.robot_wheel_x = x;
14810       game.robot_wheel_y = y;
14811       game.robot_wheel_active = TRUE;
14812
14813       TEST_DrawLevelField(x, y);
14814     }
14815     else if (element == EL_SP_TERMINAL)
14816     {
14817       int xx, yy;
14818
14819       SCAN_PLAYFIELD(xx, yy)
14820       {
14821         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14822         {
14823           Bang(xx, yy);
14824         }
14825         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14826         {
14827           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14828
14829           ResetGfxAnimation(xx, yy);
14830           TEST_DrawLevelField(xx, yy);
14831         }
14832       }
14833     }
14834     else if (IS_BELT_SWITCH(element))
14835     {
14836       ToggleBeltSwitch(x, y);
14837     }
14838     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14839              element == EL_SWITCHGATE_SWITCH_DOWN ||
14840              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14841              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14842     {
14843       ToggleSwitchgateSwitch(x, y);
14844     }
14845     else if (element == EL_LIGHT_SWITCH ||
14846              element == EL_LIGHT_SWITCH_ACTIVE)
14847     {
14848       ToggleLightSwitch(x, y);
14849     }
14850     else if (element == EL_TIMEGATE_SWITCH ||
14851              element == EL_DC_TIMEGATE_SWITCH)
14852     {
14853       ActivateTimegateSwitch(x, y);
14854     }
14855     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14856              element == EL_BALLOON_SWITCH_RIGHT ||
14857              element == EL_BALLOON_SWITCH_UP    ||
14858              element == EL_BALLOON_SWITCH_DOWN  ||
14859              element == EL_BALLOON_SWITCH_NONE  ||
14860              element == EL_BALLOON_SWITCH_ANY)
14861     {
14862       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14863                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14864                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14865                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14866                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14867                              move_direction);
14868     }
14869     else if (element == EL_LAMP)
14870     {
14871       Tile[x][y] = EL_LAMP_ACTIVE;
14872       game.lights_still_needed--;
14873
14874       ResetGfxAnimation(x, y);
14875       TEST_DrawLevelField(x, y);
14876     }
14877     else if (element == EL_TIME_ORB_FULL)
14878     {
14879       Tile[x][y] = EL_TIME_ORB_EMPTY;
14880
14881       if (level.time > 0 || level.use_time_orb_bug)
14882       {
14883         TimeLeft += level.time_orb_time;
14884         game.no_time_limit = FALSE;
14885
14886         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14887
14888         DisplayGameControlValues();
14889       }
14890
14891       ResetGfxAnimation(x, y);
14892       TEST_DrawLevelField(x, y);
14893     }
14894     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14895              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14896     {
14897       int xx, yy;
14898
14899       game.ball_active = !game.ball_active;
14900
14901       SCAN_PLAYFIELD(xx, yy)
14902       {
14903         int e = Tile[xx][yy];
14904
14905         if (game.ball_active)
14906         {
14907           if (e == EL_EMC_MAGIC_BALL)
14908             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14909           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14910             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14911         }
14912         else
14913         {
14914           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14915             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14916           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14917             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14918         }
14919       }
14920     }
14921
14922     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14923                                         player->index_bit, dig_side);
14924
14925     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14926                                         player->index_bit, dig_side);
14927
14928     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14929                                         player->index_bit, dig_side);
14930
14931     return MP_ACTION;
14932   }
14933   else
14934   {
14935     if (!PLAYER_SWITCHING(player, x, y))
14936     {
14937       player->is_switching = TRUE;
14938       player->switch_x = x;
14939       player->switch_y = y;
14940
14941       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14942                                  player->index_bit, dig_side);
14943       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14944                                           player->index_bit, dig_side);
14945
14946       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14947                                  player->index_bit, dig_side);
14948       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14949                                           player->index_bit, dig_side);
14950     }
14951
14952     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14953                                player->index_bit, dig_side);
14954     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14955                                         player->index_bit, dig_side);
14956
14957     return MP_NO_ACTION;
14958   }
14959
14960   player->push_delay = -1;
14961
14962   if (is_player)                // function can also be called by EL_PENGUIN
14963   {
14964     if (Tile[x][y] != element)          // really digged/collected something
14965     {
14966       player->is_collecting = !player->is_digging;
14967       player->is_active = TRUE;
14968
14969       player->last_removed_element = element;
14970     }
14971   }
14972
14973   return MP_MOVING;
14974 }
14975
14976 static boolean DigFieldByCE(int x, int y, int digging_element)
14977 {
14978   int element = Tile[x][y];
14979
14980   if (!IS_FREE(x, y))
14981   {
14982     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14983                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14984                   ACTION_BREAKING);
14985
14986     // no element can dig solid indestructible elements
14987     if (IS_INDESTRUCTIBLE(element) &&
14988         !IS_DIGGABLE(element) &&
14989         !IS_COLLECTIBLE(element))
14990       return FALSE;
14991
14992     if (AmoebaNr[x][y] &&
14993         (element == EL_AMOEBA_FULL ||
14994          element == EL_BD_AMOEBA ||
14995          element == EL_AMOEBA_GROWING))
14996     {
14997       AmoebaCnt[AmoebaNr[x][y]]--;
14998       AmoebaCnt2[AmoebaNr[x][y]]--;
14999     }
15000
15001     if (IS_MOVING(x, y))
15002       RemoveMovingField(x, y);
15003     else
15004     {
15005       RemoveField(x, y);
15006       TEST_DrawLevelField(x, y);
15007     }
15008
15009     // if digged element was about to explode, prevent the explosion
15010     ExplodeField[x][y] = EX_TYPE_NONE;
15011
15012     PlayLevelSoundAction(x, y, action);
15013   }
15014
15015   Store[x][y] = EL_EMPTY;
15016
15017   // this makes it possible to leave the removed element again
15018   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15019     Store[x][y] = element;
15020
15021   return TRUE;
15022 }
15023
15024 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15025 {
15026   int jx = player->jx, jy = player->jy;
15027   int x = jx + dx, y = jy + dy;
15028   int snap_direction = (dx == -1 ? MV_LEFT  :
15029                         dx == +1 ? MV_RIGHT :
15030                         dy == -1 ? MV_UP    :
15031                         dy == +1 ? MV_DOWN  : MV_NONE);
15032   boolean can_continue_snapping = (level.continuous_snapping &&
15033                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15034
15035   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15036     return FALSE;
15037
15038   if (!player->active || !IN_LEV_FIELD(x, y))
15039     return FALSE;
15040
15041   if (dx && dy)
15042     return FALSE;
15043
15044   if (!dx && !dy)
15045   {
15046     if (player->MovPos == 0)
15047       player->is_pushing = FALSE;
15048
15049     player->is_snapping = FALSE;
15050
15051     if (player->MovPos == 0)
15052     {
15053       player->is_moving = FALSE;
15054       player->is_digging = FALSE;
15055       player->is_collecting = FALSE;
15056     }
15057
15058     return FALSE;
15059   }
15060
15061   // prevent snapping with already pressed snap key when not allowed
15062   if (player->is_snapping && !can_continue_snapping)
15063     return FALSE;
15064
15065   player->MovDir = snap_direction;
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   player->is_dropping = FALSE;
15075   player->is_dropping_pressed = FALSE;
15076   player->drop_pressed_delay = 0;
15077
15078   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15079     return FALSE;
15080
15081   player->is_snapping = TRUE;
15082   player->is_active = TRUE;
15083
15084   if (player->MovPos == 0)
15085   {
15086     player->is_moving = FALSE;
15087     player->is_digging = FALSE;
15088     player->is_collecting = FALSE;
15089   }
15090
15091   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15092     TEST_DrawLevelField(player->last_jx, player->last_jy);
15093
15094   TEST_DrawLevelField(x, y);
15095
15096   return TRUE;
15097 }
15098
15099 static boolean DropElement(struct PlayerInfo *player)
15100 {
15101   int old_element, new_element;
15102   int dropx = player->jx, dropy = player->jy;
15103   int drop_direction = player->MovDir;
15104   int drop_side = drop_direction;
15105   int drop_element = get_next_dropped_element(player);
15106
15107   /* do not drop an element on top of another element; when holding drop key
15108      pressed without moving, dropped element must move away before the next
15109      element can be dropped (this is especially important if the next element
15110      is dynamite, which can be placed on background for historical reasons) */
15111   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15112     return MP_ACTION;
15113
15114   if (IS_THROWABLE(drop_element))
15115   {
15116     dropx += GET_DX_FROM_DIR(drop_direction);
15117     dropy += GET_DY_FROM_DIR(drop_direction);
15118
15119     if (!IN_LEV_FIELD(dropx, dropy))
15120       return FALSE;
15121   }
15122
15123   old_element = Tile[dropx][dropy];     // old element at dropping position
15124   new_element = drop_element;           // default: no change when dropping
15125
15126   // check if player is active, not moving and ready to drop
15127   if (!player->active || player->MovPos || player->drop_delay > 0)
15128     return FALSE;
15129
15130   // check if player has anything that can be dropped
15131   if (new_element == EL_UNDEFINED)
15132     return FALSE;
15133
15134   // only set if player has anything that can be dropped
15135   player->is_dropping_pressed = TRUE;
15136
15137   // check if drop key was pressed long enough for EM style dynamite
15138   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15139     return FALSE;
15140
15141   // check if anything can be dropped at the current position
15142   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15143     return FALSE;
15144
15145   // collected custom elements can only be dropped on empty fields
15146   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15147     return FALSE;
15148
15149   if (old_element != EL_EMPTY)
15150     Back[dropx][dropy] = old_element;   // store old element on this field
15151
15152   ResetGfxAnimation(dropx, dropy);
15153   ResetRandomAnimationValue(dropx, dropy);
15154
15155   if (player->inventory_size > 0 ||
15156       player->inventory_infinite_element != EL_UNDEFINED)
15157   {
15158     if (player->inventory_size > 0)
15159     {
15160       player->inventory_size--;
15161
15162       DrawGameDoorValues();
15163
15164       if (new_element == EL_DYNAMITE)
15165         new_element = EL_DYNAMITE_ACTIVE;
15166       else if (new_element == EL_EM_DYNAMITE)
15167         new_element = EL_EM_DYNAMITE_ACTIVE;
15168       else if (new_element == EL_SP_DISK_RED)
15169         new_element = EL_SP_DISK_RED_ACTIVE;
15170     }
15171
15172     Tile[dropx][dropy] = new_element;
15173
15174     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15175       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15176                           el2img(Tile[dropx][dropy]), 0);
15177
15178     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15179
15180     // needed if previous element just changed to "empty" in the last frame
15181     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15182
15183     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15184                                player->index_bit, drop_side);
15185     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15186                                         CE_PLAYER_DROPS_X,
15187                                         player->index_bit, drop_side);
15188
15189     TestIfElementTouchesCustomElement(dropx, dropy);
15190   }
15191   else          // player is dropping a dyna bomb
15192   {
15193     player->dynabombs_left--;
15194
15195     Tile[dropx][dropy] = new_element;
15196
15197     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15198       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15199                           el2img(Tile[dropx][dropy]), 0);
15200
15201     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15202   }
15203
15204   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15205     InitField_WithBug1(dropx, dropy, FALSE);
15206
15207   new_element = Tile[dropx][dropy];     // element might have changed
15208
15209   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15210       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15211   {
15212     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15213       MovDir[dropx][dropy] = drop_direction;
15214
15215     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15216
15217     // do not cause impact style collision by dropping elements that can fall
15218     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15219   }
15220
15221   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15222   player->is_dropping = TRUE;
15223
15224   player->drop_pressed_delay = 0;
15225   player->is_dropping_pressed = FALSE;
15226
15227   player->drop_x = dropx;
15228   player->drop_y = dropy;
15229
15230   return TRUE;
15231 }
15232
15233 // ----------------------------------------------------------------------------
15234 // game sound playing functions
15235 // ----------------------------------------------------------------------------
15236
15237 static int *loop_sound_frame = NULL;
15238 static int *loop_sound_volume = NULL;
15239
15240 void InitPlayLevelSound(void)
15241 {
15242   int num_sounds = getSoundListSize();
15243
15244   checked_free(loop_sound_frame);
15245   checked_free(loop_sound_volume);
15246
15247   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15248   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15249 }
15250
15251 static void PlayLevelSound(int x, int y, int nr)
15252 {
15253   int sx = SCREENX(x), sy = SCREENY(y);
15254   int volume, stereo_position;
15255   int max_distance = 8;
15256   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15257
15258   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15259       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15260     return;
15261
15262   if (!IN_LEV_FIELD(x, y) ||
15263       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15264       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15265     return;
15266
15267   volume = SOUND_MAX_VOLUME;
15268
15269   if (!IN_SCR_FIELD(sx, sy))
15270   {
15271     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15272     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15273
15274     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15275   }
15276
15277   stereo_position = (SOUND_MAX_LEFT +
15278                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15279                      (SCR_FIELDX + 2 * max_distance));
15280
15281   if (IS_LOOP_SOUND(nr))
15282   {
15283     /* This assures that quieter loop sounds do not overwrite louder ones,
15284        while restarting sound volume comparison with each new game frame. */
15285
15286     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15287       return;
15288
15289     loop_sound_volume[nr] = volume;
15290     loop_sound_frame[nr] = FrameCounter;
15291   }
15292
15293   PlaySoundExt(nr, volume, stereo_position, type);
15294 }
15295
15296 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15297 {
15298   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15299                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15300                  y < LEVELY(BY1) ? LEVELY(BY1) :
15301                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15302                  sound_action);
15303 }
15304
15305 static void PlayLevelSoundAction(int x, int y, int action)
15306 {
15307   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15308 }
15309
15310 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15311 {
15312   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15313
15314   if (sound_effect != SND_UNDEFINED)
15315     PlayLevelSound(x, y, sound_effect);
15316 }
15317
15318 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15319                                               int action)
15320 {
15321   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15322
15323   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15324     PlayLevelSound(x, y, sound_effect);
15325 }
15326
15327 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15328 {
15329   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15330
15331   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15332     PlayLevelSound(x, y, sound_effect);
15333 }
15334
15335 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15336 {
15337   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15338
15339   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15340     StopSound(sound_effect);
15341 }
15342
15343 static int getLevelMusicNr(void)
15344 {
15345   if (levelset.music[level_nr] != MUS_UNDEFINED)
15346     return levelset.music[level_nr];            // from config file
15347   else
15348     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15349 }
15350
15351 static void FadeLevelSounds(void)
15352 {
15353   FadeSounds();
15354 }
15355
15356 static void FadeLevelMusic(void)
15357 {
15358   int music_nr = getLevelMusicNr();
15359   char *curr_music = getCurrentlyPlayingMusicFilename();
15360   char *next_music = getMusicInfoEntryFilename(music_nr);
15361
15362   if (!strEqual(curr_music, next_music))
15363     FadeMusic();
15364 }
15365
15366 void FadeLevelSoundsAndMusic(void)
15367 {
15368   FadeLevelSounds();
15369   FadeLevelMusic();
15370 }
15371
15372 static void PlayLevelMusic(void)
15373 {
15374   int music_nr = getLevelMusicNr();
15375   char *curr_music = getCurrentlyPlayingMusicFilename();
15376   char *next_music = getMusicInfoEntryFilename(music_nr);
15377
15378   if (!strEqual(curr_music, next_music))
15379     PlayMusicLoop(music_nr);
15380 }
15381
15382 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15383 {
15384   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15385   int offset = 0;
15386   int x = xx - offset;
15387   int y = yy - offset;
15388
15389   switch (sample)
15390   {
15391     case SOUND_blank:
15392       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15393       break;
15394
15395     case SOUND_roll:
15396       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15397       break;
15398
15399     case SOUND_stone:
15400       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15401       break;
15402
15403     case SOUND_nut:
15404       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15405       break;
15406
15407     case SOUND_crack:
15408       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15409       break;
15410
15411     case SOUND_bug:
15412       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15413       break;
15414
15415     case SOUND_tank:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15417       break;
15418
15419     case SOUND_android_clone:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15421       break;
15422
15423     case SOUND_android_move:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15425       break;
15426
15427     case SOUND_spring:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15429       break;
15430
15431     case SOUND_slurp:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15433       break;
15434
15435     case SOUND_eater:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15437       break;
15438
15439     case SOUND_eater_eat:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15441       break;
15442
15443     case SOUND_alien:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15445       break;
15446
15447     case SOUND_collect:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15449       break;
15450
15451     case SOUND_diamond:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15453       break;
15454
15455     case SOUND_squash:
15456       // !!! CHECK THIS !!!
15457 #if 1
15458       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15459 #else
15460       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15461 #endif
15462       break;
15463
15464     case SOUND_wonderfall:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15466       break;
15467
15468     case SOUND_drip:
15469       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15470       break;
15471
15472     case SOUND_push:
15473       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15474       break;
15475
15476     case SOUND_dirt:
15477       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15478       break;
15479
15480     case SOUND_acid:
15481       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15482       break;
15483
15484     case SOUND_ball:
15485       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15486       break;
15487
15488     case SOUND_slide:
15489       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15490       break;
15491
15492     case SOUND_wonder:
15493       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15494       break;
15495
15496     case SOUND_door:
15497       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15498       break;
15499
15500     case SOUND_exit_open:
15501       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15502       break;
15503
15504     case SOUND_exit_leave:
15505       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15506       break;
15507
15508     case SOUND_dynamite:
15509       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15510       break;
15511
15512     case SOUND_tick:
15513       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15514       break;
15515
15516     case SOUND_press:
15517       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15518       break;
15519
15520     case SOUND_wheel:
15521       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15522       break;
15523
15524     case SOUND_boom:
15525       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15526       break;
15527
15528     case SOUND_die:
15529       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15530       break;
15531
15532     case SOUND_time:
15533       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15534       break;
15535
15536     default:
15537       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15538       break;
15539   }
15540 }
15541
15542 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15543 {
15544   int element = map_element_SP_to_RND(element_sp);
15545   int action = map_action_SP_to_RND(action_sp);
15546   int offset = (setup.sp_show_border_elements ? 0 : 1);
15547   int x = xx - offset;
15548   int y = yy - offset;
15549
15550   PlayLevelSoundElementAction(x, y, element, action);
15551 }
15552
15553 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15554 {
15555   int element = map_element_MM_to_RND(element_mm);
15556   int action = map_action_MM_to_RND(action_mm);
15557   int offset = 0;
15558   int x = xx - offset;
15559   int y = yy - offset;
15560
15561   if (!IS_MM_ELEMENT(element))
15562     element = EL_MM_DEFAULT;
15563
15564   PlayLevelSoundElementAction(x, y, element, action);
15565 }
15566
15567 void PlaySound_MM(int sound_mm)
15568 {
15569   int sound = map_sound_MM_to_RND(sound_mm);
15570
15571   if (sound == SND_UNDEFINED)
15572     return;
15573
15574   PlaySound(sound);
15575 }
15576
15577 void PlaySoundLoop_MM(int sound_mm)
15578 {
15579   int sound = map_sound_MM_to_RND(sound_mm);
15580
15581   if (sound == SND_UNDEFINED)
15582     return;
15583
15584   PlaySoundLoop(sound);
15585 }
15586
15587 void StopSound_MM(int sound_mm)
15588 {
15589   int sound = map_sound_MM_to_RND(sound_mm);
15590
15591   if (sound == SND_UNDEFINED)
15592     return;
15593
15594   StopSound(sound);
15595 }
15596
15597 void RaiseScore(int value)
15598 {
15599   game.score += value;
15600
15601   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15602
15603   DisplayGameControlValues();
15604 }
15605
15606 void RaiseScoreElement(int element)
15607 {
15608   switch (element)
15609   {
15610     case EL_EMERALD:
15611     case EL_BD_DIAMOND:
15612     case EL_EMERALD_YELLOW:
15613     case EL_EMERALD_RED:
15614     case EL_EMERALD_PURPLE:
15615     case EL_SP_INFOTRON:
15616       RaiseScore(level.score[SC_EMERALD]);
15617       break;
15618     case EL_DIAMOND:
15619       RaiseScore(level.score[SC_DIAMOND]);
15620       break;
15621     case EL_CRYSTAL:
15622       RaiseScore(level.score[SC_CRYSTAL]);
15623       break;
15624     case EL_PEARL:
15625       RaiseScore(level.score[SC_PEARL]);
15626       break;
15627     case EL_BUG:
15628     case EL_BD_BUTTERFLY:
15629     case EL_SP_ELECTRON:
15630       RaiseScore(level.score[SC_BUG]);
15631       break;
15632     case EL_SPACESHIP:
15633     case EL_BD_FIREFLY:
15634     case EL_SP_SNIKSNAK:
15635       RaiseScore(level.score[SC_SPACESHIP]);
15636       break;
15637     case EL_YAMYAM:
15638     case EL_DARK_YAMYAM:
15639       RaiseScore(level.score[SC_YAMYAM]);
15640       break;
15641     case EL_ROBOT:
15642       RaiseScore(level.score[SC_ROBOT]);
15643       break;
15644     case EL_PACMAN:
15645       RaiseScore(level.score[SC_PACMAN]);
15646       break;
15647     case EL_NUT:
15648       RaiseScore(level.score[SC_NUT]);
15649       break;
15650     case EL_DYNAMITE:
15651     case EL_EM_DYNAMITE:
15652     case EL_SP_DISK_RED:
15653     case EL_DYNABOMB_INCREASE_NUMBER:
15654     case EL_DYNABOMB_INCREASE_SIZE:
15655     case EL_DYNABOMB_INCREASE_POWER:
15656       RaiseScore(level.score[SC_DYNAMITE]);
15657       break;
15658     case EL_SHIELD_NORMAL:
15659     case EL_SHIELD_DEADLY:
15660       RaiseScore(level.score[SC_SHIELD]);
15661       break;
15662     case EL_EXTRA_TIME:
15663       RaiseScore(level.extra_time_score);
15664       break;
15665     case EL_KEY_1:
15666     case EL_KEY_2:
15667     case EL_KEY_3:
15668     case EL_KEY_4:
15669     case EL_EM_KEY_1:
15670     case EL_EM_KEY_2:
15671     case EL_EM_KEY_3:
15672     case EL_EM_KEY_4:
15673     case EL_EMC_KEY_5:
15674     case EL_EMC_KEY_6:
15675     case EL_EMC_KEY_7:
15676     case EL_EMC_KEY_8:
15677     case EL_DC_KEY_WHITE:
15678       RaiseScore(level.score[SC_KEY]);
15679       break;
15680     default:
15681       RaiseScore(element_info[element].collect_score);
15682       break;
15683   }
15684 }
15685
15686 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15687 {
15688   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15689   {
15690     if (!quick_quit)
15691     {
15692       // prevent short reactivation of overlay buttons while closing door
15693       SetOverlayActive(FALSE);
15694
15695       // door may still be open due to skipped or envelope style request
15696       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15697     }
15698
15699     if (network.enabled)
15700       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15701     else
15702     {
15703       if (quick_quit)
15704         FadeSkipNextFadeIn();
15705
15706       SetGameStatus(GAME_MODE_MAIN);
15707
15708       DrawMainMenu();
15709     }
15710   }
15711   else          // continue playing the game
15712   {
15713     if (tape.playing && tape.deactivate_display)
15714       TapeDeactivateDisplayOff(TRUE);
15715
15716     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15717
15718     if (tape.playing && tape.deactivate_display)
15719       TapeDeactivateDisplayOn();
15720   }
15721 }
15722
15723 void RequestQuitGame(boolean escape_key_pressed)
15724 {
15725   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15726   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15727                         level_editor_test_game);
15728   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15729                           quick_quit || score_info_tape_play);
15730
15731   RequestQuitGameExt(skip_request, quick_quit,
15732                      "Do you really want to quit the game?");
15733 }
15734
15735 void RequestRestartGame(char *message)
15736 {
15737   game.restart_game_message = NULL;
15738
15739   boolean has_started_game = hasStartedNetworkGame();
15740   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15741
15742   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15743   {
15744     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15745   }
15746   else
15747   {
15748     // needed in case of envelope request to close game panel
15749     CloseDoor(DOOR_CLOSE_1);
15750
15751     SetGameStatus(GAME_MODE_MAIN);
15752
15753     DrawMainMenu();
15754   }
15755 }
15756
15757 void CheckGameOver(void)
15758 {
15759   static boolean last_game_over = FALSE;
15760   static int game_over_delay = 0;
15761   int game_over_delay_value = 50;
15762   boolean game_over = checkGameFailed();
15763
15764   // do not handle game over if request dialog is already active
15765   if (game.request_active)
15766     return;
15767
15768   // do not ask to play again if game was never actually played
15769   if (!game.GamePlayed)
15770     return;
15771
15772   if (!game_over)
15773   {
15774     last_game_over = FALSE;
15775     game_over_delay = game_over_delay_value;
15776
15777     return;
15778   }
15779
15780   if (game_over_delay > 0)
15781   {
15782     game_over_delay--;
15783
15784     return;
15785   }
15786
15787   if (last_game_over != game_over)
15788     game.restart_game_message = (hasStartedNetworkGame() ?
15789                                  "Game over! Play it again?" :
15790                                  "Game over!");
15791
15792   last_game_over = game_over;
15793 }
15794
15795 boolean checkGameSolved(void)
15796 {
15797   // set for all game engines if level was solved
15798   return game.LevelSolved_GameEnd;
15799 }
15800
15801 boolean checkGameFailed(void)
15802 {
15803   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15804     return (game_em.game_over && !game_em.level_solved);
15805   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15806     return (game_sp.game_over && !game_sp.level_solved);
15807   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15808     return (game_mm.game_over && !game_mm.level_solved);
15809   else                          // GAME_ENGINE_TYPE_RND
15810     return (game.GameOver && !game.LevelSolved);
15811 }
15812
15813 boolean checkGameEnded(void)
15814 {
15815   return (checkGameSolved() || checkGameFailed());
15816 }
15817
15818
15819 // ----------------------------------------------------------------------------
15820 // random generator functions
15821 // ----------------------------------------------------------------------------
15822
15823 unsigned int InitEngineRandom_RND(int seed)
15824 {
15825   game.num_random_calls = 0;
15826
15827   return InitEngineRandom(seed);
15828 }
15829
15830 unsigned int RND(int max)
15831 {
15832   if (max > 0)
15833   {
15834     game.num_random_calls++;
15835
15836     return GetEngineRandom(max);
15837   }
15838
15839   return 0;
15840 }
15841
15842
15843 // ----------------------------------------------------------------------------
15844 // game engine snapshot handling functions
15845 // ----------------------------------------------------------------------------
15846
15847 struct EngineSnapshotInfo
15848 {
15849   // runtime values for custom element collect score
15850   int collect_score[NUM_CUSTOM_ELEMENTS];
15851
15852   // runtime values for group element choice position
15853   int choice_pos[NUM_GROUP_ELEMENTS];
15854
15855   // runtime values for belt position animations
15856   int belt_graphic[4][NUM_BELT_PARTS];
15857   int belt_anim_mode[4][NUM_BELT_PARTS];
15858 };
15859
15860 static struct EngineSnapshotInfo engine_snapshot_rnd;
15861 static char *snapshot_level_identifier = NULL;
15862 static int snapshot_level_nr = -1;
15863
15864 static void SaveEngineSnapshotValues_RND(void)
15865 {
15866   static int belt_base_active_element[4] =
15867   {
15868     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15869     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15870     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15871     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15872   };
15873   int i, j;
15874
15875   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15876   {
15877     int element = EL_CUSTOM_START + i;
15878
15879     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15880   }
15881
15882   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15883   {
15884     int element = EL_GROUP_START + i;
15885
15886     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15887   }
15888
15889   for (i = 0; i < 4; i++)
15890   {
15891     for (j = 0; j < NUM_BELT_PARTS; j++)
15892     {
15893       int element = belt_base_active_element[i] + j;
15894       int graphic = el2img(element);
15895       int anim_mode = graphic_info[graphic].anim_mode;
15896
15897       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15898       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15899     }
15900   }
15901 }
15902
15903 static void LoadEngineSnapshotValues_RND(void)
15904 {
15905   unsigned int num_random_calls = game.num_random_calls;
15906   int i, j;
15907
15908   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15909   {
15910     int element = EL_CUSTOM_START + i;
15911
15912     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15913   }
15914
15915   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15916   {
15917     int element = EL_GROUP_START + i;
15918
15919     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15920   }
15921
15922   for (i = 0; i < 4; i++)
15923   {
15924     for (j = 0; j < NUM_BELT_PARTS; j++)
15925     {
15926       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15927       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15928
15929       graphic_info[graphic].anim_mode = anim_mode;
15930     }
15931   }
15932
15933   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15934   {
15935     InitRND(tape.random_seed);
15936     for (i = 0; i < num_random_calls; i++)
15937       RND(1);
15938   }
15939
15940   if (game.num_random_calls != num_random_calls)
15941   {
15942     Error("number of random calls out of sync");
15943     Error("number of random calls should be %d", num_random_calls);
15944     Error("number of random calls is %d", game.num_random_calls);
15945
15946     Fail("this should not happen -- please debug");
15947   }
15948 }
15949
15950 void FreeEngineSnapshotSingle(void)
15951 {
15952   FreeSnapshotSingle();
15953
15954   setString(&snapshot_level_identifier, NULL);
15955   snapshot_level_nr = -1;
15956 }
15957
15958 void FreeEngineSnapshotList(void)
15959 {
15960   FreeSnapshotList();
15961 }
15962
15963 static ListNode *SaveEngineSnapshotBuffers(void)
15964 {
15965   ListNode *buffers = NULL;
15966
15967   // copy some special values to a structure better suited for the snapshot
15968
15969   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15970     SaveEngineSnapshotValues_RND();
15971   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15972     SaveEngineSnapshotValues_EM();
15973   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15974     SaveEngineSnapshotValues_SP(&buffers);
15975   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15976     SaveEngineSnapshotValues_MM(&buffers);
15977
15978   // save values stored in special snapshot structure
15979
15980   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15981     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15982   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15983     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15984   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15985     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15986   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15987     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15988
15989   // save further RND engine values
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15994
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16004
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16006
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16009
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16028
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16031
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16035
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16038
16039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16044   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16045
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16048
16049 #if 0
16050   ListNode *node = engine_snapshot_list_rnd;
16051   int num_bytes = 0;
16052
16053   while (node != NULL)
16054   {
16055     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16056
16057     node = node->next;
16058   }
16059
16060   Debug("game:playing:SaveEngineSnapshotBuffers",
16061         "size of engine snapshot: %d bytes", num_bytes);
16062 #endif
16063
16064   return buffers;
16065 }
16066
16067 void SaveEngineSnapshotSingle(void)
16068 {
16069   ListNode *buffers = SaveEngineSnapshotBuffers();
16070
16071   // finally save all snapshot buffers to single snapshot
16072   SaveSnapshotSingle(buffers);
16073
16074   // save level identification information
16075   setString(&snapshot_level_identifier, leveldir_current->identifier);
16076   snapshot_level_nr = level_nr;
16077 }
16078
16079 boolean CheckSaveEngineSnapshotToList(void)
16080 {
16081   boolean save_snapshot =
16082     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16083      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16084       game.snapshot.changed_action) ||
16085      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16086       game.snapshot.collected_item));
16087
16088   game.snapshot.changed_action = FALSE;
16089   game.snapshot.collected_item = FALSE;
16090   game.snapshot.save_snapshot = save_snapshot;
16091
16092   return save_snapshot;
16093 }
16094
16095 void SaveEngineSnapshotToList(void)
16096 {
16097   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16098       tape.quick_resume)
16099     return;
16100
16101   ListNode *buffers = SaveEngineSnapshotBuffers();
16102
16103   // finally save all snapshot buffers to snapshot list
16104   SaveSnapshotToList(buffers);
16105 }
16106
16107 void SaveEngineSnapshotToListInitial(void)
16108 {
16109   FreeEngineSnapshotList();
16110
16111   SaveEngineSnapshotToList();
16112 }
16113
16114 static void LoadEngineSnapshotValues(void)
16115 {
16116   // restore special values from snapshot structure
16117
16118   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16119     LoadEngineSnapshotValues_RND();
16120   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16121     LoadEngineSnapshotValues_EM();
16122   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16123     LoadEngineSnapshotValues_SP();
16124   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16125     LoadEngineSnapshotValues_MM();
16126 }
16127
16128 void LoadEngineSnapshotSingle(void)
16129 {
16130   LoadSnapshotSingle();
16131
16132   LoadEngineSnapshotValues();
16133 }
16134
16135 static void LoadEngineSnapshot_Undo(int steps)
16136 {
16137   LoadSnapshotFromList_Older(steps);
16138
16139   LoadEngineSnapshotValues();
16140 }
16141
16142 static void LoadEngineSnapshot_Redo(int steps)
16143 {
16144   LoadSnapshotFromList_Newer(steps);
16145
16146   LoadEngineSnapshotValues();
16147 }
16148
16149 boolean CheckEngineSnapshotSingle(void)
16150 {
16151   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16152           snapshot_level_nr == level_nr);
16153 }
16154
16155 boolean CheckEngineSnapshotList(void)
16156 {
16157   return CheckSnapshotList();
16158 }
16159
16160
16161 // ---------- new game button stuff -------------------------------------------
16162
16163 static struct
16164 {
16165   int graphic;
16166   struct XY *pos;
16167   int gadget_id;
16168   boolean *setup_value;
16169   boolean allowed_on_tape;
16170   boolean is_touch_button;
16171   char *infotext;
16172 } gamebutton_info[NUM_GAME_BUTTONS] =
16173 {
16174   {
16175     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16176     GAME_CTRL_ID_STOP,                          NULL,
16177     TRUE, FALSE,                                "stop game"
16178   },
16179   {
16180     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16181     GAME_CTRL_ID_PAUSE,                         NULL,
16182     TRUE, FALSE,                                "pause game"
16183   },
16184   {
16185     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16186     GAME_CTRL_ID_PLAY,                          NULL,
16187     TRUE, FALSE,                                "play game"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16191     GAME_CTRL_ID_UNDO,                          NULL,
16192     TRUE, FALSE,                                "undo step"
16193   },
16194   {
16195     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16196     GAME_CTRL_ID_REDO,                          NULL,
16197     TRUE, FALSE,                                "redo step"
16198   },
16199   {
16200     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16201     GAME_CTRL_ID_SAVE,                          NULL,
16202     TRUE, FALSE,                                "save game"
16203   },
16204   {
16205     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16206     GAME_CTRL_ID_PAUSE2,                        NULL,
16207     TRUE, FALSE,                                "pause game"
16208   },
16209   {
16210     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16211     GAME_CTRL_ID_LOAD,                          NULL,
16212     TRUE, FALSE,                                "load game"
16213   },
16214   {
16215     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16216     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16217     FALSE, FALSE,                               "stop game"
16218   },
16219   {
16220     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16221     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16222     FALSE, FALSE,                               "pause game"
16223   },
16224   {
16225     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16226     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16227     FALSE, FALSE,                               "play game"
16228   },
16229   {
16230     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16231     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16232     FALSE, TRUE,                                "stop game"
16233   },
16234   {
16235     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16236     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16237     FALSE, TRUE,                                "pause game"
16238   },
16239   {
16240     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16241     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16242     TRUE, FALSE,                                "background music on/off"
16243   },
16244   {
16245     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16246     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16247     TRUE, FALSE,                                "sound loops on/off"
16248   },
16249   {
16250     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16251     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16252     TRUE, FALSE,                                "normal sounds on/off"
16253   },
16254   {
16255     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16256     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16257     FALSE, FALSE,                               "background music on/off"
16258   },
16259   {
16260     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16261     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16262     FALSE, FALSE,                               "sound loops on/off"
16263   },
16264   {
16265     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16266     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16267     FALSE, FALSE,                               "normal sounds on/off"
16268   }
16269 };
16270
16271 void CreateGameButtons(void)
16272 {
16273   int i;
16274
16275   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16276   {
16277     int graphic = gamebutton_info[i].graphic;
16278     struct GraphicInfo *gfx = &graphic_info[graphic];
16279     struct XY *pos = gamebutton_info[i].pos;
16280     struct GadgetInfo *gi;
16281     int button_type;
16282     boolean checked;
16283     unsigned int event_mask;
16284     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16285     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16286     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16287     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16288     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16289     int gd_x   = gfx->src_x;
16290     int gd_y   = gfx->src_y;
16291     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16292     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16293     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16294     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16295     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16296     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16297     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16298     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16299     int id = i;
16300
16301     if (gfx->bitmap == NULL)
16302     {
16303       game_gadget[id] = NULL;
16304
16305       continue;
16306     }
16307
16308     if (id == GAME_CTRL_ID_STOP ||
16309         id == GAME_CTRL_ID_PANEL_STOP ||
16310         id == GAME_CTRL_ID_TOUCH_STOP ||
16311         id == GAME_CTRL_ID_PLAY ||
16312         id == GAME_CTRL_ID_PANEL_PLAY ||
16313         id == GAME_CTRL_ID_SAVE ||
16314         id == GAME_CTRL_ID_LOAD)
16315     {
16316       button_type = GD_TYPE_NORMAL_BUTTON;
16317       checked = FALSE;
16318       event_mask = GD_EVENT_RELEASED;
16319     }
16320     else if (id == GAME_CTRL_ID_UNDO ||
16321              id == GAME_CTRL_ID_REDO)
16322     {
16323       button_type = GD_TYPE_NORMAL_BUTTON;
16324       checked = FALSE;
16325       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16326     }
16327     else
16328     {
16329       button_type = GD_TYPE_CHECK_BUTTON;
16330       checked = (gamebutton_info[i].setup_value != NULL ?
16331                  *gamebutton_info[i].setup_value : FALSE);
16332       event_mask = GD_EVENT_PRESSED;
16333     }
16334
16335     gi = CreateGadget(GDI_CUSTOM_ID, id,
16336                       GDI_IMAGE_ID, graphic,
16337                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16338                       GDI_X, base_x + x,
16339                       GDI_Y, base_y + y,
16340                       GDI_WIDTH, gfx->width,
16341                       GDI_HEIGHT, gfx->height,
16342                       GDI_TYPE, button_type,
16343                       GDI_STATE, GD_BUTTON_UNPRESSED,
16344                       GDI_CHECKED, checked,
16345                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16346                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16347                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16348                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16349                       GDI_DIRECT_DRAW, FALSE,
16350                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16351                       GDI_EVENT_MASK, event_mask,
16352                       GDI_CALLBACK_ACTION, HandleGameButtons,
16353                       GDI_END);
16354
16355     if (gi == NULL)
16356       Fail("cannot create gadget");
16357
16358     game_gadget[id] = gi;
16359   }
16360 }
16361
16362 void FreeGameButtons(void)
16363 {
16364   int i;
16365
16366   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16367     FreeGadget(game_gadget[i]);
16368 }
16369
16370 static void UnmapGameButtonsAtSamePosition(int id)
16371 {
16372   int i;
16373
16374   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16375     if (i != id &&
16376         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16377         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16378       UnmapGadget(game_gadget[i]);
16379 }
16380
16381 static void UnmapGameButtonsAtSamePosition_All(void)
16382 {
16383   if (setup.show_load_save_buttons)
16384   {
16385     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16386     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16387     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16388   }
16389   else if (setup.show_undo_redo_buttons)
16390   {
16391     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16392     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16393     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16394   }
16395   else
16396   {
16397     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16398     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16399     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16400
16401     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16402     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16403     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16404   }
16405 }
16406
16407 void MapLoadSaveButtons(void)
16408 {
16409   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16410   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16411
16412   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16413   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16414 }
16415
16416 void MapUndoRedoButtons(void)
16417 {
16418   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16419   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16420
16421   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16422   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16423 }
16424
16425 void ModifyPauseButtons(void)
16426 {
16427   static int ids[] =
16428   {
16429     GAME_CTRL_ID_PAUSE,
16430     GAME_CTRL_ID_PAUSE2,
16431     GAME_CTRL_ID_PANEL_PAUSE,
16432     GAME_CTRL_ID_TOUCH_PAUSE,
16433     -1
16434   };
16435   int i;
16436
16437   for (i = 0; ids[i] > -1; i++)
16438     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16439 }
16440
16441 static void MapGameButtonsExt(boolean on_tape)
16442 {
16443   int i;
16444
16445   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16446   {
16447     if ((i == GAME_CTRL_ID_UNDO ||
16448          i == GAME_CTRL_ID_REDO) &&
16449         game_status != GAME_MODE_PLAYING)
16450       continue;
16451
16452     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16453       MapGadget(game_gadget[i]);
16454   }
16455
16456   UnmapGameButtonsAtSamePosition_All();
16457
16458   RedrawGameButtons();
16459 }
16460
16461 static void UnmapGameButtonsExt(boolean on_tape)
16462 {
16463   int i;
16464
16465   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16466     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16467       UnmapGadget(game_gadget[i]);
16468 }
16469
16470 static void RedrawGameButtonsExt(boolean on_tape)
16471 {
16472   int i;
16473
16474   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16475     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16476       RedrawGadget(game_gadget[i]);
16477 }
16478
16479 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16480 {
16481   if (gi == NULL)
16482     return;
16483
16484   gi->checked = state;
16485 }
16486
16487 static void RedrawSoundButtonGadget(int id)
16488 {
16489   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16490              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16491              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16492              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16493              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16494              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16495              id);
16496
16497   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16498   RedrawGadget(game_gadget[id2]);
16499 }
16500
16501 void MapGameButtons(void)
16502 {
16503   MapGameButtonsExt(FALSE);
16504 }
16505
16506 void UnmapGameButtons(void)
16507 {
16508   UnmapGameButtonsExt(FALSE);
16509 }
16510
16511 void RedrawGameButtons(void)
16512 {
16513   RedrawGameButtonsExt(FALSE);
16514 }
16515
16516 void MapGameButtonsOnTape(void)
16517 {
16518   MapGameButtonsExt(TRUE);
16519 }
16520
16521 void UnmapGameButtonsOnTape(void)
16522 {
16523   UnmapGameButtonsExt(TRUE);
16524 }
16525
16526 void RedrawGameButtonsOnTape(void)
16527 {
16528   RedrawGameButtonsExt(TRUE);
16529 }
16530
16531 static void GameUndoRedoExt(void)
16532 {
16533   ClearPlayerAction();
16534
16535   tape.pausing = TRUE;
16536
16537   RedrawPlayfield();
16538   UpdateAndDisplayGameControlValues();
16539
16540   DrawCompleteVideoDisplay();
16541   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16542   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16543   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16544
16545   ModifyPauseButtons();
16546
16547   BackToFront();
16548 }
16549
16550 static void GameUndo(int steps)
16551 {
16552   if (!CheckEngineSnapshotList())
16553     return;
16554
16555   int tape_property_bits = tape.property_bits;
16556
16557   LoadEngineSnapshot_Undo(steps);
16558
16559   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16560
16561   GameUndoRedoExt();
16562 }
16563
16564 static void GameRedo(int steps)
16565 {
16566   if (!CheckEngineSnapshotList())
16567     return;
16568
16569   int tape_property_bits = tape.property_bits;
16570
16571   LoadEngineSnapshot_Redo(steps);
16572
16573   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16574
16575   GameUndoRedoExt();
16576 }
16577
16578 static void HandleGameButtonsExt(int id, int button)
16579 {
16580   static boolean game_undo_executed = FALSE;
16581   int steps = BUTTON_STEPSIZE(button);
16582   boolean handle_game_buttons =
16583     (game_status == GAME_MODE_PLAYING ||
16584      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16585
16586   if (!handle_game_buttons)
16587     return;
16588
16589   switch (id)
16590   {
16591     case GAME_CTRL_ID_STOP:
16592     case GAME_CTRL_ID_PANEL_STOP:
16593     case GAME_CTRL_ID_TOUCH_STOP:
16594       TapeStopGame();
16595
16596       break;
16597
16598     case GAME_CTRL_ID_PAUSE:
16599     case GAME_CTRL_ID_PAUSE2:
16600     case GAME_CTRL_ID_PANEL_PAUSE:
16601     case GAME_CTRL_ID_TOUCH_PAUSE:
16602       if (network.enabled && game_status == GAME_MODE_PLAYING)
16603       {
16604         if (tape.pausing)
16605           SendToServer_ContinuePlaying();
16606         else
16607           SendToServer_PausePlaying();
16608       }
16609       else
16610         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16611
16612       game_undo_executed = FALSE;
16613
16614       break;
16615
16616     case GAME_CTRL_ID_PLAY:
16617     case GAME_CTRL_ID_PANEL_PLAY:
16618       if (game_status == GAME_MODE_MAIN)
16619       {
16620         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16621       }
16622       else if (tape.pausing)
16623       {
16624         if (network.enabled)
16625           SendToServer_ContinuePlaying();
16626         else
16627           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16628       }
16629       break;
16630
16631     case GAME_CTRL_ID_UNDO:
16632       // Important: When using "save snapshot when collecting an item" mode,
16633       // load last (current) snapshot for first "undo" after pressing "pause"
16634       // (else the last-but-one snapshot would be loaded, because the snapshot
16635       // pointer already points to the last snapshot when pressing "pause",
16636       // which is fine for "every step/move" mode, but not for "every collect")
16637       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16638           !game_undo_executed)
16639         steps--;
16640
16641       game_undo_executed = TRUE;
16642
16643       GameUndo(steps);
16644       break;
16645
16646     case GAME_CTRL_ID_REDO:
16647       GameRedo(steps);
16648       break;
16649
16650     case GAME_CTRL_ID_SAVE:
16651       TapeQuickSave();
16652       break;
16653
16654     case GAME_CTRL_ID_LOAD:
16655       TapeQuickLoad();
16656       break;
16657
16658     case SOUND_CTRL_ID_MUSIC:
16659     case SOUND_CTRL_ID_PANEL_MUSIC:
16660       if (setup.sound_music)
16661       { 
16662         setup.sound_music = FALSE;
16663
16664         FadeMusic();
16665       }
16666       else if (audio.music_available)
16667       { 
16668         setup.sound = setup.sound_music = TRUE;
16669
16670         SetAudioMode(setup.sound);
16671
16672         if (game_status == GAME_MODE_PLAYING)
16673           PlayLevelMusic();
16674       }
16675
16676       RedrawSoundButtonGadget(id);
16677
16678       break;
16679
16680     case SOUND_CTRL_ID_LOOPS:
16681     case SOUND_CTRL_ID_PANEL_LOOPS:
16682       if (setup.sound_loops)
16683         setup.sound_loops = FALSE;
16684       else if (audio.loops_available)
16685       {
16686         setup.sound = setup.sound_loops = TRUE;
16687
16688         SetAudioMode(setup.sound);
16689       }
16690
16691       RedrawSoundButtonGadget(id);
16692
16693       break;
16694
16695     case SOUND_CTRL_ID_SIMPLE:
16696     case SOUND_CTRL_ID_PANEL_SIMPLE:
16697       if (setup.sound_simple)
16698         setup.sound_simple = FALSE;
16699       else if (audio.sound_available)
16700       {
16701         setup.sound = setup.sound_simple = TRUE;
16702
16703         SetAudioMode(setup.sound);
16704       }
16705
16706       RedrawSoundButtonGadget(id);
16707
16708       break;
16709
16710     default:
16711       break;
16712   }
16713 }
16714
16715 static void HandleGameButtons(struct GadgetInfo *gi)
16716 {
16717   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16718 }
16719
16720 void HandleSoundButtonKeys(Key key)
16721 {
16722   if (key == setup.shortcut.sound_simple)
16723     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16724   else if (key == setup.shortcut.sound_loops)
16725     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16726   else if (key == setup.shortcut.sound_music)
16727     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16728 }