renamed function
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 int NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963   int highlight_position;
4964
4965   game.LevelSolved_GameEnd = TRUE;
4966
4967   if (game.LevelSolved_SaveTape)
4968   {
4969     // make sure that request dialog to save tape does not open door again
4970     if (!global.use_envelope_request)
4971       CloseDoor(DOOR_CLOSE_1);
4972
4973     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4974
4975     // set unique basename for score tape (also saved in high score table)
4976     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4977   }
4978
4979   // if no tape is to be saved, close both doors simultaneously
4980   CloseDoor(DOOR_CLOSE_ALL);
4981
4982   if (level_editor_test_game)
4983   {
4984     SetGameStatus(GAME_MODE_MAIN);
4985
4986     DrawMainMenu();
4987
4988     return;
4989   }
4990
4991   if (!game.LevelSolved_SaveScore)
4992   {
4993     SetGameStatus(GAME_MODE_MAIN);
4994
4995     DrawMainMenu();
4996
4997     return;
4998   }
4999
5000   if (level_nr == leveldir_current->handicap_level)
5001   {
5002     leveldir_current->handicap_level++;
5003
5004     SaveLevelSetup_SeriesInfo();
5005   }
5006
5007   if (setup.increment_levels &&
5008       level_nr < leveldir_current->last_level &&
5009       !network_playing)
5010   {
5011     level_nr++;         // advance to next level
5012     TapeErase();        // start with empty tape
5013
5014     if (setup.auto_play_next_level)
5015     {
5016       LoadLevel(level_nr);
5017
5018       SaveLevelSetup_SeriesInfo();
5019     }
5020   }
5021
5022   highlight_position = NewHighScore(last_level_nr);
5023
5024   if (highlight_position >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr, highlight_position);
5029   }
5030   else if (setup.auto_play_next_level && setup.increment_levels &&
5031            last_level_nr < leveldir_current->last_level &&
5032            !network_playing)
5033   {
5034     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5035   }
5036   else
5037   {
5038     SetGameStatus(GAME_MODE_MAIN);
5039
5040     DrawMainMenu();
5041   }
5042 }
5043
5044 int NewHighScore(int level_nr)
5045 {
5046   int i, l;
5047   int position = -1;
5048   boolean one_score_entry_per_name = !program.many_scores_per_name;
5049
5050   LoadScore(level_nr);
5051
5052   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5053       game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
5054     return -1;
5055
5056   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5057   {
5058     struct ScoreEntry *entry = &scores.entry[i];
5059     boolean score_is_better = (game.score_final      >  entry->score);
5060     boolean score_is_equal  = (game.score_final      == entry->score);
5061     boolean time_is_better  = (game.score_time_final <  entry->time);
5062     boolean time_is_equal   = (game.score_time_final == entry->time);
5063     boolean better_by_score = (score_is_better ||
5064                                (score_is_equal && time_is_better));
5065     boolean better_by_time  = (time_is_better ||
5066                                (time_is_equal && score_is_better));
5067     boolean is_better = (level.rate_time_over_score ? better_by_time :
5068                          better_by_score);
5069     boolean entry_is_empty = (entry->score == 0 &&
5070                               entry->time == 0);
5071
5072     if (is_better || entry_is_empty)
5073     {
5074       // player has made it to the hall of fame
5075
5076       if (i < MAX_SCORE_ENTRIES - 1)
5077       {
5078         int m = MAX_SCORE_ENTRIES - 1;
5079
5080         if (one_score_entry_per_name)
5081         {
5082           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5083             if (strEqual(setup.player_name, scores.entry[l].name))
5084               m = l;
5085
5086           if (m == i)   // player's new highscore overwrites his old one
5087             goto put_into_list;
5088         }
5089
5090         for (l = m; l > i; l--)
5091         {
5092           strcpy(scores.entry[l].name, scores.entry[l - 1].name);
5093           scores.entry[l].score = scores.entry[l - 1].score;
5094           scores.entry[l].time  = scores.entry[l - 1].time;
5095         }
5096       }
5097
5098       put_into_list:
5099
5100       strcpy(entry->tape_basename, tape.score_tape_basename);
5101       strncpy(entry->name, setup.player_name, MAX_PLAYER_NAME_LEN);
5102       entry->name[MAX_PLAYER_NAME_LEN] = '\0';
5103       entry->score = game.score_final;
5104       entry->time = game.score_time_final;
5105       position = i;
5106
5107       break;
5108     }
5109     else if (one_score_entry_per_name &&
5110              !strncmp(setup.player_name, entry->name, MAX_PLAYER_NAME_LEN))
5111       break;    // player already there with a higher score
5112   }
5113
5114   if (position >= 0)
5115   {
5116     SaveScoreTape(level_nr);
5117     SaveScore(level_nr);
5118   }
5119
5120   return position;
5121 }
5122
5123 static int getElementMoveStepsizeExt(int x, int y, int direction)
5124 {
5125   int element = Tile[x][y];
5126   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5127   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5128   int horiz_move = (dx != 0);
5129   int sign = (horiz_move ? dx : dy);
5130   int step = sign * element_info[element].move_stepsize;
5131
5132   // special values for move stepsize for spring and things on conveyor belt
5133   if (horiz_move)
5134   {
5135     if (CAN_FALL(element) &&
5136         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5137       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5138     else if (element == EL_SPRING)
5139       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5140   }
5141
5142   return step;
5143 }
5144
5145 static int getElementMoveStepsize(int x, int y)
5146 {
5147   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5148 }
5149
5150 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5151 {
5152   if (player->GfxAction != action || player->GfxDir != dir)
5153   {
5154     player->GfxAction = action;
5155     player->GfxDir = dir;
5156     player->Frame = 0;
5157     player->StepFrame = 0;
5158   }
5159 }
5160
5161 static void ResetGfxFrame(int x, int y)
5162 {
5163   // profiling showed that "autotest" spends 10~20% of its time in this function
5164   if (DrawingDeactivatedField())
5165     return;
5166
5167   int element = Tile[x][y];
5168   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5169
5170   if (graphic_info[graphic].anim_global_sync)
5171     GfxFrame[x][y] = FrameCounter;
5172   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5173     GfxFrame[x][y] = CustomValue[x][y];
5174   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5175     GfxFrame[x][y] = element_info[element].collect_score;
5176   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5177     GfxFrame[x][y] = ChangeDelay[x][y];
5178 }
5179
5180 static void ResetGfxAnimation(int x, int y)
5181 {
5182   GfxAction[x][y] = ACTION_DEFAULT;
5183   GfxDir[x][y] = MovDir[x][y];
5184   GfxFrame[x][y] = 0;
5185
5186   ResetGfxFrame(x, y);
5187 }
5188
5189 static void ResetRandomAnimationValue(int x, int y)
5190 {
5191   GfxRandom[x][y] = INIT_GFX_RANDOM();
5192 }
5193
5194 static void InitMovingField(int x, int y, int direction)
5195 {
5196   int element = Tile[x][y];
5197   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5198   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5199   int newx = x + dx;
5200   int newy = y + dy;
5201   boolean is_moving_before, is_moving_after;
5202
5203   // check if element was/is moving or being moved before/after mode change
5204   is_moving_before = (WasJustMoving[x][y] != 0);
5205   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5206
5207   // reset animation only for moving elements which change direction of moving
5208   // or which just started or stopped moving
5209   // (else CEs with property "can move" / "not moving" are reset each frame)
5210   if (is_moving_before != is_moving_after ||
5211       direction != MovDir[x][y])
5212     ResetGfxAnimation(x, y);
5213
5214   MovDir[x][y] = direction;
5215   GfxDir[x][y] = direction;
5216
5217   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5218                      direction == MV_DOWN && CAN_FALL(element) ?
5219                      ACTION_FALLING : ACTION_MOVING);
5220
5221   // this is needed for CEs with property "can move" / "not moving"
5222
5223   if (is_moving_after)
5224   {
5225     if (Tile[newx][newy] == EL_EMPTY)
5226       Tile[newx][newy] = EL_BLOCKED;
5227
5228     MovDir[newx][newy] = MovDir[x][y];
5229
5230     CustomValue[newx][newy] = CustomValue[x][y];
5231
5232     GfxFrame[newx][newy] = GfxFrame[x][y];
5233     GfxRandom[newx][newy] = GfxRandom[x][y];
5234     GfxAction[newx][newy] = GfxAction[x][y];
5235     GfxDir[newx][newy] = GfxDir[x][y];
5236   }
5237 }
5238
5239 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5240 {
5241   int direction = MovDir[x][y];
5242   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5243   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5244
5245   *goes_to_x = newx;
5246   *goes_to_y = newy;
5247 }
5248
5249 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5250 {
5251   int oldx = x, oldy = y;
5252   int direction = MovDir[x][y];
5253
5254   if (direction == MV_LEFT)
5255     oldx++;
5256   else if (direction == MV_RIGHT)
5257     oldx--;
5258   else if (direction == MV_UP)
5259     oldy++;
5260   else if (direction == MV_DOWN)
5261     oldy--;
5262
5263   *comes_from_x = oldx;
5264   *comes_from_y = oldy;
5265 }
5266
5267 static int MovingOrBlocked2Element(int x, int y)
5268 {
5269   int element = Tile[x][y];
5270
5271   if (element == EL_BLOCKED)
5272   {
5273     int oldx, oldy;
5274
5275     Blocked2Moving(x, y, &oldx, &oldy);
5276     return Tile[oldx][oldy];
5277   }
5278   else
5279     return element;
5280 }
5281
5282 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5283 {
5284   // like MovingOrBlocked2Element(), but if element is moving
5285   // and (x,y) is the field the moving element is just leaving,
5286   // return EL_BLOCKED instead of the element value
5287   int element = Tile[x][y];
5288
5289   if (IS_MOVING(x, y))
5290   {
5291     if (element == EL_BLOCKED)
5292     {
5293       int oldx, oldy;
5294
5295       Blocked2Moving(x, y, &oldx, &oldy);
5296       return Tile[oldx][oldy];
5297     }
5298     else
5299       return EL_BLOCKED;
5300   }
5301   else
5302     return element;
5303 }
5304
5305 static void RemoveField(int x, int y)
5306 {
5307   Tile[x][y] = EL_EMPTY;
5308
5309   MovPos[x][y] = 0;
5310   MovDir[x][y] = 0;
5311   MovDelay[x][y] = 0;
5312
5313   CustomValue[x][y] = 0;
5314
5315   AmoebaNr[x][y] = 0;
5316   ChangeDelay[x][y] = 0;
5317   ChangePage[x][y] = -1;
5318   Pushed[x][y] = FALSE;
5319
5320   GfxElement[x][y] = EL_UNDEFINED;
5321   GfxAction[x][y] = ACTION_DEFAULT;
5322   GfxDir[x][y] = MV_NONE;
5323 }
5324
5325 static void RemoveMovingField(int x, int y)
5326 {
5327   int oldx = x, oldy = y, newx = x, newy = y;
5328   int element = Tile[x][y];
5329   int next_element = EL_UNDEFINED;
5330
5331   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5332     return;
5333
5334   if (IS_MOVING(x, y))
5335   {
5336     Moving2Blocked(x, y, &newx, &newy);
5337
5338     if (Tile[newx][newy] != EL_BLOCKED)
5339     {
5340       // element is moving, but target field is not free (blocked), but
5341       // already occupied by something different (example: acid pool);
5342       // in this case, only remove the moving field, but not the target
5343
5344       RemoveField(oldx, oldy);
5345
5346       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5347
5348       TEST_DrawLevelField(oldx, oldy);
5349
5350       return;
5351     }
5352   }
5353   else if (element == EL_BLOCKED)
5354   {
5355     Blocked2Moving(x, y, &oldx, &oldy);
5356     if (!IS_MOVING(oldx, oldy))
5357       return;
5358   }
5359
5360   if (element == EL_BLOCKED &&
5361       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5362        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5363        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5364        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5365        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5366        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5367     next_element = get_next_element(Tile[oldx][oldy]);
5368
5369   RemoveField(oldx, oldy);
5370   RemoveField(newx, newy);
5371
5372   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5373
5374   if (next_element != EL_UNDEFINED)
5375     Tile[oldx][oldy] = next_element;
5376
5377   TEST_DrawLevelField(oldx, oldy);
5378   TEST_DrawLevelField(newx, newy);
5379 }
5380
5381 void DrawDynamite(int x, int y)
5382 {
5383   int sx = SCREENX(x), sy = SCREENY(y);
5384   int graphic = el2img(Tile[x][y]);
5385   int frame;
5386
5387   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5388     return;
5389
5390   if (IS_WALKABLE_INSIDE(Back[x][y]))
5391     return;
5392
5393   if (Back[x][y])
5394     DrawLevelElement(x, y, Back[x][y]);
5395   else if (Store[x][y])
5396     DrawLevelElement(x, y, Store[x][y]);
5397   else if (game.use_masked_elements)
5398     DrawLevelElement(x, y, EL_EMPTY);
5399
5400   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5401
5402   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5403     DrawGraphicThruMask(sx, sy, graphic, frame);
5404   else
5405     DrawGraphic(sx, sy, graphic, frame);
5406 }
5407
5408 static void CheckDynamite(int x, int y)
5409 {
5410   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5411   {
5412     MovDelay[x][y]--;
5413
5414     if (MovDelay[x][y] != 0)
5415     {
5416       DrawDynamite(x, y);
5417       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5418
5419       return;
5420     }
5421   }
5422
5423   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5424
5425   Bang(x, y);
5426 }
5427
5428 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5429 {
5430   boolean num_checked_players = 0;
5431   int i;
5432
5433   for (i = 0; i < MAX_PLAYERS; i++)
5434   {
5435     if (stored_player[i].active)
5436     {
5437       int sx = stored_player[i].jx;
5438       int sy = stored_player[i].jy;
5439
5440       if (num_checked_players == 0)
5441       {
5442         *sx1 = *sx2 = sx;
5443         *sy1 = *sy2 = sy;
5444       }
5445       else
5446       {
5447         *sx1 = MIN(*sx1, sx);
5448         *sy1 = MIN(*sy1, sy);
5449         *sx2 = MAX(*sx2, sx);
5450         *sy2 = MAX(*sy2, sy);
5451       }
5452
5453       num_checked_players++;
5454     }
5455   }
5456 }
5457
5458 static boolean checkIfAllPlayersFitToScreen_RND(void)
5459 {
5460   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5461
5462   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5463
5464   return (sx2 - sx1 < SCR_FIELDX &&
5465           sy2 - sy1 < SCR_FIELDY);
5466 }
5467
5468 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5469 {
5470   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5471
5472   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5473
5474   *sx = (sx1 + sx2) / 2;
5475   *sy = (sy1 + sy2) / 2;
5476 }
5477
5478 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5479                                boolean center_screen, boolean quick_relocation)
5480 {
5481   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5482   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5483   boolean no_delay = (tape.warp_forward);
5484   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5485   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5486   int new_scroll_x, new_scroll_y;
5487
5488   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5489   {
5490     // case 1: quick relocation inside visible screen (without scrolling)
5491
5492     RedrawPlayfield();
5493
5494     return;
5495   }
5496
5497   if (!level.shifted_relocation || center_screen)
5498   {
5499     // relocation _with_ centering of screen
5500
5501     new_scroll_x = SCROLL_POSITION_X(x);
5502     new_scroll_y = SCROLL_POSITION_Y(y);
5503   }
5504   else
5505   {
5506     // relocation _without_ centering of screen
5507
5508     int center_scroll_x = SCROLL_POSITION_X(old_x);
5509     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5510     int offset_x = x + (scroll_x - center_scroll_x);
5511     int offset_y = y + (scroll_y - center_scroll_y);
5512
5513     // for new screen position, apply previous offset to center position
5514     new_scroll_x = SCROLL_POSITION_X(offset_x);
5515     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5516   }
5517
5518   if (quick_relocation)
5519   {
5520     // case 2: quick relocation (redraw without visible scrolling)
5521
5522     scroll_x = new_scroll_x;
5523     scroll_y = new_scroll_y;
5524
5525     RedrawPlayfield();
5526
5527     return;
5528   }
5529
5530   // case 3: visible relocation (with scrolling to new position)
5531
5532   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5533
5534   SetVideoFrameDelay(wait_delay_value);
5535
5536   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5537   {
5538     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5539     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5540
5541     if (dx == 0 && dy == 0)             // no scrolling needed at all
5542       break;
5543
5544     scroll_x -= dx;
5545     scroll_y -= dy;
5546
5547     // set values for horizontal/vertical screen scrolling (half tile size)
5548     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5549     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5550     int pos_x = dx * TILEX / 2;
5551     int pos_y = dy * TILEY / 2;
5552     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5553     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5554
5555     ScrollLevel(dx, dy);
5556     DrawAllPlayers();
5557
5558     // scroll in two steps of half tile size to make things smoother
5559     BlitScreenToBitmapExt_RND(window, fx, fy);
5560
5561     // scroll second step to align at full tile size
5562     BlitScreenToBitmap(window);
5563   }
5564
5565   DrawAllPlayers();
5566   BackToFront();
5567
5568   SetVideoFrameDelay(frame_delay_value_old);
5569 }
5570
5571 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5572 {
5573   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5574   int player_nr = GET_PLAYER_NR(el_player);
5575   struct PlayerInfo *player = &stored_player[player_nr];
5576   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5577   boolean no_delay = (tape.warp_forward);
5578   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5579   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5580   int old_jx = player->jx;
5581   int old_jy = player->jy;
5582   int old_element = Tile[old_jx][old_jy];
5583   int element = Tile[jx][jy];
5584   boolean player_relocated = (old_jx != jx || old_jy != jy);
5585
5586   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5587   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5588   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5589   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5590   int leave_side_horiz = move_dir_horiz;
5591   int leave_side_vert  = move_dir_vert;
5592   int enter_side = enter_side_horiz | enter_side_vert;
5593   int leave_side = leave_side_horiz | leave_side_vert;
5594
5595   if (player->buried)           // do not reanimate dead player
5596     return;
5597
5598   if (!player_relocated)        // no need to relocate the player
5599     return;
5600
5601   if (IS_PLAYER(jx, jy))        // player already placed at new position
5602   {
5603     RemoveField(jx, jy);        // temporarily remove newly placed player
5604     DrawLevelField(jx, jy);
5605   }
5606
5607   if (player->present)
5608   {
5609     while (player->MovPos)
5610     {
5611       ScrollPlayer(player, SCROLL_GO_ON);
5612       ScrollScreen(NULL, SCROLL_GO_ON);
5613
5614       AdvanceFrameAndPlayerCounters(player->index_nr);
5615
5616       DrawPlayer(player);
5617
5618       BackToFront_WithFrameDelay(wait_delay_value);
5619     }
5620
5621     DrawPlayer(player);         // needed here only to cleanup last field
5622     DrawLevelField(player->jx, player->jy);     // remove player graphic
5623
5624     player->is_moving = FALSE;
5625   }
5626
5627   if (IS_CUSTOM_ELEMENT(old_element))
5628     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5629                                CE_LEFT_BY_PLAYER,
5630                                player->index_bit, leave_side);
5631
5632   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5633                                       CE_PLAYER_LEAVES_X,
5634                                       player->index_bit, leave_side);
5635
5636   Tile[jx][jy] = el_player;
5637   InitPlayerField(jx, jy, el_player, TRUE);
5638
5639   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5640      possible that the relocation target field did not contain a player element,
5641      but a walkable element, to which the new player was relocated -- in this
5642      case, restore that (already initialized!) element on the player field */
5643   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5644   {
5645     Tile[jx][jy] = element;     // restore previously existing element
5646   }
5647
5648   // only visually relocate centered player
5649   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5650                      FALSE, level.instant_relocation);
5651
5652   TestIfPlayerTouchesBadThing(jx, jy);
5653   TestIfPlayerTouchesCustomElement(jx, jy);
5654
5655   if (IS_CUSTOM_ELEMENT(element))
5656     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5657                                player->index_bit, enter_side);
5658
5659   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5660                                       player->index_bit, enter_side);
5661
5662   if (player->is_switching)
5663   {
5664     /* ensure that relocation while still switching an element does not cause
5665        a new element to be treated as also switched directly after relocation
5666        (this is important for teleporter switches that teleport the player to
5667        a place where another teleporter switch is in the same direction, which
5668        would then incorrectly be treated as immediately switched before the
5669        direction key that caused the switch was released) */
5670
5671     player->switch_x += jx - old_jx;
5672     player->switch_y += jy - old_jy;
5673   }
5674 }
5675
5676 static void Explode(int ex, int ey, int phase, int mode)
5677 {
5678   int x, y;
5679   int last_phase;
5680   int border_element;
5681
5682   // !!! eliminate this variable !!!
5683   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5684
5685   if (game.explosions_delayed)
5686   {
5687     ExplodeField[ex][ey] = mode;
5688     return;
5689   }
5690
5691   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5692   {
5693     int center_element = Tile[ex][ey];
5694     int artwork_element, explosion_element;     // set these values later
5695
5696     // remove things displayed in background while burning dynamite
5697     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5698       Back[ex][ey] = 0;
5699
5700     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5701     {
5702       // put moving element to center field (and let it explode there)
5703       center_element = MovingOrBlocked2Element(ex, ey);
5704       RemoveMovingField(ex, ey);
5705       Tile[ex][ey] = center_element;
5706     }
5707
5708     // now "center_element" is finally determined -- set related values now
5709     artwork_element = center_element;           // for custom player artwork
5710     explosion_element = center_element;         // for custom player artwork
5711
5712     if (IS_PLAYER(ex, ey))
5713     {
5714       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5715
5716       artwork_element = stored_player[player_nr].artwork_element;
5717
5718       if (level.use_explosion_element[player_nr])
5719       {
5720         explosion_element = level.explosion_element[player_nr];
5721         artwork_element = explosion_element;
5722       }
5723     }
5724
5725     if (mode == EX_TYPE_NORMAL ||
5726         mode == EX_TYPE_CENTER ||
5727         mode == EX_TYPE_CROSS)
5728       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5729
5730     last_phase = element_info[explosion_element].explosion_delay + 1;
5731
5732     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5733     {
5734       int xx = x - ex + 1;
5735       int yy = y - ey + 1;
5736       int element;
5737
5738       if (!IN_LEV_FIELD(x, y) ||
5739           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5740           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5741         continue;
5742
5743       element = Tile[x][y];
5744
5745       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5746       {
5747         element = MovingOrBlocked2Element(x, y);
5748
5749         if (!IS_EXPLOSION_PROOF(element))
5750           RemoveMovingField(x, y);
5751       }
5752
5753       // indestructible elements can only explode in center (but not flames)
5754       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5755                                            mode == EX_TYPE_BORDER)) ||
5756           element == EL_FLAMES)
5757         continue;
5758
5759       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5760          behaviour, for example when touching a yamyam that explodes to rocks
5761          with active deadly shield, a rock is created under the player !!! */
5762       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5763 #if 0
5764       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5765           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5766            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5767 #else
5768       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5769 #endif
5770       {
5771         if (IS_ACTIVE_BOMB(element))
5772         {
5773           // re-activate things under the bomb like gate or penguin
5774           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5775           Back[x][y] = 0;
5776         }
5777
5778         continue;
5779       }
5780
5781       // save walkable background elements while explosion on same tile
5782       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5783           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5784         Back[x][y] = element;
5785
5786       // ignite explodable elements reached by other explosion
5787       if (element == EL_EXPLOSION)
5788         element = Store2[x][y];
5789
5790       if (AmoebaNr[x][y] &&
5791           (element == EL_AMOEBA_FULL ||
5792            element == EL_BD_AMOEBA ||
5793            element == EL_AMOEBA_GROWING))
5794       {
5795         AmoebaCnt[AmoebaNr[x][y]]--;
5796         AmoebaCnt2[AmoebaNr[x][y]]--;
5797       }
5798
5799       RemoveField(x, y);
5800
5801       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5802       {
5803         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5804
5805         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5806
5807         if (PLAYERINFO(ex, ey)->use_murphy)
5808           Store[x][y] = EL_EMPTY;
5809       }
5810
5811       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5812       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5813       else if (ELEM_IS_PLAYER(center_element))
5814         Store[x][y] = EL_EMPTY;
5815       else if (center_element == EL_YAMYAM)
5816         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5817       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5818         Store[x][y] = element_info[center_element].content.e[xx][yy];
5819 #if 1
5820       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5821       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5822       // otherwise) -- FIX THIS !!!
5823       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5824         Store[x][y] = element_info[element].content.e[1][1];
5825 #else
5826       else if (!CAN_EXPLODE(element))
5827         Store[x][y] = element_info[element].content.e[1][1];
5828 #endif
5829       else
5830         Store[x][y] = EL_EMPTY;
5831
5832       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5833           center_element == EL_AMOEBA_TO_DIAMOND)
5834         Store2[x][y] = element;
5835
5836       Tile[x][y] = EL_EXPLOSION;
5837       GfxElement[x][y] = artwork_element;
5838
5839       ExplodePhase[x][y] = 1;
5840       ExplodeDelay[x][y] = last_phase;
5841
5842       Stop[x][y] = TRUE;
5843     }
5844
5845     if (center_element == EL_YAMYAM)
5846       game.yamyam_content_nr =
5847         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5848
5849     return;
5850   }
5851
5852   if (Stop[ex][ey])
5853     return;
5854
5855   x = ex;
5856   y = ey;
5857
5858   if (phase == 1)
5859     GfxFrame[x][y] = 0;         // restart explosion animation
5860
5861   last_phase = ExplodeDelay[x][y];
5862
5863   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5864
5865   // this can happen if the player leaves an explosion just in time
5866   if (GfxElement[x][y] == EL_UNDEFINED)
5867     GfxElement[x][y] = EL_EMPTY;
5868
5869   border_element = Store2[x][y];
5870   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5871     border_element = StorePlayer[x][y];
5872
5873   if (phase == element_info[border_element].ignition_delay ||
5874       phase == last_phase)
5875   {
5876     boolean border_explosion = FALSE;
5877
5878     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5879         !PLAYER_EXPLOSION_PROTECTED(x, y))
5880     {
5881       KillPlayerUnlessExplosionProtected(x, y);
5882       border_explosion = TRUE;
5883     }
5884     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5885     {
5886       Tile[x][y] = Store2[x][y];
5887       Store2[x][y] = 0;
5888       Bang(x, y);
5889       border_explosion = TRUE;
5890     }
5891     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5892     {
5893       AmoebaToDiamond(x, y);
5894       Store2[x][y] = 0;
5895       border_explosion = TRUE;
5896     }
5897
5898     // if an element just explodes due to another explosion (chain-reaction),
5899     // do not immediately end the new explosion when it was the last frame of
5900     // the explosion (as it would be done in the following "if"-statement!)
5901     if (border_explosion && phase == last_phase)
5902       return;
5903   }
5904
5905   if (phase == last_phase)
5906   {
5907     int element;
5908
5909     element = Tile[x][y] = Store[x][y];
5910     Store[x][y] = Store2[x][y] = 0;
5911     GfxElement[x][y] = EL_UNDEFINED;
5912
5913     // player can escape from explosions and might therefore be still alive
5914     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5915         element <= EL_PLAYER_IS_EXPLODING_4)
5916     {
5917       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5918       int explosion_element = EL_PLAYER_1 + player_nr;
5919       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5920       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5921
5922       if (level.use_explosion_element[player_nr])
5923         explosion_element = level.explosion_element[player_nr];
5924
5925       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5926                     element_info[explosion_element].content.e[xx][yy]);
5927     }
5928
5929     // restore probably existing indestructible background element
5930     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5931       element = Tile[x][y] = Back[x][y];
5932     Back[x][y] = 0;
5933
5934     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5935     GfxDir[x][y] = MV_NONE;
5936     ChangeDelay[x][y] = 0;
5937     ChangePage[x][y] = -1;
5938
5939     CustomValue[x][y] = 0;
5940
5941     InitField_WithBug2(x, y, FALSE);
5942
5943     TEST_DrawLevelField(x, y);
5944
5945     TestIfElementTouchesCustomElement(x, y);
5946
5947     if (GFX_CRUMBLED(element))
5948       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5949
5950     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5951       StorePlayer[x][y] = 0;
5952
5953     if (ELEM_IS_PLAYER(element))
5954       RelocatePlayer(x, y, element);
5955   }
5956   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5957   {
5958     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5959     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5960
5961     if (phase == delay)
5962       TEST_DrawLevelFieldCrumbled(x, y);
5963
5964     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5965     {
5966       DrawLevelElement(x, y, Back[x][y]);
5967       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5968     }
5969     else if (IS_WALKABLE_UNDER(Back[x][y]))
5970     {
5971       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5972       DrawLevelElementThruMask(x, y, Back[x][y]);
5973     }
5974     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5975       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5976   }
5977 }
5978
5979 static void DynaExplode(int ex, int ey)
5980 {
5981   int i, j;
5982   int dynabomb_element = Tile[ex][ey];
5983   int dynabomb_size = 1;
5984   boolean dynabomb_xl = FALSE;
5985   struct PlayerInfo *player;
5986   static int xy[4][2] =
5987   {
5988     { 0, -1 },
5989     { -1, 0 },
5990     { +1, 0 },
5991     { 0, +1 }
5992   };
5993
5994   if (IS_ACTIVE_BOMB(dynabomb_element))
5995   {
5996     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5997     dynabomb_size = player->dynabomb_size;
5998     dynabomb_xl = player->dynabomb_xl;
5999     player->dynabombs_left++;
6000   }
6001
6002   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6003
6004   for (i = 0; i < NUM_DIRECTIONS; i++)
6005   {
6006     for (j = 1; j <= dynabomb_size; j++)
6007     {
6008       int x = ex + j * xy[i][0];
6009       int y = ey + j * xy[i][1];
6010       int element;
6011
6012       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6013         break;
6014
6015       element = Tile[x][y];
6016
6017       // do not restart explosions of fields with active bombs
6018       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6019         continue;
6020
6021       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6022
6023       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6024           !IS_DIGGABLE(element) && !dynabomb_xl)
6025         break;
6026     }
6027   }
6028 }
6029
6030 void Bang(int x, int y)
6031 {
6032   int element = MovingOrBlocked2Element(x, y);
6033   int explosion_type = EX_TYPE_NORMAL;
6034
6035   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6036   {
6037     struct PlayerInfo *player = PLAYERINFO(x, y);
6038
6039     element = Tile[x][y] = player->initial_element;
6040
6041     if (level.use_explosion_element[player->index_nr])
6042     {
6043       int explosion_element = level.explosion_element[player->index_nr];
6044
6045       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6046         explosion_type = EX_TYPE_CROSS;
6047       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6048         explosion_type = EX_TYPE_CENTER;
6049     }
6050   }
6051
6052   switch (element)
6053   {
6054     case EL_BUG:
6055     case EL_SPACESHIP:
6056     case EL_BD_BUTTERFLY:
6057     case EL_BD_FIREFLY:
6058     case EL_YAMYAM:
6059     case EL_DARK_YAMYAM:
6060     case EL_ROBOT:
6061     case EL_PACMAN:
6062     case EL_MOLE:
6063       RaiseScoreElement(element);
6064       break;
6065
6066     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6067     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6068     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6069     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6070     case EL_DYNABOMB_INCREASE_NUMBER:
6071     case EL_DYNABOMB_INCREASE_SIZE:
6072     case EL_DYNABOMB_INCREASE_POWER:
6073       explosion_type = EX_TYPE_DYNA;
6074       break;
6075
6076     case EL_DC_LANDMINE:
6077       explosion_type = EX_TYPE_CENTER;
6078       break;
6079
6080     case EL_PENGUIN:
6081     case EL_LAMP:
6082     case EL_LAMP_ACTIVE:
6083     case EL_AMOEBA_TO_DIAMOND:
6084       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6085         explosion_type = EX_TYPE_CENTER;
6086       break;
6087
6088     default:
6089       if (element_info[element].explosion_type == EXPLODES_CROSS)
6090         explosion_type = EX_TYPE_CROSS;
6091       else if (element_info[element].explosion_type == EXPLODES_1X1)
6092         explosion_type = EX_TYPE_CENTER;
6093       break;
6094   }
6095
6096   if (explosion_type == EX_TYPE_DYNA)
6097     DynaExplode(x, y);
6098   else
6099     Explode(x, y, EX_PHASE_START, explosion_type);
6100
6101   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6102 }
6103
6104 static void SplashAcid(int x, int y)
6105 {
6106   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6107       (!IN_LEV_FIELD(x - 1, y - 2) ||
6108        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6109     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6110
6111   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6112       (!IN_LEV_FIELD(x + 1, y - 2) ||
6113        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6114     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6115
6116   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6117 }
6118
6119 static void InitBeltMovement(void)
6120 {
6121   static int belt_base_element[4] =
6122   {
6123     EL_CONVEYOR_BELT_1_LEFT,
6124     EL_CONVEYOR_BELT_2_LEFT,
6125     EL_CONVEYOR_BELT_3_LEFT,
6126     EL_CONVEYOR_BELT_4_LEFT
6127   };
6128   static int belt_base_active_element[4] =
6129   {
6130     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6131     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6132     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6133     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6134   };
6135
6136   int x, y, i, j;
6137
6138   // set frame order for belt animation graphic according to belt direction
6139   for (i = 0; i < NUM_BELTS; i++)
6140   {
6141     int belt_nr = i;
6142
6143     for (j = 0; j < NUM_BELT_PARTS; j++)
6144     {
6145       int element = belt_base_active_element[belt_nr] + j;
6146       int graphic_1 = el2img(element);
6147       int graphic_2 = el2panelimg(element);
6148
6149       if (game.belt_dir[i] == MV_LEFT)
6150       {
6151         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6152         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6153       }
6154       else
6155       {
6156         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6157         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6158       }
6159     }
6160   }
6161
6162   SCAN_PLAYFIELD(x, y)
6163   {
6164     int element = Tile[x][y];
6165
6166     for (i = 0; i < NUM_BELTS; i++)
6167     {
6168       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6169       {
6170         int e_belt_nr = getBeltNrFromBeltElement(element);
6171         int belt_nr = i;
6172
6173         if (e_belt_nr == belt_nr)
6174         {
6175           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6176
6177           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6178         }
6179       }
6180     }
6181   }
6182 }
6183
6184 static void ToggleBeltSwitch(int x, int y)
6185 {
6186   static int belt_base_element[4] =
6187   {
6188     EL_CONVEYOR_BELT_1_LEFT,
6189     EL_CONVEYOR_BELT_2_LEFT,
6190     EL_CONVEYOR_BELT_3_LEFT,
6191     EL_CONVEYOR_BELT_4_LEFT
6192   };
6193   static int belt_base_active_element[4] =
6194   {
6195     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6196     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6197     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6198     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6199   };
6200   static int belt_base_switch_element[4] =
6201   {
6202     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6203     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6204     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6205     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6206   };
6207   static int belt_move_dir[4] =
6208   {
6209     MV_LEFT,
6210     MV_NONE,
6211     MV_RIGHT,
6212     MV_NONE,
6213   };
6214
6215   int element = Tile[x][y];
6216   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6217   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6218   int belt_dir = belt_move_dir[belt_dir_nr];
6219   int xx, yy, i;
6220
6221   if (!IS_BELT_SWITCH(element))
6222     return;
6223
6224   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6225   game.belt_dir[belt_nr] = belt_dir;
6226
6227   if (belt_dir_nr == 3)
6228     belt_dir_nr = 1;
6229
6230   // set frame order for belt animation graphic according to belt direction
6231   for (i = 0; i < NUM_BELT_PARTS; i++)
6232   {
6233     int element = belt_base_active_element[belt_nr] + i;
6234     int graphic_1 = el2img(element);
6235     int graphic_2 = el2panelimg(element);
6236
6237     if (belt_dir == MV_LEFT)
6238     {
6239       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6240       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6241     }
6242     else
6243     {
6244       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6245       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6246     }
6247   }
6248
6249   SCAN_PLAYFIELD(xx, yy)
6250   {
6251     int element = Tile[xx][yy];
6252
6253     if (IS_BELT_SWITCH(element))
6254     {
6255       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6256
6257       if (e_belt_nr == belt_nr)
6258       {
6259         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6260         TEST_DrawLevelField(xx, yy);
6261       }
6262     }
6263     else if (IS_BELT(element) && belt_dir != MV_NONE)
6264     {
6265       int e_belt_nr = getBeltNrFromBeltElement(element);
6266
6267       if (e_belt_nr == belt_nr)
6268       {
6269         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6270
6271         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6272         TEST_DrawLevelField(xx, yy);
6273       }
6274     }
6275     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6276     {
6277       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6278
6279       if (e_belt_nr == belt_nr)
6280       {
6281         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6282
6283         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6284         TEST_DrawLevelField(xx, yy);
6285       }
6286     }
6287   }
6288 }
6289
6290 static void ToggleSwitchgateSwitch(int x, int y)
6291 {
6292   int xx, yy;
6293
6294   game.switchgate_pos = !game.switchgate_pos;
6295
6296   SCAN_PLAYFIELD(xx, yy)
6297   {
6298     int element = Tile[xx][yy];
6299
6300     if (element == EL_SWITCHGATE_SWITCH_UP)
6301     {
6302       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6303       TEST_DrawLevelField(xx, yy);
6304     }
6305     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6306     {
6307       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6308       TEST_DrawLevelField(xx, yy);
6309     }
6310     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6311     {
6312       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6313       TEST_DrawLevelField(xx, yy);
6314     }
6315     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6316     {
6317       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6318       TEST_DrawLevelField(xx, yy);
6319     }
6320     else if (element == EL_SWITCHGATE_OPEN ||
6321              element == EL_SWITCHGATE_OPENING)
6322     {
6323       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6324
6325       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6326     }
6327     else if (element == EL_SWITCHGATE_CLOSED ||
6328              element == EL_SWITCHGATE_CLOSING)
6329     {
6330       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6331
6332       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6333     }
6334   }
6335 }
6336
6337 static int getInvisibleActiveFromInvisibleElement(int element)
6338 {
6339   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6340           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6341           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6342           element);
6343 }
6344
6345 static int getInvisibleFromInvisibleActiveElement(int element)
6346 {
6347   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6348           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6349           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6350           element);
6351 }
6352
6353 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6354 {
6355   int x, y;
6356
6357   SCAN_PLAYFIELD(x, y)
6358   {
6359     int element = Tile[x][y];
6360
6361     if (element == EL_LIGHT_SWITCH &&
6362         game.light_time_left > 0)
6363     {
6364       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6365       TEST_DrawLevelField(x, y);
6366     }
6367     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6368              game.light_time_left == 0)
6369     {
6370       Tile[x][y] = EL_LIGHT_SWITCH;
6371       TEST_DrawLevelField(x, y);
6372     }
6373     else if (element == EL_EMC_DRIPPER &&
6374              game.light_time_left > 0)
6375     {
6376       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6377       TEST_DrawLevelField(x, y);
6378     }
6379     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6380              game.light_time_left == 0)
6381     {
6382       Tile[x][y] = EL_EMC_DRIPPER;
6383       TEST_DrawLevelField(x, y);
6384     }
6385     else if (element == EL_INVISIBLE_STEELWALL ||
6386              element == EL_INVISIBLE_WALL ||
6387              element == EL_INVISIBLE_SAND)
6388     {
6389       if (game.light_time_left > 0)
6390         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6391
6392       TEST_DrawLevelField(x, y);
6393
6394       // uncrumble neighbour fields, if needed
6395       if (element == EL_INVISIBLE_SAND)
6396         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6397     }
6398     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6399              element == EL_INVISIBLE_WALL_ACTIVE ||
6400              element == EL_INVISIBLE_SAND_ACTIVE)
6401     {
6402       if (game.light_time_left == 0)
6403         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6404
6405       TEST_DrawLevelField(x, y);
6406
6407       // re-crumble neighbour fields, if needed
6408       if (element == EL_INVISIBLE_SAND)
6409         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6410     }
6411   }
6412 }
6413
6414 static void RedrawAllInvisibleElementsForLenses(void)
6415 {
6416   int x, y;
6417
6418   SCAN_PLAYFIELD(x, y)
6419   {
6420     int element = Tile[x][y];
6421
6422     if (element == EL_EMC_DRIPPER &&
6423         game.lenses_time_left > 0)
6424     {
6425       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6426       TEST_DrawLevelField(x, y);
6427     }
6428     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6429              game.lenses_time_left == 0)
6430     {
6431       Tile[x][y] = EL_EMC_DRIPPER;
6432       TEST_DrawLevelField(x, y);
6433     }
6434     else if (element == EL_INVISIBLE_STEELWALL ||
6435              element == EL_INVISIBLE_WALL ||
6436              element == EL_INVISIBLE_SAND)
6437     {
6438       if (game.lenses_time_left > 0)
6439         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6440
6441       TEST_DrawLevelField(x, y);
6442
6443       // uncrumble neighbour fields, if needed
6444       if (element == EL_INVISIBLE_SAND)
6445         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6446     }
6447     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6448              element == EL_INVISIBLE_WALL_ACTIVE ||
6449              element == EL_INVISIBLE_SAND_ACTIVE)
6450     {
6451       if (game.lenses_time_left == 0)
6452         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6453
6454       TEST_DrawLevelField(x, y);
6455
6456       // re-crumble neighbour fields, if needed
6457       if (element == EL_INVISIBLE_SAND)
6458         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6459     }
6460   }
6461 }
6462
6463 static void RedrawAllInvisibleElementsForMagnifier(void)
6464 {
6465   int x, y;
6466
6467   SCAN_PLAYFIELD(x, y)
6468   {
6469     int element = Tile[x][y];
6470
6471     if (element == EL_EMC_FAKE_GRASS &&
6472         game.magnify_time_left > 0)
6473     {
6474       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6475       TEST_DrawLevelField(x, y);
6476     }
6477     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6478              game.magnify_time_left == 0)
6479     {
6480       Tile[x][y] = EL_EMC_FAKE_GRASS;
6481       TEST_DrawLevelField(x, y);
6482     }
6483     else if (IS_GATE_GRAY(element) &&
6484              game.magnify_time_left > 0)
6485     {
6486       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6487                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6488                     IS_EM_GATE_GRAY(element) ?
6489                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6490                     IS_EMC_GATE_GRAY(element) ?
6491                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6492                     IS_DC_GATE_GRAY(element) ?
6493                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6494                     element);
6495       TEST_DrawLevelField(x, y);
6496     }
6497     else if (IS_GATE_GRAY_ACTIVE(element) &&
6498              game.magnify_time_left == 0)
6499     {
6500       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6501                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6502                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6503                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6504                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6505                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6506                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6507                     EL_DC_GATE_WHITE_GRAY :
6508                     element);
6509       TEST_DrawLevelField(x, y);
6510     }
6511   }
6512 }
6513
6514 static void ToggleLightSwitch(int x, int y)
6515 {
6516   int element = Tile[x][y];
6517
6518   game.light_time_left =
6519     (element == EL_LIGHT_SWITCH ?
6520      level.time_light * FRAMES_PER_SECOND : 0);
6521
6522   RedrawAllLightSwitchesAndInvisibleElements();
6523 }
6524
6525 static void ActivateTimegateSwitch(int x, int y)
6526 {
6527   int xx, yy;
6528
6529   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6530
6531   SCAN_PLAYFIELD(xx, yy)
6532   {
6533     int element = Tile[xx][yy];
6534
6535     if (element == EL_TIMEGATE_CLOSED ||
6536         element == EL_TIMEGATE_CLOSING)
6537     {
6538       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6539       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6540     }
6541
6542     /*
6543     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6544     {
6545       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6546       TEST_DrawLevelField(xx, yy);
6547     }
6548     */
6549
6550   }
6551
6552   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6553                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6554 }
6555
6556 static void Impact(int x, int y)
6557 {
6558   boolean last_line = (y == lev_fieldy - 1);
6559   boolean object_hit = FALSE;
6560   boolean impact = (last_line || object_hit);
6561   int element = Tile[x][y];
6562   int smashed = EL_STEELWALL;
6563
6564   if (!last_line)       // check if element below was hit
6565   {
6566     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6567       return;
6568
6569     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6570                                          MovDir[x][y + 1] != MV_DOWN ||
6571                                          MovPos[x][y + 1] <= TILEY / 2));
6572
6573     // do not smash moving elements that left the smashed field in time
6574     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6575         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6576       object_hit = FALSE;
6577
6578 #if USE_QUICKSAND_IMPACT_BUGFIX
6579     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6580     {
6581       RemoveMovingField(x, y + 1);
6582       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6583       Tile[x][y + 2] = EL_ROCK;
6584       TEST_DrawLevelField(x, y + 2);
6585
6586       object_hit = TRUE;
6587     }
6588
6589     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6590     {
6591       RemoveMovingField(x, y + 1);
6592       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6593       Tile[x][y + 2] = EL_ROCK;
6594       TEST_DrawLevelField(x, y + 2);
6595
6596       object_hit = TRUE;
6597     }
6598 #endif
6599
6600     if (object_hit)
6601       smashed = MovingOrBlocked2Element(x, y + 1);
6602
6603     impact = (last_line || object_hit);
6604   }
6605
6606   if (!last_line && smashed == EL_ACID) // element falls into acid
6607   {
6608     SplashAcid(x, y + 1);
6609     return;
6610   }
6611
6612   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6613   // only reset graphic animation if graphic really changes after impact
6614   if (impact &&
6615       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6616   {
6617     ResetGfxAnimation(x, y);
6618     TEST_DrawLevelField(x, y);
6619   }
6620
6621   if (impact && CAN_EXPLODE_IMPACT(element))
6622   {
6623     Bang(x, y);
6624     return;
6625   }
6626   else if (impact && element == EL_PEARL &&
6627            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6628   {
6629     ResetGfxAnimation(x, y);
6630
6631     Tile[x][y] = EL_PEARL_BREAKING;
6632     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6633     return;
6634   }
6635   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6636   {
6637     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6638
6639     return;
6640   }
6641
6642   if (impact && element == EL_AMOEBA_DROP)
6643   {
6644     if (object_hit && IS_PLAYER(x, y + 1))
6645       KillPlayerUnlessEnemyProtected(x, y + 1);
6646     else if (object_hit && smashed == EL_PENGUIN)
6647       Bang(x, y + 1);
6648     else
6649     {
6650       Tile[x][y] = EL_AMOEBA_GROWING;
6651       Store[x][y] = EL_AMOEBA_WET;
6652
6653       ResetRandomAnimationValue(x, y);
6654     }
6655     return;
6656   }
6657
6658   if (object_hit)               // check which object was hit
6659   {
6660     if ((CAN_PASS_MAGIC_WALL(element) && 
6661          (smashed == EL_MAGIC_WALL ||
6662           smashed == EL_BD_MAGIC_WALL)) ||
6663         (CAN_PASS_DC_MAGIC_WALL(element) &&
6664          smashed == EL_DC_MAGIC_WALL))
6665     {
6666       int xx, yy;
6667       int activated_magic_wall =
6668         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6669          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6670          EL_DC_MAGIC_WALL_ACTIVE);
6671
6672       // activate magic wall / mill
6673       SCAN_PLAYFIELD(xx, yy)
6674       {
6675         if (Tile[xx][yy] == smashed)
6676           Tile[xx][yy] = activated_magic_wall;
6677       }
6678
6679       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6680       game.magic_wall_active = TRUE;
6681
6682       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6683                             SND_MAGIC_WALL_ACTIVATING :
6684                             smashed == EL_BD_MAGIC_WALL ?
6685                             SND_BD_MAGIC_WALL_ACTIVATING :
6686                             SND_DC_MAGIC_WALL_ACTIVATING));
6687     }
6688
6689     if (IS_PLAYER(x, y + 1))
6690     {
6691       if (CAN_SMASH_PLAYER(element))
6692       {
6693         KillPlayerUnlessEnemyProtected(x, y + 1);
6694         return;
6695       }
6696     }
6697     else if (smashed == EL_PENGUIN)
6698     {
6699       if (CAN_SMASH_PLAYER(element))
6700       {
6701         Bang(x, y + 1);
6702         return;
6703       }
6704     }
6705     else if (element == EL_BD_DIAMOND)
6706     {
6707       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6708       {
6709         Bang(x, y + 1);
6710         return;
6711       }
6712     }
6713     else if (((element == EL_SP_INFOTRON ||
6714                element == EL_SP_ZONK) &&
6715               (smashed == EL_SP_SNIKSNAK ||
6716                smashed == EL_SP_ELECTRON ||
6717                smashed == EL_SP_DISK_ORANGE)) ||
6718              (element == EL_SP_INFOTRON &&
6719               smashed == EL_SP_DISK_YELLOW))
6720     {
6721       Bang(x, y + 1);
6722       return;
6723     }
6724     else if (CAN_SMASH_EVERYTHING(element))
6725     {
6726       if (IS_CLASSIC_ENEMY(smashed) ||
6727           CAN_EXPLODE_SMASHED(smashed))
6728       {
6729         Bang(x, y + 1);
6730         return;
6731       }
6732       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6733       {
6734         if (smashed == EL_LAMP ||
6735             smashed == EL_LAMP_ACTIVE)
6736         {
6737           Bang(x, y + 1);
6738           return;
6739         }
6740         else if (smashed == EL_NUT)
6741         {
6742           Tile[x][y + 1] = EL_NUT_BREAKING;
6743           PlayLevelSound(x, y, SND_NUT_BREAKING);
6744           RaiseScoreElement(EL_NUT);
6745           return;
6746         }
6747         else if (smashed == EL_PEARL)
6748         {
6749           ResetGfxAnimation(x, y);
6750
6751           Tile[x][y + 1] = EL_PEARL_BREAKING;
6752           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6753           return;
6754         }
6755         else if (smashed == EL_DIAMOND)
6756         {
6757           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6758           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6759           return;
6760         }
6761         else if (IS_BELT_SWITCH(smashed))
6762         {
6763           ToggleBeltSwitch(x, y + 1);
6764         }
6765         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6766                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6767                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6768                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6769         {
6770           ToggleSwitchgateSwitch(x, y + 1);
6771         }
6772         else if (smashed == EL_LIGHT_SWITCH ||
6773                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6774         {
6775           ToggleLightSwitch(x, y + 1);
6776         }
6777         else
6778         {
6779           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6780
6781           CheckElementChangeBySide(x, y + 1, smashed, element,
6782                                    CE_SWITCHED, CH_SIDE_TOP);
6783           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6784                                             CH_SIDE_TOP);
6785         }
6786       }
6787       else
6788       {
6789         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6790       }
6791     }
6792   }
6793
6794   // play sound of magic wall / mill
6795   if (!last_line &&
6796       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6797        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6798        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6799   {
6800     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6801       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6802     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6803       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6804     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6805       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6806
6807     return;
6808   }
6809
6810   // play sound of object that hits the ground
6811   if (last_line || object_hit)
6812     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6813 }
6814
6815 static void TurnRoundExt(int x, int y)
6816 {
6817   static struct
6818   {
6819     int dx, dy;
6820   } move_xy[] =
6821   {
6822     {  0,  0 },
6823     { -1,  0 },
6824     { +1,  0 },
6825     {  0,  0 },
6826     {  0, -1 },
6827     {  0,  0 }, { 0, 0 }, { 0, 0 },
6828     {  0, +1 }
6829   };
6830   static struct
6831   {
6832     int left, right, back;
6833   } turn[] =
6834   {
6835     { 0,        0,              0        },
6836     { MV_DOWN,  MV_UP,          MV_RIGHT },
6837     { MV_UP,    MV_DOWN,        MV_LEFT  },
6838     { 0,        0,              0        },
6839     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6840     { 0,        0,              0        },
6841     { 0,        0,              0        },
6842     { 0,        0,              0        },
6843     { MV_RIGHT, MV_LEFT,        MV_UP    }
6844   };
6845
6846   int element = Tile[x][y];
6847   int move_pattern = element_info[element].move_pattern;
6848
6849   int old_move_dir = MovDir[x][y];
6850   int left_dir  = turn[old_move_dir].left;
6851   int right_dir = turn[old_move_dir].right;
6852   int back_dir  = turn[old_move_dir].back;
6853
6854   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6855   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6856   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6857   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6858
6859   int left_x  = x + left_dx,  left_y  = y + left_dy;
6860   int right_x = x + right_dx, right_y = y + right_dy;
6861   int move_x  = x + move_dx,  move_y  = y + move_dy;
6862
6863   int xx, yy;
6864
6865   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6866   {
6867     TestIfBadThingTouchesOtherBadThing(x, y);
6868
6869     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6870       MovDir[x][y] = right_dir;
6871     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6872       MovDir[x][y] = left_dir;
6873
6874     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6875       MovDelay[x][y] = 9;
6876     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6877       MovDelay[x][y] = 1;
6878   }
6879   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6880   {
6881     TestIfBadThingTouchesOtherBadThing(x, y);
6882
6883     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6884       MovDir[x][y] = left_dir;
6885     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6886       MovDir[x][y] = right_dir;
6887
6888     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6889       MovDelay[x][y] = 9;
6890     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6891       MovDelay[x][y] = 1;
6892   }
6893   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6894   {
6895     TestIfBadThingTouchesOtherBadThing(x, y);
6896
6897     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6898       MovDir[x][y] = left_dir;
6899     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6900       MovDir[x][y] = right_dir;
6901
6902     if (MovDir[x][y] != old_move_dir)
6903       MovDelay[x][y] = 9;
6904   }
6905   else if (element == EL_YAMYAM)
6906   {
6907     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6908     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6909
6910     if (can_turn_left && can_turn_right)
6911       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6912     else if (can_turn_left)
6913       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6914     else if (can_turn_right)
6915       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6916     else
6917       MovDir[x][y] = back_dir;
6918
6919     MovDelay[x][y] = 16 + 16 * RND(3);
6920   }
6921   else if (element == EL_DARK_YAMYAM)
6922   {
6923     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6924                                                          left_x, left_y);
6925     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6926                                                          right_x, right_y);
6927
6928     if (can_turn_left && can_turn_right)
6929       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6930     else if (can_turn_left)
6931       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6932     else if (can_turn_right)
6933       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6934     else
6935       MovDir[x][y] = back_dir;
6936
6937     MovDelay[x][y] = 16 + 16 * RND(3);
6938   }
6939   else if (element == EL_PACMAN)
6940   {
6941     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6942     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6943
6944     if (can_turn_left && can_turn_right)
6945       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6946     else if (can_turn_left)
6947       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6948     else if (can_turn_right)
6949       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6950     else
6951       MovDir[x][y] = back_dir;
6952
6953     MovDelay[x][y] = 6 + RND(40);
6954   }
6955   else if (element == EL_PIG)
6956   {
6957     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6958     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6959     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6960     boolean should_turn_left, should_turn_right, should_move_on;
6961     int rnd_value = 24;
6962     int rnd = RND(rnd_value);
6963
6964     should_turn_left = (can_turn_left &&
6965                         (!can_move_on ||
6966                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6967                                                    y + back_dy + left_dy)));
6968     should_turn_right = (can_turn_right &&
6969                          (!can_move_on ||
6970                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6971                                                     y + back_dy + right_dy)));
6972     should_move_on = (can_move_on &&
6973                       (!can_turn_left ||
6974                        !can_turn_right ||
6975                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6976                                                  y + move_dy + left_dy) ||
6977                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6978                                                  y + move_dy + right_dy)));
6979
6980     if (should_turn_left || should_turn_right || should_move_on)
6981     {
6982       if (should_turn_left && should_turn_right && should_move_on)
6983         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6984                         rnd < 2 * rnd_value / 3 ? right_dir :
6985                         old_move_dir);
6986       else if (should_turn_left && should_turn_right)
6987         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6988       else if (should_turn_left && should_move_on)
6989         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6990       else if (should_turn_right && should_move_on)
6991         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6992       else if (should_turn_left)
6993         MovDir[x][y] = left_dir;
6994       else if (should_turn_right)
6995         MovDir[x][y] = right_dir;
6996       else if (should_move_on)
6997         MovDir[x][y] = old_move_dir;
6998     }
6999     else if (can_move_on && rnd > rnd_value / 8)
7000       MovDir[x][y] = old_move_dir;
7001     else if (can_turn_left && can_turn_right)
7002       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7003     else if (can_turn_left && rnd > rnd_value / 8)
7004       MovDir[x][y] = left_dir;
7005     else if (can_turn_right && rnd > rnd_value/8)
7006       MovDir[x][y] = right_dir;
7007     else
7008       MovDir[x][y] = back_dir;
7009
7010     xx = x + move_xy[MovDir[x][y]].dx;
7011     yy = y + move_xy[MovDir[x][y]].dy;
7012
7013     if (!IN_LEV_FIELD(xx, yy) ||
7014         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7015       MovDir[x][y] = old_move_dir;
7016
7017     MovDelay[x][y] = 0;
7018   }
7019   else if (element == EL_DRAGON)
7020   {
7021     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7022     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7023     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7024     int rnd_value = 24;
7025     int rnd = RND(rnd_value);
7026
7027     if (can_move_on && rnd > rnd_value / 8)
7028       MovDir[x][y] = old_move_dir;
7029     else if (can_turn_left && can_turn_right)
7030       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7031     else if (can_turn_left && rnd > rnd_value / 8)
7032       MovDir[x][y] = left_dir;
7033     else if (can_turn_right && rnd > rnd_value / 8)
7034       MovDir[x][y] = right_dir;
7035     else
7036       MovDir[x][y] = back_dir;
7037
7038     xx = x + move_xy[MovDir[x][y]].dx;
7039     yy = y + move_xy[MovDir[x][y]].dy;
7040
7041     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7042       MovDir[x][y] = old_move_dir;
7043
7044     MovDelay[x][y] = 0;
7045   }
7046   else if (element == EL_MOLE)
7047   {
7048     boolean can_move_on =
7049       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7050                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7051                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7052     if (!can_move_on)
7053     {
7054       boolean can_turn_left =
7055         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7056                               IS_AMOEBOID(Tile[left_x][left_y])));
7057
7058       boolean can_turn_right =
7059         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7060                               IS_AMOEBOID(Tile[right_x][right_y])));
7061
7062       if (can_turn_left && can_turn_right)
7063         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7064       else if (can_turn_left)
7065         MovDir[x][y] = left_dir;
7066       else
7067         MovDir[x][y] = right_dir;
7068     }
7069
7070     if (MovDir[x][y] != old_move_dir)
7071       MovDelay[x][y] = 9;
7072   }
7073   else if (element == EL_BALLOON)
7074   {
7075     MovDir[x][y] = game.wind_direction;
7076     MovDelay[x][y] = 0;
7077   }
7078   else if (element == EL_SPRING)
7079   {
7080     if (MovDir[x][y] & MV_HORIZONTAL)
7081     {
7082       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7083           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7084       {
7085         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7086         ResetGfxAnimation(move_x, move_y);
7087         TEST_DrawLevelField(move_x, move_y);
7088
7089         MovDir[x][y] = back_dir;
7090       }
7091       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7092                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7093         MovDir[x][y] = MV_NONE;
7094     }
7095
7096     MovDelay[x][y] = 0;
7097   }
7098   else if (element == EL_ROBOT ||
7099            element == EL_SATELLITE ||
7100            element == EL_PENGUIN ||
7101            element == EL_EMC_ANDROID)
7102   {
7103     int attr_x = -1, attr_y = -1;
7104
7105     if (game.all_players_gone)
7106     {
7107       attr_x = game.exit_x;
7108       attr_y = game.exit_y;
7109     }
7110     else
7111     {
7112       int i;
7113
7114       for (i = 0; i < MAX_PLAYERS; i++)
7115       {
7116         struct PlayerInfo *player = &stored_player[i];
7117         int jx = player->jx, jy = player->jy;
7118
7119         if (!player->active)
7120           continue;
7121
7122         if (attr_x == -1 ||
7123             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7124         {
7125           attr_x = jx;
7126           attr_y = jy;
7127         }
7128       }
7129     }
7130
7131     if (element == EL_ROBOT &&
7132         game.robot_wheel_x >= 0 &&
7133         game.robot_wheel_y >= 0 &&
7134         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7135          game.engine_version < VERSION_IDENT(3,1,0,0)))
7136     {
7137       attr_x = game.robot_wheel_x;
7138       attr_y = game.robot_wheel_y;
7139     }
7140
7141     if (element == EL_PENGUIN)
7142     {
7143       int i;
7144       static int xy[4][2] =
7145       {
7146         { 0, -1 },
7147         { -1, 0 },
7148         { +1, 0 },
7149         { 0, +1 }
7150       };
7151
7152       for (i = 0; i < NUM_DIRECTIONS; i++)
7153       {
7154         int ex = x + xy[i][0];
7155         int ey = y + xy[i][1];
7156
7157         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7158                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7159                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7160                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7161         {
7162           attr_x = ex;
7163           attr_y = ey;
7164           break;
7165         }
7166       }
7167     }
7168
7169     MovDir[x][y] = MV_NONE;
7170     if (attr_x < x)
7171       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7172     else if (attr_x > x)
7173       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7174     if (attr_y < y)
7175       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7176     else if (attr_y > y)
7177       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7178
7179     if (element == EL_ROBOT)
7180     {
7181       int newx, newy;
7182
7183       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7184         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7185       Moving2Blocked(x, y, &newx, &newy);
7186
7187       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7188         MovDelay[x][y] = 8 + 8 * !RND(3);
7189       else
7190         MovDelay[x][y] = 16;
7191     }
7192     else if (element == EL_PENGUIN)
7193     {
7194       int newx, newy;
7195
7196       MovDelay[x][y] = 1;
7197
7198       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7199       {
7200         boolean first_horiz = RND(2);
7201         int new_move_dir = MovDir[x][y];
7202
7203         MovDir[x][y] =
7204           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205         Moving2Blocked(x, y, &newx, &newy);
7206
7207         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7208           return;
7209
7210         MovDir[x][y] =
7211           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212         Moving2Blocked(x, y, &newx, &newy);
7213
7214         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7215           return;
7216
7217         MovDir[x][y] = old_move_dir;
7218         return;
7219       }
7220     }
7221     else if (element == EL_SATELLITE)
7222     {
7223       int newx, newy;
7224
7225       MovDelay[x][y] = 1;
7226
7227       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7228       {
7229         boolean first_horiz = RND(2);
7230         int new_move_dir = MovDir[x][y];
7231
7232         MovDir[x][y] =
7233           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7234         Moving2Blocked(x, y, &newx, &newy);
7235
7236         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7237           return;
7238
7239         MovDir[x][y] =
7240           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7241         Moving2Blocked(x, y, &newx, &newy);
7242
7243         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7244           return;
7245
7246         MovDir[x][y] = old_move_dir;
7247         return;
7248       }
7249     }
7250     else if (element == EL_EMC_ANDROID)
7251     {
7252       static int check_pos[16] =
7253       {
7254         -1,             //  0 => (invalid)
7255         7,              //  1 => MV_LEFT
7256         3,              //  2 => MV_RIGHT
7257         -1,             //  3 => (invalid)
7258         1,              //  4 =>            MV_UP
7259         0,              //  5 => MV_LEFT  | MV_UP
7260         2,              //  6 => MV_RIGHT | MV_UP
7261         -1,             //  7 => (invalid)
7262         5,              //  8 =>            MV_DOWN
7263         6,              //  9 => MV_LEFT  | MV_DOWN
7264         4,              // 10 => MV_RIGHT | MV_DOWN
7265         -1,             // 11 => (invalid)
7266         -1,             // 12 => (invalid)
7267         -1,             // 13 => (invalid)
7268         -1,             // 14 => (invalid)
7269         -1,             // 15 => (invalid)
7270       };
7271       static struct
7272       {
7273         int dx, dy;
7274         int dir;
7275       } check_xy[8] =
7276       {
7277         { -1, -1,       MV_LEFT  | MV_UP   },
7278         {  0, -1,                  MV_UP   },
7279         { +1, -1,       MV_RIGHT | MV_UP   },
7280         { +1,  0,       MV_RIGHT           },
7281         { +1, +1,       MV_RIGHT | MV_DOWN },
7282         {  0, +1,                  MV_DOWN },
7283         { -1, +1,       MV_LEFT  | MV_DOWN },
7284         { -1,  0,       MV_LEFT            },
7285       };
7286       int start_pos, check_order;
7287       boolean can_clone = FALSE;
7288       int i;
7289
7290       // check if there is any free field around current position
7291       for (i = 0; i < 8; i++)
7292       {
7293         int newx = x + check_xy[i].dx;
7294         int newy = y + check_xy[i].dy;
7295
7296         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7297         {
7298           can_clone = TRUE;
7299
7300           break;
7301         }
7302       }
7303
7304       if (can_clone)            // randomly find an element to clone
7305       {
7306         can_clone = FALSE;
7307
7308         start_pos = check_pos[RND(8)];
7309         check_order = (RND(2) ? -1 : +1);
7310
7311         for (i = 0; i < 8; i++)
7312         {
7313           int pos_raw = start_pos + i * check_order;
7314           int pos = (pos_raw + 8) % 8;
7315           int newx = x + check_xy[pos].dx;
7316           int newy = y + check_xy[pos].dy;
7317
7318           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7319           {
7320             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7321             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7322
7323             Store[x][y] = Tile[newx][newy];
7324
7325             can_clone = TRUE;
7326
7327             break;
7328           }
7329         }
7330       }
7331
7332       if (can_clone)            // randomly find a direction to move
7333       {
7334         can_clone = FALSE;
7335
7336         start_pos = check_pos[RND(8)];
7337         check_order = (RND(2) ? -1 : +1);
7338
7339         for (i = 0; i < 8; i++)
7340         {
7341           int pos_raw = start_pos + i * check_order;
7342           int pos = (pos_raw + 8) % 8;
7343           int newx = x + check_xy[pos].dx;
7344           int newy = y + check_xy[pos].dy;
7345           int new_move_dir = check_xy[pos].dir;
7346
7347           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7348           {
7349             MovDir[x][y] = new_move_dir;
7350             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7351
7352             can_clone = TRUE;
7353
7354             break;
7355           }
7356         }
7357       }
7358
7359       if (can_clone)            // cloning and moving successful
7360         return;
7361
7362       // cannot clone -- try to move towards player
7363
7364       start_pos = check_pos[MovDir[x][y] & 0x0f];
7365       check_order = (RND(2) ? -1 : +1);
7366
7367       for (i = 0; i < 3; i++)
7368       {
7369         // first check start_pos, then previous/next or (next/previous) pos
7370         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7371         int pos = (pos_raw + 8) % 8;
7372         int newx = x + check_xy[pos].dx;
7373         int newy = y + check_xy[pos].dy;
7374         int new_move_dir = check_xy[pos].dir;
7375
7376         if (IS_PLAYER(newx, newy))
7377           break;
7378
7379         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7380         {
7381           MovDir[x][y] = new_move_dir;
7382           MovDelay[x][y] = level.android_move_time * 8 + 1;
7383
7384           break;
7385         }
7386       }
7387     }
7388   }
7389   else if (move_pattern == MV_TURNING_LEFT ||
7390            move_pattern == MV_TURNING_RIGHT ||
7391            move_pattern == MV_TURNING_LEFT_RIGHT ||
7392            move_pattern == MV_TURNING_RIGHT_LEFT ||
7393            move_pattern == MV_TURNING_RANDOM ||
7394            move_pattern == MV_ALL_DIRECTIONS)
7395   {
7396     boolean can_turn_left =
7397       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7398     boolean can_turn_right =
7399       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7400
7401     if (element_info[element].move_stepsize == 0)       // "not moving"
7402       return;
7403
7404     if (move_pattern == MV_TURNING_LEFT)
7405       MovDir[x][y] = left_dir;
7406     else if (move_pattern == MV_TURNING_RIGHT)
7407       MovDir[x][y] = right_dir;
7408     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7409       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7410     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7411       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7412     else if (move_pattern == MV_TURNING_RANDOM)
7413       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7414                       can_turn_right && !can_turn_left ? right_dir :
7415                       RND(2) ? left_dir : right_dir);
7416     else if (can_turn_left && can_turn_right)
7417       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7418     else if (can_turn_left)
7419       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7420     else if (can_turn_right)
7421       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7422     else
7423       MovDir[x][y] = back_dir;
7424
7425     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7426   }
7427   else if (move_pattern == MV_HORIZONTAL ||
7428            move_pattern == MV_VERTICAL)
7429   {
7430     if (move_pattern & old_move_dir)
7431       MovDir[x][y] = back_dir;
7432     else if (move_pattern == MV_HORIZONTAL)
7433       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7434     else if (move_pattern == MV_VERTICAL)
7435       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7436
7437     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7438   }
7439   else if (move_pattern & MV_ANY_DIRECTION)
7440   {
7441     MovDir[x][y] = move_pattern;
7442     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7443   }
7444   else if (move_pattern & MV_WIND_DIRECTION)
7445   {
7446     MovDir[x][y] = game.wind_direction;
7447     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7448   }
7449   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7450   {
7451     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7452       MovDir[x][y] = left_dir;
7453     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7454       MovDir[x][y] = right_dir;
7455
7456     if (MovDir[x][y] != old_move_dir)
7457       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7458   }
7459   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7460   {
7461     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7462       MovDir[x][y] = right_dir;
7463     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7464       MovDir[x][y] = left_dir;
7465
7466     if (MovDir[x][y] != old_move_dir)
7467       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7468   }
7469   else if (move_pattern == MV_TOWARDS_PLAYER ||
7470            move_pattern == MV_AWAY_FROM_PLAYER)
7471   {
7472     int attr_x = -1, attr_y = -1;
7473     int newx, newy;
7474     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7475
7476     if (game.all_players_gone)
7477     {
7478       attr_x = game.exit_x;
7479       attr_y = game.exit_y;
7480     }
7481     else
7482     {
7483       int i;
7484
7485       for (i = 0; i < MAX_PLAYERS; i++)
7486       {
7487         struct PlayerInfo *player = &stored_player[i];
7488         int jx = player->jx, jy = player->jy;
7489
7490         if (!player->active)
7491           continue;
7492
7493         if (attr_x == -1 ||
7494             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7495         {
7496           attr_x = jx;
7497           attr_y = jy;
7498         }
7499       }
7500     }
7501
7502     MovDir[x][y] = MV_NONE;
7503     if (attr_x < x)
7504       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7505     else if (attr_x > x)
7506       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7507     if (attr_y < y)
7508       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7509     else if (attr_y > y)
7510       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7511
7512     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7513
7514     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7515     {
7516       boolean first_horiz = RND(2);
7517       int new_move_dir = MovDir[x][y];
7518
7519       if (element_info[element].move_stepsize == 0)     // "not moving"
7520       {
7521         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7522         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7523
7524         return;
7525       }
7526
7527       MovDir[x][y] =
7528         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529       Moving2Blocked(x, y, &newx, &newy);
7530
7531       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7532         return;
7533
7534       MovDir[x][y] =
7535         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7536       Moving2Blocked(x, y, &newx, &newy);
7537
7538       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7539         return;
7540
7541       MovDir[x][y] = old_move_dir;
7542     }
7543   }
7544   else if (move_pattern == MV_WHEN_PUSHED ||
7545            move_pattern == MV_WHEN_DROPPED)
7546   {
7547     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7548       MovDir[x][y] = MV_NONE;
7549
7550     MovDelay[x][y] = 0;
7551   }
7552   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7553   {
7554     static int test_xy[7][2] =
7555     {
7556       { 0, -1 },
7557       { -1, 0 },
7558       { +1, 0 },
7559       { 0, +1 },
7560       { 0, -1 },
7561       { -1, 0 },
7562       { +1, 0 },
7563     };
7564     static int test_dir[7] =
7565     {
7566       MV_UP,
7567       MV_LEFT,
7568       MV_RIGHT,
7569       MV_DOWN,
7570       MV_UP,
7571       MV_LEFT,
7572       MV_RIGHT,
7573     };
7574     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7575     int move_preference = -1000000;     // start with very low preference
7576     int new_move_dir = MV_NONE;
7577     int start_test = RND(4);
7578     int i;
7579
7580     for (i = 0; i < NUM_DIRECTIONS; i++)
7581     {
7582       int move_dir = test_dir[start_test + i];
7583       int move_dir_preference;
7584
7585       xx = x + test_xy[start_test + i][0];
7586       yy = y + test_xy[start_test + i][1];
7587
7588       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7589           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7590       {
7591         new_move_dir = move_dir;
7592
7593         break;
7594       }
7595
7596       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7597         continue;
7598
7599       move_dir_preference = -1 * RunnerVisit[xx][yy];
7600       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7601         move_dir_preference = PlayerVisit[xx][yy];
7602
7603       if (move_dir_preference > move_preference)
7604       {
7605         // prefer field that has not been visited for the longest time
7606         move_preference = move_dir_preference;
7607         new_move_dir = move_dir;
7608       }
7609       else if (move_dir_preference == move_preference &&
7610                move_dir == old_move_dir)
7611       {
7612         // prefer last direction when all directions are preferred equally
7613         move_preference = move_dir_preference;
7614         new_move_dir = move_dir;
7615       }
7616     }
7617
7618     MovDir[x][y] = new_move_dir;
7619     if (old_move_dir != new_move_dir)
7620       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7621   }
7622 }
7623
7624 static void TurnRound(int x, int y)
7625 {
7626   int direction = MovDir[x][y];
7627
7628   TurnRoundExt(x, y);
7629
7630   GfxDir[x][y] = MovDir[x][y];
7631
7632   if (direction != MovDir[x][y])
7633     GfxFrame[x][y] = 0;
7634
7635   if (MovDelay[x][y])
7636     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7637
7638   ResetGfxFrame(x, y);
7639 }
7640
7641 static boolean JustBeingPushed(int x, int y)
7642 {
7643   int i;
7644
7645   for (i = 0; i < MAX_PLAYERS; i++)
7646   {
7647     struct PlayerInfo *player = &stored_player[i];
7648
7649     if (player->active && player->is_pushing && player->MovPos)
7650     {
7651       int next_jx = player->jx + (player->jx - player->last_jx);
7652       int next_jy = player->jy + (player->jy - player->last_jy);
7653
7654       if (x == next_jx && y == next_jy)
7655         return TRUE;
7656     }
7657   }
7658
7659   return FALSE;
7660 }
7661
7662 static void StartMoving(int x, int y)
7663 {
7664   boolean started_moving = FALSE;       // some elements can fall _and_ move
7665   int element = Tile[x][y];
7666
7667   if (Stop[x][y])
7668     return;
7669
7670   if (MovDelay[x][y] == 0)
7671     GfxAction[x][y] = ACTION_DEFAULT;
7672
7673   if (CAN_FALL(element) && y < lev_fieldy - 1)
7674   {
7675     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7676         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7677       if (JustBeingPushed(x, y))
7678         return;
7679
7680     if (element == EL_QUICKSAND_FULL)
7681     {
7682       if (IS_FREE(x, y + 1))
7683       {
7684         InitMovingField(x, y, MV_DOWN);
7685         started_moving = TRUE;
7686
7687         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7688 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7689         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7690           Store[x][y] = EL_ROCK;
7691 #else
7692         Store[x][y] = EL_ROCK;
7693 #endif
7694
7695         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7696       }
7697       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7698       {
7699         if (!MovDelay[x][y])
7700         {
7701           MovDelay[x][y] = TILEY + 1;
7702
7703           ResetGfxAnimation(x, y);
7704           ResetGfxAnimation(x, y + 1);
7705         }
7706
7707         if (MovDelay[x][y])
7708         {
7709           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7710           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7711
7712           MovDelay[x][y]--;
7713           if (MovDelay[x][y])
7714             return;
7715         }
7716
7717         Tile[x][y] = EL_QUICKSAND_EMPTY;
7718         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7719         Store[x][y + 1] = Store[x][y];
7720         Store[x][y] = 0;
7721
7722         PlayLevelSoundAction(x, y, ACTION_FILLING);
7723       }
7724       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7725       {
7726         if (!MovDelay[x][y])
7727         {
7728           MovDelay[x][y] = TILEY + 1;
7729
7730           ResetGfxAnimation(x, y);
7731           ResetGfxAnimation(x, y + 1);
7732         }
7733
7734         if (MovDelay[x][y])
7735         {
7736           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7737           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7738
7739           MovDelay[x][y]--;
7740           if (MovDelay[x][y])
7741             return;
7742         }
7743
7744         Tile[x][y] = EL_QUICKSAND_EMPTY;
7745         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7746         Store[x][y + 1] = Store[x][y];
7747         Store[x][y] = 0;
7748
7749         PlayLevelSoundAction(x, y, ACTION_FILLING);
7750       }
7751     }
7752     else if (element == EL_QUICKSAND_FAST_FULL)
7753     {
7754       if (IS_FREE(x, y + 1))
7755       {
7756         InitMovingField(x, y, MV_DOWN);
7757         started_moving = TRUE;
7758
7759         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7760 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7761         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7762           Store[x][y] = EL_ROCK;
7763 #else
7764         Store[x][y] = EL_ROCK;
7765 #endif
7766
7767         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7768       }
7769       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7770       {
7771         if (!MovDelay[x][y])
7772         {
7773           MovDelay[x][y] = TILEY + 1;
7774
7775           ResetGfxAnimation(x, y);
7776           ResetGfxAnimation(x, y + 1);
7777         }
7778
7779         if (MovDelay[x][y])
7780         {
7781           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7782           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7783
7784           MovDelay[x][y]--;
7785           if (MovDelay[x][y])
7786             return;
7787         }
7788
7789         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7790         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7791         Store[x][y + 1] = Store[x][y];
7792         Store[x][y] = 0;
7793
7794         PlayLevelSoundAction(x, y, ACTION_FILLING);
7795       }
7796       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7797       {
7798         if (!MovDelay[x][y])
7799         {
7800           MovDelay[x][y] = TILEY + 1;
7801
7802           ResetGfxAnimation(x, y);
7803           ResetGfxAnimation(x, y + 1);
7804         }
7805
7806         if (MovDelay[x][y])
7807         {
7808           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7809           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7810
7811           MovDelay[x][y]--;
7812           if (MovDelay[x][y])
7813             return;
7814         }
7815
7816         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7817         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7818         Store[x][y + 1] = Store[x][y];
7819         Store[x][y] = 0;
7820
7821         PlayLevelSoundAction(x, y, ACTION_FILLING);
7822       }
7823     }
7824     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7825              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7826     {
7827       InitMovingField(x, y, MV_DOWN);
7828       started_moving = TRUE;
7829
7830       Tile[x][y] = EL_QUICKSAND_FILLING;
7831       Store[x][y] = element;
7832
7833       PlayLevelSoundAction(x, y, ACTION_FILLING);
7834     }
7835     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7836              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7837     {
7838       InitMovingField(x, y, MV_DOWN);
7839       started_moving = TRUE;
7840
7841       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7842       Store[x][y] = element;
7843
7844       PlayLevelSoundAction(x, y, ACTION_FILLING);
7845     }
7846     else if (element == EL_MAGIC_WALL_FULL)
7847     {
7848       if (IS_FREE(x, y + 1))
7849       {
7850         InitMovingField(x, y, MV_DOWN);
7851         started_moving = TRUE;
7852
7853         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7854         Store[x][y] = EL_CHANGED(Store[x][y]);
7855       }
7856       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7857       {
7858         if (!MovDelay[x][y])
7859           MovDelay[x][y] = TILEY / 4 + 1;
7860
7861         if (MovDelay[x][y])
7862         {
7863           MovDelay[x][y]--;
7864           if (MovDelay[x][y])
7865             return;
7866         }
7867
7868         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7869         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7870         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7871         Store[x][y] = 0;
7872       }
7873     }
7874     else if (element == EL_BD_MAGIC_WALL_FULL)
7875     {
7876       if (IS_FREE(x, y + 1))
7877       {
7878         InitMovingField(x, y, MV_DOWN);
7879         started_moving = TRUE;
7880
7881         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7882         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7883       }
7884       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7885       {
7886         if (!MovDelay[x][y])
7887           MovDelay[x][y] = TILEY / 4 + 1;
7888
7889         if (MovDelay[x][y])
7890         {
7891           MovDelay[x][y]--;
7892           if (MovDelay[x][y])
7893             return;
7894         }
7895
7896         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7897         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7898         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7899         Store[x][y] = 0;
7900       }
7901     }
7902     else if (element == EL_DC_MAGIC_WALL_FULL)
7903     {
7904       if (IS_FREE(x, y + 1))
7905       {
7906         InitMovingField(x, y, MV_DOWN);
7907         started_moving = TRUE;
7908
7909         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7910         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7911       }
7912       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7913       {
7914         if (!MovDelay[x][y])
7915           MovDelay[x][y] = TILEY / 4 + 1;
7916
7917         if (MovDelay[x][y])
7918         {
7919           MovDelay[x][y]--;
7920           if (MovDelay[x][y])
7921             return;
7922         }
7923
7924         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7925         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7926         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7927         Store[x][y] = 0;
7928       }
7929     }
7930     else if ((CAN_PASS_MAGIC_WALL(element) &&
7931               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7932                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7933              (CAN_PASS_DC_MAGIC_WALL(element) &&
7934               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7935
7936     {
7937       InitMovingField(x, y, MV_DOWN);
7938       started_moving = TRUE;
7939
7940       Tile[x][y] =
7941         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7942          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7943          EL_DC_MAGIC_WALL_FILLING);
7944       Store[x][y] = element;
7945     }
7946     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7947     {
7948       SplashAcid(x, y + 1);
7949
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952
7953       Store[x][y] = EL_ACID;
7954     }
7955     else if (
7956              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7957               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7958              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7959               CAN_FALL(element) && WasJustFalling[x][y] &&
7960               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7961
7962              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7963               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7964               (Tile[x][y + 1] == EL_BLOCKED)))
7965     {
7966       /* this is needed for a special case not covered by calling "Impact()"
7967          from "ContinueMoving()": if an element moves to a tile directly below
7968          another element which was just falling on that tile (which was empty
7969          in the previous frame), the falling element above would just stop
7970          instead of smashing the element below (in previous version, the above
7971          element was just checked for "moving" instead of "falling", resulting
7972          in incorrect smashes caused by horizontal movement of the above
7973          element; also, the case of the player being the element to smash was
7974          simply not covered here... :-/ ) */
7975
7976       CheckCollision[x][y] = 0;
7977       CheckImpact[x][y] = 0;
7978
7979       Impact(x, y);
7980     }
7981     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7982     {
7983       if (MovDir[x][y] == MV_NONE)
7984       {
7985         InitMovingField(x, y, MV_DOWN);
7986         started_moving = TRUE;
7987       }
7988     }
7989     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7990     {
7991       if (WasJustFalling[x][y]) // prevent animation from being restarted
7992         MovDir[x][y] = MV_DOWN;
7993
7994       InitMovingField(x, y, MV_DOWN);
7995       started_moving = TRUE;
7996     }
7997     else if (element == EL_AMOEBA_DROP)
7998     {
7999       Tile[x][y] = EL_AMOEBA_GROWING;
8000       Store[x][y] = EL_AMOEBA_WET;
8001     }
8002     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8003               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8004              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8005              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8006     {
8007       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8008                                 (IS_FREE(x - 1, y + 1) ||
8009                                  Tile[x - 1][y + 1] == EL_ACID));
8010       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8011                                 (IS_FREE(x + 1, y + 1) ||
8012                                  Tile[x + 1][y + 1] == EL_ACID));
8013       boolean can_fall_any  = (can_fall_left || can_fall_right);
8014       boolean can_fall_both = (can_fall_left && can_fall_right);
8015       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8016
8017       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8018       {
8019         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8020           can_fall_right = FALSE;
8021         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8022           can_fall_left = FALSE;
8023         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8024           can_fall_right = FALSE;
8025         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8026           can_fall_left = FALSE;
8027
8028         can_fall_any  = (can_fall_left || can_fall_right);
8029         can_fall_both = FALSE;
8030       }
8031
8032       if (can_fall_both)
8033       {
8034         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8035           can_fall_right = FALSE;       // slip down on left side
8036         else
8037           can_fall_left = !(can_fall_right = RND(2));
8038
8039         can_fall_both = FALSE;
8040       }
8041
8042       if (can_fall_any)
8043       {
8044         // if not determined otherwise, prefer left side for slipping down
8045         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8046         started_moving = TRUE;
8047       }
8048     }
8049     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8050     {
8051       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8052       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8053       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8054       int belt_dir = game.belt_dir[belt_nr];
8055
8056       if ((belt_dir == MV_LEFT  && left_is_free) ||
8057           (belt_dir == MV_RIGHT && right_is_free))
8058       {
8059         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8060
8061         InitMovingField(x, y, belt_dir);
8062         started_moving = TRUE;
8063
8064         Pushed[x][y] = TRUE;
8065         Pushed[nextx][y] = TRUE;
8066
8067         GfxAction[x][y] = ACTION_DEFAULT;
8068       }
8069       else
8070       {
8071         MovDir[x][y] = 0;       // if element was moving, stop it
8072       }
8073     }
8074   }
8075
8076   // not "else if" because of elements that can fall and move (EL_SPRING)
8077   if (CAN_MOVE(element) && !started_moving)
8078   {
8079     int move_pattern = element_info[element].move_pattern;
8080     int newx, newy;
8081
8082     Moving2Blocked(x, y, &newx, &newy);
8083
8084     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8085       return;
8086
8087     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8088         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8089     {
8090       WasJustMoving[x][y] = 0;
8091       CheckCollision[x][y] = 0;
8092
8093       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8094
8095       if (Tile[x][y] != element)        // element has changed
8096         return;
8097     }
8098
8099     if (!MovDelay[x][y])        // start new movement phase
8100     {
8101       // all objects that can change their move direction after each step
8102       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8103
8104       if (element != EL_YAMYAM &&
8105           element != EL_DARK_YAMYAM &&
8106           element != EL_PACMAN &&
8107           !(move_pattern & MV_ANY_DIRECTION) &&
8108           move_pattern != MV_TURNING_LEFT &&
8109           move_pattern != MV_TURNING_RIGHT &&
8110           move_pattern != MV_TURNING_LEFT_RIGHT &&
8111           move_pattern != MV_TURNING_RIGHT_LEFT &&
8112           move_pattern != MV_TURNING_RANDOM)
8113       {
8114         TurnRound(x, y);
8115
8116         if (MovDelay[x][y] && (element == EL_BUG ||
8117                                element == EL_SPACESHIP ||
8118                                element == EL_SP_SNIKSNAK ||
8119                                element == EL_SP_ELECTRON ||
8120                                element == EL_MOLE))
8121           TEST_DrawLevelField(x, y);
8122       }
8123     }
8124
8125     if (MovDelay[x][y])         // wait some time before next movement
8126     {
8127       MovDelay[x][y]--;
8128
8129       if (element == EL_ROBOT ||
8130           element == EL_YAMYAM ||
8131           element == EL_DARK_YAMYAM)
8132       {
8133         DrawLevelElementAnimationIfNeeded(x, y, element);
8134         PlayLevelSoundAction(x, y, ACTION_WAITING);
8135       }
8136       else if (element == EL_SP_ELECTRON)
8137         DrawLevelElementAnimationIfNeeded(x, y, element);
8138       else if (element == EL_DRAGON)
8139       {
8140         int i;
8141         int dir = MovDir[x][y];
8142         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8143         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8144         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8145                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8146                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8147                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8148         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8149
8150         GfxAction[x][y] = ACTION_ATTACKING;
8151
8152         if (IS_PLAYER(x, y))
8153           DrawPlayerField(x, y);
8154         else
8155           TEST_DrawLevelField(x, y);
8156
8157         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8158
8159         for (i = 1; i <= 3; i++)
8160         {
8161           int xx = x + i * dx;
8162           int yy = y + i * dy;
8163           int sx = SCREENX(xx);
8164           int sy = SCREENY(yy);
8165           int flame_graphic = graphic + (i - 1);
8166
8167           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8168             break;
8169
8170           if (MovDelay[x][y])
8171           {
8172             int flamed = MovingOrBlocked2Element(xx, yy);
8173
8174             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8175               Bang(xx, yy);
8176             else
8177               RemoveMovingField(xx, yy);
8178
8179             ChangeDelay[xx][yy] = 0;
8180
8181             Tile[xx][yy] = EL_FLAMES;
8182
8183             if (IN_SCR_FIELD(sx, sy))
8184             {
8185               TEST_DrawLevelFieldCrumbled(xx, yy);
8186               DrawGraphic(sx, sy, flame_graphic, frame);
8187             }
8188           }
8189           else
8190           {
8191             if (Tile[xx][yy] == EL_FLAMES)
8192               Tile[xx][yy] = EL_EMPTY;
8193             TEST_DrawLevelField(xx, yy);
8194           }
8195         }
8196       }
8197
8198       if (MovDelay[x][y])       // element still has to wait some time
8199       {
8200         PlayLevelSoundAction(x, y, ACTION_WAITING);
8201
8202         return;
8203       }
8204     }
8205
8206     // now make next step
8207
8208     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8209
8210     if (DONT_COLLIDE_WITH(element) &&
8211         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8212         !PLAYER_ENEMY_PROTECTED(newx, newy))
8213     {
8214       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8215
8216       return;
8217     }
8218
8219     else if (CAN_MOVE_INTO_ACID(element) &&
8220              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8221              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8222              (MovDir[x][y] == MV_DOWN ||
8223               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8224     {
8225       SplashAcid(newx, newy);
8226       Store[x][y] = EL_ACID;
8227     }
8228     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8229     {
8230       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8231           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8232           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8233           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8234       {
8235         RemoveField(x, y);
8236         TEST_DrawLevelField(x, y);
8237
8238         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8239         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8240           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8241
8242         game.friends_still_needed--;
8243         if (!game.friends_still_needed &&
8244             !game.GameOver &&
8245             game.all_players_gone)
8246           LevelSolved();
8247
8248         return;
8249       }
8250       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8251       {
8252         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8253           TEST_DrawLevelField(newx, newy);
8254         else
8255           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8256       }
8257       else if (!IS_FREE(newx, newy))
8258       {
8259         GfxAction[x][y] = ACTION_WAITING;
8260
8261         if (IS_PLAYER(x, y))
8262           DrawPlayerField(x, y);
8263         else
8264           TEST_DrawLevelField(x, y);
8265
8266         return;
8267       }
8268     }
8269     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8270     {
8271       if (IS_FOOD_PIG(Tile[newx][newy]))
8272       {
8273         if (IS_MOVING(newx, newy))
8274           RemoveMovingField(newx, newy);
8275         else
8276         {
8277           Tile[newx][newy] = EL_EMPTY;
8278           TEST_DrawLevelField(newx, newy);
8279         }
8280
8281         PlayLevelSound(x, y, SND_PIG_DIGGING);
8282       }
8283       else if (!IS_FREE(newx, newy))
8284       {
8285         if (IS_PLAYER(x, y))
8286           DrawPlayerField(x, y);
8287         else
8288           TEST_DrawLevelField(x, y);
8289
8290         return;
8291       }
8292     }
8293     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8294     {
8295       if (Store[x][y] != EL_EMPTY)
8296       {
8297         boolean can_clone = FALSE;
8298         int xx, yy;
8299
8300         // check if element to clone is still there
8301         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8302         {
8303           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8304           {
8305             can_clone = TRUE;
8306
8307             break;
8308           }
8309         }
8310
8311         // cannot clone or target field not free anymore -- do not clone
8312         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8313           Store[x][y] = EL_EMPTY;
8314       }
8315
8316       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8317       {
8318         if (IS_MV_DIAGONAL(MovDir[x][y]))
8319         {
8320           int diagonal_move_dir = MovDir[x][y];
8321           int stored = Store[x][y];
8322           int change_delay = 8;
8323           int graphic;
8324
8325           // android is moving diagonally
8326
8327           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8328
8329           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8330           GfxElement[x][y] = EL_EMC_ANDROID;
8331           GfxAction[x][y] = ACTION_SHRINKING;
8332           GfxDir[x][y] = diagonal_move_dir;
8333           ChangeDelay[x][y] = change_delay;
8334
8335           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8336                                    GfxDir[x][y]);
8337
8338           DrawLevelGraphicAnimation(x, y, graphic);
8339           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8340
8341           if (Tile[newx][newy] == EL_ACID)
8342           {
8343             SplashAcid(newx, newy);
8344
8345             return;
8346           }
8347
8348           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8349
8350           Store[newx][newy] = EL_EMC_ANDROID;
8351           GfxElement[newx][newy] = EL_EMC_ANDROID;
8352           GfxAction[newx][newy] = ACTION_GROWING;
8353           GfxDir[newx][newy] = diagonal_move_dir;
8354           ChangeDelay[newx][newy] = change_delay;
8355
8356           graphic = el_act_dir2img(GfxElement[newx][newy],
8357                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8358
8359           DrawLevelGraphicAnimation(newx, newy, graphic);
8360           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8361
8362           return;
8363         }
8364         else
8365         {
8366           Tile[newx][newy] = EL_EMPTY;
8367           TEST_DrawLevelField(newx, newy);
8368
8369           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8370         }
8371       }
8372       else if (!IS_FREE(newx, newy))
8373       {
8374         return;
8375       }
8376     }
8377     else if (IS_CUSTOM_ELEMENT(element) &&
8378              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8379     {
8380       if (!DigFieldByCE(newx, newy, element))
8381         return;
8382
8383       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8384       {
8385         RunnerVisit[x][y] = FrameCounter;
8386         PlayerVisit[x][y] /= 8;         // expire player visit path
8387       }
8388     }
8389     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8390     {
8391       if (!IS_FREE(newx, newy))
8392       {
8393         if (IS_PLAYER(x, y))
8394           DrawPlayerField(x, y);
8395         else
8396           TEST_DrawLevelField(x, y);
8397
8398         return;
8399       }
8400       else
8401       {
8402         boolean wanna_flame = !RND(10);
8403         int dx = newx - x, dy = newy - y;
8404         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8405         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8406         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8407                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8408         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8409                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8410
8411         if ((wanna_flame ||
8412              IS_CLASSIC_ENEMY(element1) ||
8413              IS_CLASSIC_ENEMY(element2)) &&
8414             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8415             element1 != EL_FLAMES && element2 != EL_FLAMES)
8416         {
8417           ResetGfxAnimation(x, y);
8418           GfxAction[x][y] = ACTION_ATTACKING;
8419
8420           if (IS_PLAYER(x, y))
8421             DrawPlayerField(x, y);
8422           else
8423             TEST_DrawLevelField(x, y);
8424
8425           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8426
8427           MovDelay[x][y] = 50;
8428
8429           Tile[newx][newy] = EL_FLAMES;
8430           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8431             Tile[newx1][newy1] = EL_FLAMES;
8432           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8433             Tile[newx2][newy2] = EL_FLAMES;
8434
8435           return;
8436         }
8437       }
8438     }
8439     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8440              Tile[newx][newy] == EL_DIAMOND)
8441     {
8442       if (IS_MOVING(newx, newy))
8443         RemoveMovingField(newx, newy);
8444       else
8445       {
8446         Tile[newx][newy] = EL_EMPTY;
8447         TEST_DrawLevelField(newx, newy);
8448       }
8449
8450       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8451     }
8452     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8453              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8454     {
8455       if (AmoebaNr[newx][newy])
8456       {
8457         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8458         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8459             Tile[newx][newy] == EL_BD_AMOEBA)
8460           AmoebaCnt[AmoebaNr[newx][newy]]--;
8461       }
8462
8463       if (IS_MOVING(newx, newy))
8464       {
8465         RemoveMovingField(newx, newy);
8466       }
8467       else
8468       {
8469         Tile[newx][newy] = EL_EMPTY;
8470         TEST_DrawLevelField(newx, newy);
8471       }
8472
8473       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8474     }
8475     else if ((element == EL_PACMAN || element == EL_MOLE)
8476              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8477     {
8478       if (AmoebaNr[newx][newy])
8479       {
8480         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8481         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8482             Tile[newx][newy] == EL_BD_AMOEBA)
8483           AmoebaCnt[AmoebaNr[newx][newy]]--;
8484       }
8485
8486       if (element == EL_MOLE)
8487       {
8488         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8489         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8490
8491         ResetGfxAnimation(x, y);
8492         GfxAction[x][y] = ACTION_DIGGING;
8493         TEST_DrawLevelField(x, y);
8494
8495         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8496
8497         return;                         // wait for shrinking amoeba
8498       }
8499       else      // element == EL_PACMAN
8500       {
8501         Tile[newx][newy] = EL_EMPTY;
8502         TEST_DrawLevelField(newx, newy);
8503         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8504       }
8505     }
8506     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8507              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8508               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8509     {
8510       // wait for shrinking amoeba to completely disappear
8511       return;
8512     }
8513     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8514     {
8515       // object was running against a wall
8516
8517       TurnRound(x, y);
8518
8519       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8520         DrawLevelElementAnimation(x, y, element);
8521
8522       if (DONT_TOUCH(element))
8523         TestIfBadThingTouchesPlayer(x, y);
8524
8525       return;
8526     }
8527
8528     InitMovingField(x, y, MovDir[x][y]);
8529
8530     PlayLevelSoundAction(x, y, ACTION_MOVING);
8531   }
8532
8533   if (MovDir[x][y])
8534     ContinueMoving(x, y);
8535 }
8536
8537 void ContinueMoving(int x, int y)
8538 {
8539   int element = Tile[x][y];
8540   struct ElementInfo *ei = &element_info[element];
8541   int direction = MovDir[x][y];
8542   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8543   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8544   int newx = x + dx, newy = y + dy;
8545   int stored = Store[x][y];
8546   int stored_new = Store[newx][newy];
8547   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8548   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8549   boolean last_line = (newy == lev_fieldy - 1);
8550   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8551
8552   if (pushed_by_player)         // special case: moving object pushed by player
8553   {
8554     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8555   }
8556   else if (use_step_delay)      // special case: moving object has step delay
8557   {
8558     if (!MovDelay[x][y])
8559       MovPos[x][y] += getElementMoveStepsize(x, y);
8560
8561     if (MovDelay[x][y])
8562       MovDelay[x][y]--;
8563     else
8564       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8565
8566     if (MovDelay[x][y])
8567     {
8568       TEST_DrawLevelField(x, y);
8569
8570       return;   // element is still waiting
8571     }
8572   }
8573   else                          // normal case: generically moving object
8574   {
8575     MovPos[x][y] += getElementMoveStepsize(x, y);
8576   }
8577
8578   if (ABS(MovPos[x][y]) < TILEX)
8579   {
8580     TEST_DrawLevelField(x, y);
8581
8582     return;     // element is still moving
8583   }
8584
8585   // element reached destination field
8586
8587   Tile[x][y] = EL_EMPTY;
8588   Tile[newx][newy] = element;
8589   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8590
8591   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8592   {
8593     element = Tile[newx][newy] = EL_ACID;
8594   }
8595   else if (element == EL_MOLE)
8596   {
8597     Tile[x][y] = EL_SAND;
8598
8599     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8600   }
8601   else if (element == EL_QUICKSAND_FILLING)
8602   {
8603     element = Tile[newx][newy] = get_next_element(element);
8604     Store[newx][newy] = Store[x][y];
8605   }
8606   else if (element == EL_QUICKSAND_EMPTYING)
8607   {
8608     Tile[x][y] = get_next_element(element);
8609     element = Tile[newx][newy] = Store[x][y];
8610   }
8611   else if (element == EL_QUICKSAND_FAST_FILLING)
8612   {
8613     element = Tile[newx][newy] = get_next_element(element);
8614     Store[newx][newy] = Store[x][y];
8615   }
8616   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8617   {
8618     Tile[x][y] = get_next_element(element);
8619     element = Tile[newx][newy] = Store[x][y];
8620   }
8621   else if (element == EL_MAGIC_WALL_FILLING)
8622   {
8623     element = Tile[newx][newy] = get_next_element(element);
8624     if (!game.magic_wall_active)
8625       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8626     Store[newx][newy] = Store[x][y];
8627   }
8628   else if (element == EL_MAGIC_WALL_EMPTYING)
8629   {
8630     Tile[x][y] = get_next_element(element);
8631     if (!game.magic_wall_active)
8632       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8633     element = Tile[newx][newy] = Store[x][y];
8634
8635     InitField(newx, newy, FALSE);
8636   }
8637   else if (element == EL_BD_MAGIC_WALL_FILLING)
8638   {
8639     element = Tile[newx][newy] = get_next_element(element);
8640     if (!game.magic_wall_active)
8641       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8642     Store[newx][newy] = Store[x][y];
8643   }
8644   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8645   {
8646     Tile[x][y] = get_next_element(element);
8647     if (!game.magic_wall_active)
8648       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8649     element = Tile[newx][newy] = Store[x][y];
8650
8651     InitField(newx, newy, FALSE);
8652   }
8653   else if (element == EL_DC_MAGIC_WALL_FILLING)
8654   {
8655     element = Tile[newx][newy] = get_next_element(element);
8656     if (!game.magic_wall_active)
8657       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8658     Store[newx][newy] = Store[x][y];
8659   }
8660   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8661   {
8662     Tile[x][y] = get_next_element(element);
8663     if (!game.magic_wall_active)
8664       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8665     element = Tile[newx][newy] = Store[x][y];
8666
8667     InitField(newx, newy, FALSE);
8668   }
8669   else if (element == EL_AMOEBA_DROPPING)
8670   {
8671     Tile[x][y] = get_next_element(element);
8672     element = Tile[newx][newy] = Store[x][y];
8673   }
8674   else if (element == EL_SOKOBAN_OBJECT)
8675   {
8676     if (Back[x][y])
8677       Tile[x][y] = Back[x][y];
8678
8679     if (Back[newx][newy])
8680       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8681
8682     Back[x][y] = Back[newx][newy] = 0;
8683   }
8684
8685   Store[x][y] = EL_EMPTY;
8686   MovPos[x][y] = 0;
8687   MovDir[x][y] = 0;
8688   MovDelay[x][y] = 0;
8689
8690   MovDelay[newx][newy] = 0;
8691
8692   if (CAN_CHANGE_OR_HAS_ACTION(element))
8693   {
8694     // copy element change control values to new field
8695     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8696     ChangePage[newx][newy]  = ChangePage[x][y];
8697     ChangeCount[newx][newy] = ChangeCount[x][y];
8698     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8699   }
8700
8701   CustomValue[newx][newy] = CustomValue[x][y];
8702
8703   ChangeDelay[x][y] = 0;
8704   ChangePage[x][y] = -1;
8705   ChangeCount[x][y] = 0;
8706   ChangeEvent[x][y] = -1;
8707
8708   CustomValue[x][y] = 0;
8709
8710   // copy animation control values to new field
8711   GfxFrame[newx][newy]  = GfxFrame[x][y];
8712   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8713   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8714   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8715
8716   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8717
8718   // some elements can leave other elements behind after moving
8719   if (ei->move_leave_element != EL_EMPTY &&
8720       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8721       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8722   {
8723     int move_leave_element = ei->move_leave_element;
8724
8725     // this makes it possible to leave the removed element again
8726     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8727       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8728
8729     Tile[x][y] = move_leave_element;
8730
8731     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8732       MovDir[x][y] = direction;
8733
8734     InitField(x, y, FALSE);
8735
8736     if (GFX_CRUMBLED(Tile[x][y]))
8737       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8738
8739     if (ELEM_IS_PLAYER(move_leave_element))
8740       RelocatePlayer(x, y, move_leave_element);
8741   }
8742
8743   // do this after checking for left-behind element
8744   ResetGfxAnimation(x, y);      // reset animation values for old field
8745
8746   if (!CAN_MOVE(element) ||
8747       (CAN_FALL(element) && direction == MV_DOWN &&
8748        (element == EL_SPRING ||
8749         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8750         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8751     GfxDir[x][y] = MovDir[newx][newy] = 0;
8752
8753   TEST_DrawLevelField(x, y);
8754   TEST_DrawLevelField(newx, newy);
8755
8756   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8757
8758   // prevent pushed element from moving on in pushed direction
8759   if (pushed_by_player && CAN_MOVE(element) &&
8760       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8761       !(element_info[element].move_pattern & direction))
8762     TurnRound(newx, newy);
8763
8764   // prevent elements on conveyor belt from moving on in last direction
8765   if (pushed_by_conveyor && CAN_FALL(element) &&
8766       direction & MV_HORIZONTAL)
8767     MovDir[newx][newy] = 0;
8768
8769   if (!pushed_by_player)
8770   {
8771     int nextx = newx + dx, nexty = newy + dy;
8772     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8773
8774     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8775
8776     if (CAN_FALL(element) && direction == MV_DOWN)
8777       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8778
8779     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8780       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8781
8782     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8783       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8784   }
8785
8786   if (DONT_TOUCH(element))      // object may be nasty to player or others
8787   {
8788     TestIfBadThingTouchesPlayer(newx, newy);
8789     TestIfBadThingTouchesFriend(newx, newy);
8790
8791     if (!IS_CUSTOM_ELEMENT(element))
8792       TestIfBadThingTouchesOtherBadThing(newx, newy);
8793   }
8794   else if (element == EL_PENGUIN)
8795     TestIfFriendTouchesBadThing(newx, newy);
8796
8797   if (DONT_GET_HIT_BY(element))
8798   {
8799     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8800   }
8801
8802   // give the player one last chance (one more frame) to move away
8803   if (CAN_FALL(element) && direction == MV_DOWN &&
8804       (last_line || (!IS_FREE(x, newy + 1) &&
8805                      (!IS_PLAYER(x, newy + 1) ||
8806                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8807     Impact(x, newy);
8808
8809   if (pushed_by_player && !game.use_change_when_pushing_bug)
8810   {
8811     int push_side = MV_DIR_OPPOSITE(direction);
8812     struct PlayerInfo *player = PLAYERINFO(x, y);
8813
8814     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8815                                player->index_bit, push_side);
8816     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8817                                         player->index_bit, push_side);
8818   }
8819
8820   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8821     MovDelay[newx][newy] = 1;
8822
8823   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8824
8825   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8826   TestIfElementHitsCustomElement(newx, newy, direction);
8827   TestIfPlayerTouchesCustomElement(newx, newy);
8828   TestIfElementTouchesCustomElement(newx, newy);
8829
8830   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8831       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8832     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8833                              MV_DIR_OPPOSITE(direction));
8834 }
8835
8836 int AmoebaNeighbourNr(int ax, int ay)
8837 {
8838   int i;
8839   int element = Tile[ax][ay];
8840   int group_nr = 0;
8841   static int xy[4][2] =
8842   {
8843     { 0, -1 },
8844     { -1, 0 },
8845     { +1, 0 },
8846     { 0, +1 }
8847   };
8848
8849   for (i = 0; i < NUM_DIRECTIONS; i++)
8850   {
8851     int x = ax + xy[i][0];
8852     int y = ay + xy[i][1];
8853
8854     if (!IN_LEV_FIELD(x, y))
8855       continue;
8856
8857     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8858       group_nr = AmoebaNr[x][y];
8859   }
8860
8861   return group_nr;
8862 }
8863
8864 static void AmoebaMerge(int ax, int ay)
8865 {
8866   int i, x, y, xx, yy;
8867   int new_group_nr = AmoebaNr[ax][ay];
8868   static int xy[4][2] =
8869   {
8870     { 0, -1 },
8871     { -1, 0 },
8872     { +1, 0 },
8873     { 0, +1 }
8874   };
8875
8876   if (new_group_nr == 0)
8877     return;
8878
8879   for (i = 0; i < NUM_DIRECTIONS; i++)
8880   {
8881     x = ax + xy[i][0];
8882     y = ay + xy[i][1];
8883
8884     if (!IN_LEV_FIELD(x, y))
8885       continue;
8886
8887     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8888          Tile[x][y] == EL_BD_AMOEBA ||
8889          Tile[x][y] == EL_AMOEBA_DEAD) &&
8890         AmoebaNr[x][y] != new_group_nr)
8891     {
8892       int old_group_nr = AmoebaNr[x][y];
8893
8894       if (old_group_nr == 0)
8895         return;
8896
8897       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8898       AmoebaCnt[old_group_nr] = 0;
8899       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8900       AmoebaCnt2[old_group_nr] = 0;
8901
8902       SCAN_PLAYFIELD(xx, yy)
8903       {
8904         if (AmoebaNr[xx][yy] == old_group_nr)
8905           AmoebaNr[xx][yy] = new_group_nr;
8906       }
8907     }
8908   }
8909 }
8910
8911 void AmoebaToDiamond(int ax, int ay)
8912 {
8913   int i, x, y;
8914
8915   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8916   {
8917     int group_nr = AmoebaNr[ax][ay];
8918
8919 #ifdef DEBUG
8920     if (group_nr == 0)
8921     {
8922       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8923       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8924
8925       return;
8926     }
8927 #endif
8928
8929     SCAN_PLAYFIELD(x, y)
8930     {
8931       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8932       {
8933         AmoebaNr[x][y] = 0;
8934         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8935       }
8936     }
8937
8938     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8939                             SND_AMOEBA_TURNING_TO_GEM :
8940                             SND_AMOEBA_TURNING_TO_ROCK));
8941     Bang(ax, ay);
8942   }
8943   else
8944   {
8945     static int xy[4][2] =
8946     {
8947       { 0, -1 },
8948       { -1, 0 },
8949       { +1, 0 },
8950       { 0, +1 }
8951     };
8952
8953     for (i = 0; i < NUM_DIRECTIONS; i++)
8954     {
8955       x = ax + xy[i][0];
8956       y = ay + xy[i][1];
8957
8958       if (!IN_LEV_FIELD(x, y))
8959         continue;
8960
8961       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8962       {
8963         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8964                               SND_AMOEBA_TURNING_TO_GEM :
8965                               SND_AMOEBA_TURNING_TO_ROCK));
8966         Bang(x, y);
8967       }
8968     }
8969   }
8970 }
8971
8972 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8973 {
8974   int x, y;
8975   int group_nr = AmoebaNr[ax][ay];
8976   boolean done = FALSE;
8977
8978 #ifdef DEBUG
8979   if (group_nr == 0)
8980   {
8981     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8982     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8983
8984     return;
8985   }
8986 #endif
8987
8988   SCAN_PLAYFIELD(x, y)
8989   {
8990     if (AmoebaNr[x][y] == group_nr &&
8991         (Tile[x][y] == EL_AMOEBA_DEAD ||
8992          Tile[x][y] == EL_BD_AMOEBA ||
8993          Tile[x][y] == EL_AMOEBA_GROWING))
8994     {
8995       AmoebaNr[x][y] = 0;
8996       Tile[x][y] = new_element;
8997       InitField(x, y, FALSE);
8998       TEST_DrawLevelField(x, y);
8999       done = TRUE;
9000     }
9001   }
9002
9003   if (done)
9004     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9005                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9006                             SND_BD_AMOEBA_TURNING_TO_GEM));
9007 }
9008
9009 static void AmoebaGrowing(int x, int y)
9010 {
9011   static unsigned int sound_delay = 0;
9012   static unsigned int sound_delay_value = 0;
9013
9014   if (!MovDelay[x][y])          // start new growing cycle
9015   {
9016     MovDelay[x][y] = 7;
9017
9018     if (DelayReached(&sound_delay, sound_delay_value))
9019     {
9020       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9021       sound_delay_value = 30;
9022     }
9023   }
9024
9025   if (MovDelay[x][y])           // wait some time before growing bigger
9026   {
9027     MovDelay[x][y]--;
9028     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9029     {
9030       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9031                                            6 - MovDelay[x][y]);
9032
9033       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9034     }
9035
9036     if (!MovDelay[x][y])
9037     {
9038       Tile[x][y] = Store[x][y];
9039       Store[x][y] = 0;
9040       TEST_DrawLevelField(x, y);
9041     }
9042   }
9043 }
9044
9045 static void AmoebaShrinking(int x, int y)
9046 {
9047   static unsigned int sound_delay = 0;
9048   static unsigned int sound_delay_value = 0;
9049
9050   if (!MovDelay[x][y])          // start new shrinking cycle
9051   {
9052     MovDelay[x][y] = 7;
9053
9054     if (DelayReached(&sound_delay, sound_delay_value))
9055       sound_delay_value = 30;
9056   }
9057
9058   if (MovDelay[x][y])           // wait some time before shrinking
9059   {
9060     MovDelay[x][y]--;
9061     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9062     {
9063       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9064                                            6 - MovDelay[x][y]);
9065
9066       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9067     }
9068
9069     if (!MovDelay[x][y])
9070     {
9071       Tile[x][y] = EL_EMPTY;
9072       TEST_DrawLevelField(x, y);
9073
9074       // don't let mole enter this field in this cycle;
9075       // (give priority to objects falling to this field from above)
9076       Stop[x][y] = TRUE;
9077     }
9078   }
9079 }
9080
9081 static void AmoebaReproduce(int ax, int ay)
9082 {
9083   int i;
9084   int element = Tile[ax][ay];
9085   int graphic = el2img(element);
9086   int newax = ax, neway = ay;
9087   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9088   static int xy[4][2] =
9089   {
9090     { 0, -1 },
9091     { -1, 0 },
9092     { +1, 0 },
9093     { 0, +1 }
9094   };
9095
9096   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9097   {
9098     Tile[ax][ay] = EL_AMOEBA_DEAD;
9099     TEST_DrawLevelField(ax, ay);
9100     return;
9101   }
9102
9103   if (IS_ANIMATED(graphic))
9104     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9105
9106   if (!MovDelay[ax][ay])        // start making new amoeba field
9107     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9108
9109   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9110   {
9111     MovDelay[ax][ay]--;
9112     if (MovDelay[ax][ay])
9113       return;
9114   }
9115
9116   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9117   {
9118     int start = RND(4);
9119     int x = ax + xy[start][0];
9120     int y = ay + xy[start][1];
9121
9122     if (!IN_LEV_FIELD(x, y))
9123       return;
9124
9125     if (IS_FREE(x, y) ||
9126         CAN_GROW_INTO(Tile[x][y]) ||
9127         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9128         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9129     {
9130       newax = x;
9131       neway = y;
9132     }
9133
9134     if (newax == ax && neway == ay)
9135       return;
9136   }
9137   else                          // normal or "filled" (BD style) amoeba
9138   {
9139     int start = RND(4);
9140     boolean waiting_for_player = FALSE;
9141
9142     for (i = 0; i < NUM_DIRECTIONS; i++)
9143     {
9144       int j = (start + i) % 4;
9145       int x = ax + xy[j][0];
9146       int y = ay + xy[j][1];
9147
9148       if (!IN_LEV_FIELD(x, y))
9149         continue;
9150
9151       if (IS_FREE(x, y) ||
9152           CAN_GROW_INTO(Tile[x][y]) ||
9153           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9154           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9155       {
9156         newax = x;
9157         neway = y;
9158         break;
9159       }
9160       else if (IS_PLAYER(x, y))
9161         waiting_for_player = TRUE;
9162     }
9163
9164     if (newax == ax && neway == ay)             // amoeba cannot grow
9165     {
9166       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9167       {
9168         Tile[ax][ay] = EL_AMOEBA_DEAD;
9169         TEST_DrawLevelField(ax, ay);
9170         AmoebaCnt[AmoebaNr[ax][ay]]--;
9171
9172         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9173         {
9174           if (element == EL_AMOEBA_FULL)
9175             AmoebaToDiamond(ax, ay);
9176           else if (element == EL_BD_AMOEBA)
9177             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9178         }
9179       }
9180       return;
9181     }
9182     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9183     {
9184       // amoeba gets larger by growing in some direction
9185
9186       int new_group_nr = AmoebaNr[ax][ay];
9187
9188 #ifdef DEBUG
9189   if (new_group_nr == 0)
9190   {
9191     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9192           newax, neway);
9193     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9194
9195     return;
9196   }
9197 #endif
9198
9199       AmoebaNr[newax][neway] = new_group_nr;
9200       AmoebaCnt[new_group_nr]++;
9201       AmoebaCnt2[new_group_nr]++;
9202
9203       // if amoeba touches other amoeba(s) after growing, unify them
9204       AmoebaMerge(newax, neway);
9205
9206       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9207       {
9208         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9209         return;
9210       }
9211     }
9212   }
9213
9214   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9215       (neway == lev_fieldy - 1 && newax != ax))
9216   {
9217     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9218     Store[newax][neway] = element;
9219   }
9220   else if (neway == ay || element == EL_EMC_DRIPPER)
9221   {
9222     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9223
9224     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9225   }
9226   else
9227   {
9228     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9229     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9230     Store[ax][ay] = EL_AMOEBA_DROP;
9231     ContinueMoving(ax, ay);
9232     return;
9233   }
9234
9235   TEST_DrawLevelField(newax, neway);
9236 }
9237
9238 static void Life(int ax, int ay)
9239 {
9240   int x1, y1, x2, y2;
9241   int life_time = 40;
9242   int element = Tile[ax][ay];
9243   int graphic = el2img(element);
9244   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9245                          level.biomaze);
9246   boolean changed = FALSE;
9247
9248   if (IS_ANIMATED(graphic))
9249     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9250
9251   if (Stop[ax][ay])
9252     return;
9253
9254   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9255     MovDelay[ax][ay] = life_time;
9256
9257   if (MovDelay[ax][ay])         // wait some time before next cycle
9258   {
9259     MovDelay[ax][ay]--;
9260     if (MovDelay[ax][ay])
9261       return;
9262   }
9263
9264   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9265   {
9266     int xx = ax+x1, yy = ay+y1;
9267     int old_element = Tile[xx][yy];
9268     int num_neighbours = 0;
9269
9270     if (!IN_LEV_FIELD(xx, yy))
9271       continue;
9272
9273     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9274     {
9275       int x = xx+x2, y = yy+y2;
9276
9277       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9278         continue;
9279
9280       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9281       boolean is_neighbour = FALSE;
9282
9283       if (level.use_life_bugs)
9284         is_neighbour =
9285           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9286            (IS_FREE(x, y)                             &&  Stop[x][y]));
9287       else
9288         is_neighbour =
9289           (Last[x][y] == element || is_player_cell);
9290
9291       if (is_neighbour)
9292         num_neighbours++;
9293     }
9294
9295     boolean is_free = FALSE;
9296
9297     if (level.use_life_bugs)
9298       is_free = (IS_FREE(xx, yy));
9299     else
9300       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9301
9302     if (xx == ax && yy == ay)           // field in the middle
9303     {
9304       if (num_neighbours < life_parameter[0] ||
9305           num_neighbours > life_parameter[1])
9306       {
9307         Tile[xx][yy] = EL_EMPTY;
9308         if (Tile[xx][yy] != old_element)
9309           TEST_DrawLevelField(xx, yy);
9310         Stop[xx][yy] = TRUE;
9311         changed = TRUE;
9312       }
9313     }
9314     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9315     {                                   // free border field
9316       if (num_neighbours >= life_parameter[2] &&
9317           num_neighbours <= life_parameter[3])
9318       {
9319         Tile[xx][yy] = element;
9320         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9321         if (Tile[xx][yy] != old_element)
9322           TEST_DrawLevelField(xx, yy);
9323         Stop[xx][yy] = TRUE;
9324         changed = TRUE;
9325       }
9326     }
9327   }
9328
9329   if (changed)
9330     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9331                    SND_GAME_OF_LIFE_GROWING);
9332 }
9333
9334 static void InitRobotWheel(int x, int y)
9335 {
9336   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9337 }
9338
9339 static void RunRobotWheel(int x, int y)
9340 {
9341   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9342 }
9343
9344 static void StopRobotWheel(int x, int y)
9345 {
9346   if (game.robot_wheel_x == x &&
9347       game.robot_wheel_y == y)
9348   {
9349     game.robot_wheel_x = -1;
9350     game.robot_wheel_y = -1;
9351     game.robot_wheel_active = FALSE;
9352   }
9353 }
9354
9355 static void InitTimegateWheel(int x, int y)
9356 {
9357   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9358 }
9359
9360 static void RunTimegateWheel(int x, int y)
9361 {
9362   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9363 }
9364
9365 static void InitMagicBallDelay(int x, int y)
9366 {
9367   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9368 }
9369
9370 static void ActivateMagicBall(int bx, int by)
9371 {
9372   int x, y;
9373
9374   if (level.ball_random)
9375   {
9376     int pos_border = RND(8);    // select one of the eight border elements
9377     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9378     int xx = pos_content % 3;
9379     int yy = pos_content / 3;
9380
9381     x = bx - 1 + xx;
9382     y = by - 1 + yy;
9383
9384     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9385       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9386   }
9387   else
9388   {
9389     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9390     {
9391       int xx = x - bx + 1;
9392       int yy = y - by + 1;
9393
9394       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9395         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9396     }
9397   }
9398
9399   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9400 }
9401
9402 static void CheckExit(int x, int y)
9403 {
9404   if (game.gems_still_needed > 0 ||
9405       game.sokoban_fields_still_needed > 0 ||
9406       game.sokoban_objects_still_needed > 0 ||
9407       game.lights_still_needed > 0)
9408   {
9409     int element = Tile[x][y];
9410     int graphic = el2img(element);
9411
9412     if (IS_ANIMATED(graphic))
9413       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9414
9415     return;
9416   }
9417
9418   // do not re-open exit door closed after last player
9419   if (game.all_players_gone)
9420     return;
9421
9422   Tile[x][y] = EL_EXIT_OPENING;
9423
9424   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9425 }
9426
9427 static void CheckExitEM(int x, int y)
9428 {
9429   if (game.gems_still_needed > 0 ||
9430       game.sokoban_fields_still_needed > 0 ||
9431       game.sokoban_objects_still_needed > 0 ||
9432       game.lights_still_needed > 0)
9433   {
9434     int element = Tile[x][y];
9435     int graphic = el2img(element);
9436
9437     if (IS_ANIMATED(graphic))
9438       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9439
9440     return;
9441   }
9442
9443   // do not re-open exit door closed after last player
9444   if (game.all_players_gone)
9445     return;
9446
9447   Tile[x][y] = EL_EM_EXIT_OPENING;
9448
9449   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9450 }
9451
9452 static void CheckExitSteel(int x, int y)
9453 {
9454   if (game.gems_still_needed > 0 ||
9455       game.sokoban_fields_still_needed > 0 ||
9456       game.sokoban_objects_still_needed > 0 ||
9457       game.lights_still_needed > 0)
9458   {
9459     int element = Tile[x][y];
9460     int graphic = el2img(element);
9461
9462     if (IS_ANIMATED(graphic))
9463       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9464
9465     return;
9466   }
9467
9468   // do not re-open exit door closed after last player
9469   if (game.all_players_gone)
9470     return;
9471
9472   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9473
9474   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9475 }
9476
9477 static void CheckExitSteelEM(int x, int y)
9478 {
9479   if (game.gems_still_needed > 0 ||
9480       game.sokoban_fields_still_needed > 0 ||
9481       game.sokoban_objects_still_needed > 0 ||
9482       game.lights_still_needed > 0)
9483   {
9484     int element = Tile[x][y];
9485     int graphic = el2img(element);
9486
9487     if (IS_ANIMATED(graphic))
9488       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9489
9490     return;
9491   }
9492
9493   // do not re-open exit door closed after last player
9494   if (game.all_players_gone)
9495     return;
9496
9497   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9498
9499   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9500 }
9501
9502 static void CheckExitSP(int x, int y)
9503 {
9504   if (game.gems_still_needed > 0)
9505   {
9506     int element = Tile[x][y];
9507     int graphic = el2img(element);
9508
9509     if (IS_ANIMATED(graphic))
9510       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9511
9512     return;
9513   }
9514
9515   // do not re-open exit door closed after last player
9516   if (game.all_players_gone)
9517     return;
9518
9519   Tile[x][y] = EL_SP_EXIT_OPENING;
9520
9521   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9522 }
9523
9524 static void CloseAllOpenTimegates(void)
9525 {
9526   int x, y;
9527
9528   SCAN_PLAYFIELD(x, y)
9529   {
9530     int element = Tile[x][y];
9531
9532     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9533     {
9534       Tile[x][y] = EL_TIMEGATE_CLOSING;
9535
9536       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9537     }
9538   }
9539 }
9540
9541 static void DrawTwinkleOnField(int x, int y)
9542 {
9543   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9544     return;
9545
9546   if (Tile[x][y] == EL_BD_DIAMOND)
9547     return;
9548
9549   if (MovDelay[x][y] == 0)      // next animation frame
9550     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9551
9552   if (MovDelay[x][y] != 0)      // wait some time before next frame
9553   {
9554     MovDelay[x][y]--;
9555
9556     DrawLevelElementAnimation(x, y, Tile[x][y]);
9557
9558     if (MovDelay[x][y] != 0)
9559     {
9560       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9561                                            10 - MovDelay[x][y]);
9562
9563       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9564     }
9565   }
9566 }
9567
9568 static void MauerWaechst(int x, int y)
9569 {
9570   int delay = 6;
9571
9572   if (!MovDelay[x][y])          // next animation frame
9573     MovDelay[x][y] = 3 * delay;
9574
9575   if (MovDelay[x][y])           // wait some time before next frame
9576   {
9577     MovDelay[x][y]--;
9578
9579     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9580     {
9581       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9582       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9583
9584       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9585     }
9586
9587     if (!MovDelay[x][y])
9588     {
9589       if (MovDir[x][y] == MV_LEFT)
9590       {
9591         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9592           TEST_DrawLevelField(x - 1, y);
9593       }
9594       else if (MovDir[x][y] == MV_RIGHT)
9595       {
9596         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9597           TEST_DrawLevelField(x + 1, y);
9598       }
9599       else if (MovDir[x][y] == MV_UP)
9600       {
9601         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9602           TEST_DrawLevelField(x, y - 1);
9603       }
9604       else
9605       {
9606         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9607           TEST_DrawLevelField(x, y + 1);
9608       }
9609
9610       Tile[x][y] = Store[x][y];
9611       Store[x][y] = 0;
9612       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9613       TEST_DrawLevelField(x, y);
9614     }
9615   }
9616 }
9617
9618 static void MauerAbleger(int ax, int ay)
9619 {
9620   int element = Tile[ax][ay];
9621   int graphic = el2img(element);
9622   boolean oben_frei = FALSE, unten_frei = FALSE;
9623   boolean links_frei = FALSE, rechts_frei = FALSE;
9624   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9625   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9626   boolean new_wall = FALSE;
9627
9628   if (IS_ANIMATED(graphic))
9629     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9630
9631   if (!MovDelay[ax][ay])        // start building new wall
9632     MovDelay[ax][ay] = 6;
9633
9634   if (MovDelay[ax][ay])         // wait some time before building new wall
9635   {
9636     MovDelay[ax][ay]--;
9637     if (MovDelay[ax][ay])
9638       return;
9639   }
9640
9641   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9642     oben_frei = TRUE;
9643   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9644     unten_frei = TRUE;
9645   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9646     links_frei = TRUE;
9647   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9648     rechts_frei = TRUE;
9649
9650   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9651       element == EL_EXPANDABLE_WALL_ANY)
9652   {
9653     if (oben_frei)
9654     {
9655       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9656       Store[ax][ay-1] = element;
9657       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9658       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9659         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9660                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9661       new_wall = TRUE;
9662     }
9663     if (unten_frei)
9664     {
9665       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9666       Store[ax][ay+1] = element;
9667       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9668       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9669         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9670                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9671       new_wall = TRUE;
9672     }
9673   }
9674
9675   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9676       element == EL_EXPANDABLE_WALL_ANY ||
9677       element == EL_EXPANDABLE_WALL ||
9678       element == EL_BD_EXPANDABLE_WALL)
9679   {
9680     if (links_frei)
9681     {
9682       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9683       Store[ax-1][ay] = element;
9684       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9685       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9686         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9687                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9688       new_wall = TRUE;
9689     }
9690
9691     if (rechts_frei)
9692     {
9693       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9694       Store[ax+1][ay] = element;
9695       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9696       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9697         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9698                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9699       new_wall = TRUE;
9700     }
9701   }
9702
9703   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9704     TEST_DrawLevelField(ax, ay);
9705
9706   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9707     oben_massiv = TRUE;
9708   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9709     unten_massiv = TRUE;
9710   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9711     links_massiv = TRUE;
9712   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9713     rechts_massiv = TRUE;
9714
9715   if (((oben_massiv && unten_massiv) ||
9716        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9717        element == EL_EXPANDABLE_WALL) &&
9718       ((links_massiv && rechts_massiv) ||
9719        element == EL_EXPANDABLE_WALL_VERTICAL))
9720     Tile[ax][ay] = EL_WALL;
9721
9722   if (new_wall)
9723     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9724 }
9725
9726 static void MauerAblegerStahl(int ax, int ay)
9727 {
9728   int element = Tile[ax][ay];
9729   int graphic = el2img(element);
9730   boolean oben_frei = FALSE, unten_frei = FALSE;
9731   boolean links_frei = FALSE, rechts_frei = FALSE;
9732   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9733   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9734   boolean new_wall = FALSE;
9735
9736   if (IS_ANIMATED(graphic))
9737     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9738
9739   if (!MovDelay[ax][ay])        // start building new wall
9740     MovDelay[ax][ay] = 6;
9741
9742   if (MovDelay[ax][ay])         // wait some time before building new wall
9743   {
9744     MovDelay[ax][ay]--;
9745     if (MovDelay[ax][ay])
9746       return;
9747   }
9748
9749   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9750     oben_frei = TRUE;
9751   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9752     unten_frei = TRUE;
9753   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9754     links_frei = TRUE;
9755   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9756     rechts_frei = TRUE;
9757
9758   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9759       element == EL_EXPANDABLE_STEELWALL_ANY)
9760   {
9761     if (oben_frei)
9762     {
9763       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9764       Store[ax][ay-1] = element;
9765       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9766       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9767         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9768                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9769       new_wall = TRUE;
9770     }
9771     if (unten_frei)
9772     {
9773       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9774       Store[ax][ay+1] = element;
9775       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9776       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9777         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9778                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9779       new_wall = TRUE;
9780     }
9781   }
9782
9783   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9784       element == EL_EXPANDABLE_STEELWALL_ANY)
9785   {
9786     if (links_frei)
9787     {
9788       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9789       Store[ax-1][ay] = element;
9790       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9791       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9792         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9793                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9794       new_wall = TRUE;
9795     }
9796
9797     if (rechts_frei)
9798     {
9799       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9800       Store[ax+1][ay] = element;
9801       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9802       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9803         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9804                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9805       new_wall = TRUE;
9806     }
9807   }
9808
9809   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9810     oben_massiv = TRUE;
9811   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9812     unten_massiv = TRUE;
9813   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9814     links_massiv = TRUE;
9815   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9816     rechts_massiv = TRUE;
9817
9818   if (((oben_massiv && unten_massiv) ||
9819        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9820       ((links_massiv && rechts_massiv) ||
9821        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9822     Tile[ax][ay] = EL_STEELWALL;
9823
9824   if (new_wall)
9825     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9826 }
9827
9828 static void CheckForDragon(int x, int y)
9829 {
9830   int i, j;
9831   boolean dragon_found = FALSE;
9832   static int xy[4][2] =
9833   {
9834     { 0, -1 },
9835     { -1, 0 },
9836     { +1, 0 },
9837     { 0, +1 }
9838   };
9839
9840   for (i = 0; i < NUM_DIRECTIONS; i++)
9841   {
9842     for (j = 0; j < 4; j++)
9843     {
9844       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9845
9846       if (IN_LEV_FIELD(xx, yy) &&
9847           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9848       {
9849         if (Tile[xx][yy] == EL_DRAGON)
9850           dragon_found = TRUE;
9851       }
9852       else
9853         break;
9854     }
9855   }
9856
9857   if (!dragon_found)
9858   {
9859     for (i = 0; i < NUM_DIRECTIONS; i++)
9860     {
9861       for (j = 0; j < 3; j++)
9862       {
9863         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9864   
9865         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9866         {
9867           Tile[xx][yy] = EL_EMPTY;
9868           TEST_DrawLevelField(xx, yy);
9869         }
9870         else
9871           break;
9872       }
9873     }
9874   }
9875 }
9876
9877 static void InitBuggyBase(int x, int y)
9878 {
9879   int element = Tile[x][y];
9880   int activating_delay = FRAMES_PER_SECOND / 4;
9881
9882   ChangeDelay[x][y] =
9883     (element == EL_SP_BUGGY_BASE ?
9884      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9885      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9886      activating_delay :
9887      element == EL_SP_BUGGY_BASE_ACTIVE ?
9888      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9889 }
9890
9891 static void WarnBuggyBase(int x, int y)
9892 {
9893   int i;
9894   static int xy[4][2] =
9895   {
9896     { 0, -1 },
9897     { -1, 0 },
9898     { +1, 0 },
9899     { 0, +1 }
9900   };
9901
9902   for (i = 0; i < NUM_DIRECTIONS; i++)
9903   {
9904     int xx = x + xy[i][0];
9905     int yy = y + xy[i][1];
9906
9907     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9908     {
9909       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9910
9911       break;
9912     }
9913   }
9914 }
9915
9916 static void InitTrap(int x, int y)
9917 {
9918   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9919 }
9920
9921 static void ActivateTrap(int x, int y)
9922 {
9923   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9924 }
9925
9926 static void ChangeActiveTrap(int x, int y)
9927 {
9928   int graphic = IMG_TRAP_ACTIVE;
9929
9930   // if new animation frame was drawn, correct crumbled sand border
9931   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9932     TEST_DrawLevelFieldCrumbled(x, y);
9933 }
9934
9935 static int getSpecialActionElement(int element, int number, int base_element)
9936 {
9937   return (element != EL_EMPTY ? element :
9938           number != -1 ? base_element + number - 1 :
9939           EL_EMPTY);
9940 }
9941
9942 static int getModifiedActionNumber(int value_old, int operator, int operand,
9943                                    int value_min, int value_max)
9944 {
9945   int value_new = (operator == CA_MODE_SET      ? operand :
9946                    operator == CA_MODE_ADD      ? value_old + operand :
9947                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9948                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9949                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9950                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9951                    value_old);
9952
9953   return (value_new < value_min ? value_min :
9954           value_new > value_max ? value_max :
9955           value_new);
9956 }
9957
9958 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9959 {
9960   struct ElementInfo *ei = &element_info[element];
9961   struct ElementChangeInfo *change = &ei->change_page[page];
9962   int target_element = change->target_element;
9963   int action_type = change->action_type;
9964   int action_mode = change->action_mode;
9965   int action_arg = change->action_arg;
9966   int action_element = change->action_element;
9967   int i;
9968
9969   if (!change->has_action)
9970     return;
9971
9972   // ---------- determine action paramater values -----------------------------
9973
9974   int level_time_value =
9975     (level.time > 0 ? TimeLeft :
9976      TimePlayed);
9977
9978   int action_arg_element_raw =
9979     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9980      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9981      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9982      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9983      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9984      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9985      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9986      EL_EMPTY);
9987   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9988
9989   int action_arg_direction =
9990     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9991      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9992      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9993      change->actual_trigger_side :
9994      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9995      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9996      MV_NONE);
9997
9998   int action_arg_number_min =
9999     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10000      CA_ARG_MIN);
10001
10002   int action_arg_number_max =
10003     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10004      action_type == CA_SET_LEVEL_GEMS ? 999 :
10005      action_type == CA_SET_LEVEL_TIME ? 9999 :
10006      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10007      action_type == CA_SET_CE_VALUE ? 9999 :
10008      action_type == CA_SET_CE_SCORE ? 9999 :
10009      CA_ARG_MAX);
10010
10011   int action_arg_number_reset =
10012     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10013      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10014      action_type == CA_SET_LEVEL_TIME ? level.time :
10015      action_type == CA_SET_LEVEL_SCORE ? 0 :
10016      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10017      action_type == CA_SET_CE_SCORE ? 0 :
10018      0);
10019
10020   int action_arg_number =
10021     (action_arg <= CA_ARG_MAX ? action_arg :
10022      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10023      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10024      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10025      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10026      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10027      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10028      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10029      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10030      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10031      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10032      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10033      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10034      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10035      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10036      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10037      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10038      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10039      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10040      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10041      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10042      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10043      -1);
10044
10045   int action_arg_number_old =
10046     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10047      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10048      action_type == CA_SET_LEVEL_SCORE ? game.score :
10049      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10050      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10051      0);
10052
10053   int action_arg_number_new =
10054     getModifiedActionNumber(action_arg_number_old,
10055                             action_mode, action_arg_number,
10056                             action_arg_number_min, action_arg_number_max);
10057
10058   int trigger_player_bits =
10059     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10060      change->actual_trigger_player_bits : change->trigger_player);
10061
10062   int action_arg_player_bits =
10063     (action_arg >= CA_ARG_PLAYER_1 &&
10064      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10065      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10066      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10067      PLAYER_BITS_ANY);
10068
10069   // ---------- execute action  -----------------------------------------------
10070
10071   switch (action_type)
10072   {
10073     case CA_NO_ACTION:
10074     {
10075       return;
10076     }
10077
10078     // ---------- level actions  ----------------------------------------------
10079
10080     case CA_RESTART_LEVEL:
10081     {
10082       game.restart_level = TRUE;
10083
10084       break;
10085     }
10086
10087     case CA_SHOW_ENVELOPE:
10088     {
10089       int element = getSpecialActionElement(action_arg_element,
10090                                             action_arg_number, EL_ENVELOPE_1);
10091
10092       if (IS_ENVELOPE(element))
10093         local_player->show_envelope = element;
10094
10095       break;
10096     }
10097
10098     case CA_SET_LEVEL_TIME:
10099     {
10100       if (level.time > 0)       // only modify limited time value
10101       {
10102         TimeLeft = action_arg_number_new;
10103
10104         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10105
10106         DisplayGameControlValues();
10107
10108         if (!TimeLeft && setup.time_limit)
10109           for (i = 0; i < MAX_PLAYERS; i++)
10110             KillPlayer(&stored_player[i]);
10111       }
10112
10113       break;
10114     }
10115
10116     case CA_SET_LEVEL_SCORE:
10117     {
10118       game.score = action_arg_number_new;
10119
10120       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10121
10122       DisplayGameControlValues();
10123
10124       break;
10125     }
10126
10127     case CA_SET_LEVEL_GEMS:
10128     {
10129       game.gems_still_needed = action_arg_number_new;
10130
10131       game.snapshot.collected_item = TRUE;
10132
10133       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10134
10135       DisplayGameControlValues();
10136
10137       break;
10138     }
10139
10140     case CA_SET_LEVEL_WIND:
10141     {
10142       game.wind_direction = action_arg_direction;
10143
10144       break;
10145     }
10146
10147     case CA_SET_LEVEL_RANDOM_SEED:
10148     {
10149       // ensure that setting a new random seed while playing is predictable
10150       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10151
10152       break;
10153     }
10154
10155     // ---------- player actions  ---------------------------------------------
10156
10157     case CA_MOVE_PLAYER:
10158     case CA_MOVE_PLAYER_NEW:
10159     {
10160       // automatically move to the next field in specified direction
10161       for (i = 0; i < MAX_PLAYERS; i++)
10162         if (trigger_player_bits & (1 << i))
10163           if (action_type == CA_MOVE_PLAYER ||
10164               stored_player[i].MovPos == 0)
10165             stored_player[i].programmed_action = action_arg_direction;
10166
10167       break;
10168     }
10169
10170     case CA_EXIT_PLAYER:
10171     {
10172       for (i = 0; i < MAX_PLAYERS; i++)
10173         if (action_arg_player_bits & (1 << i))
10174           ExitPlayer(&stored_player[i]);
10175
10176       if (game.players_still_needed == 0)
10177         LevelSolved();
10178
10179       break;
10180     }
10181
10182     case CA_KILL_PLAYER:
10183     {
10184       for (i = 0; i < MAX_PLAYERS; i++)
10185         if (action_arg_player_bits & (1 << i))
10186           KillPlayer(&stored_player[i]);
10187
10188       break;
10189     }
10190
10191     case CA_SET_PLAYER_KEYS:
10192     {
10193       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10194       int element = getSpecialActionElement(action_arg_element,
10195                                             action_arg_number, EL_KEY_1);
10196
10197       if (IS_KEY(element))
10198       {
10199         for (i = 0; i < MAX_PLAYERS; i++)
10200         {
10201           if (trigger_player_bits & (1 << i))
10202           {
10203             stored_player[i].key[KEY_NR(element)] = key_state;
10204
10205             DrawGameDoorValues();
10206           }
10207         }
10208       }
10209
10210       break;
10211     }
10212
10213     case CA_SET_PLAYER_SPEED:
10214     {
10215       for (i = 0; i < MAX_PLAYERS; i++)
10216       {
10217         if (trigger_player_bits & (1 << i))
10218         {
10219           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10220
10221           if (action_arg == CA_ARG_SPEED_FASTER &&
10222               stored_player[i].cannot_move)
10223           {
10224             action_arg_number = STEPSIZE_VERY_SLOW;
10225           }
10226           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10227                    action_arg == CA_ARG_SPEED_FASTER)
10228           {
10229             action_arg_number = 2;
10230             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10231                            CA_MODE_MULTIPLY);
10232           }
10233           else if (action_arg == CA_ARG_NUMBER_RESET)
10234           {
10235             action_arg_number = level.initial_player_stepsize[i];
10236           }
10237
10238           move_stepsize =
10239             getModifiedActionNumber(move_stepsize,
10240                                     action_mode,
10241                                     action_arg_number,
10242                                     action_arg_number_min,
10243                                     action_arg_number_max);
10244
10245           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10246         }
10247       }
10248
10249       break;
10250     }
10251
10252     case CA_SET_PLAYER_SHIELD:
10253     {
10254       for (i = 0; i < MAX_PLAYERS; i++)
10255       {
10256         if (trigger_player_bits & (1 << i))
10257         {
10258           if (action_arg == CA_ARG_SHIELD_OFF)
10259           {
10260             stored_player[i].shield_normal_time_left = 0;
10261             stored_player[i].shield_deadly_time_left = 0;
10262           }
10263           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10264           {
10265             stored_player[i].shield_normal_time_left = 999999;
10266           }
10267           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10268           {
10269             stored_player[i].shield_normal_time_left = 999999;
10270             stored_player[i].shield_deadly_time_left = 999999;
10271           }
10272         }
10273       }
10274
10275       break;
10276     }
10277
10278     case CA_SET_PLAYER_GRAVITY:
10279     {
10280       for (i = 0; i < MAX_PLAYERS; i++)
10281       {
10282         if (trigger_player_bits & (1 << i))
10283         {
10284           stored_player[i].gravity =
10285             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10286              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10287              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10288              stored_player[i].gravity);
10289         }
10290       }
10291
10292       break;
10293     }
10294
10295     case CA_SET_PLAYER_ARTWORK:
10296     {
10297       for (i = 0; i < MAX_PLAYERS; i++)
10298       {
10299         if (trigger_player_bits & (1 << i))
10300         {
10301           int artwork_element = action_arg_element;
10302
10303           if (action_arg == CA_ARG_ELEMENT_RESET)
10304             artwork_element =
10305               (level.use_artwork_element[i] ? level.artwork_element[i] :
10306                stored_player[i].element_nr);
10307
10308           if (stored_player[i].artwork_element != artwork_element)
10309             stored_player[i].Frame = 0;
10310
10311           stored_player[i].artwork_element = artwork_element;
10312
10313           SetPlayerWaiting(&stored_player[i], FALSE);
10314
10315           // set number of special actions for bored and sleeping animation
10316           stored_player[i].num_special_action_bored =
10317             get_num_special_action(artwork_element,
10318                                    ACTION_BORING_1, ACTION_BORING_LAST);
10319           stored_player[i].num_special_action_sleeping =
10320             get_num_special_action(artwork_element,
10321                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10322         }
10323       }
10324
10325       break;
10326     }
10327
10328     case CA_SET_PLAYER_INVENTORY:
10329     {
10330       for (i = 0; i < MAX_PLAYERS; i++)
10331       {
10332         struct PlayerInfo *player = &stored_player[i];
10333         int j, k;
10334
10335         if (trigger_player_bits & (1 << i))
10336         {
10337           int inventory_element = action_arg_element;
10338
10339           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10340               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10341               action_arg == CA_ARG_ELEMENT_ACTION)
10342           {
10343             int element = inventory_element;
10344             int collect_count = element_info[element].collect_count_initial;
10345
10346             if (!IS_CUSTOM_ELEMENT(element))
10347               collect_count = 1;
10348
10349             if (collect_count == 0)
10350               player->inventory_infinite_element = element;
10351             else
10352               for (k = 0; k < collect_count; k++)
10353                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10354                   player->inventory_element[player->inventory_size++] =
10355                     element;
10356           }
10357           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10358                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10359                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10360           {
10361             if (player->inventory_infinite_element != EL_UNDEFINED &&
10362                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10363                                      action_arg_element_raw))
10364               player->inventory_infinite_element = EL_UNDEFINED;
10365
10366             for (k = 0, j = 0; j < player->inventory_size; j++)
10367             {
10368               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10369                                         action_arg_element_raw))
10370                 player->inventory_element[k++] = player->inventory_element[j];
10371             }
10372
10373             player->inventory_size = k;
10374           }
10375           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10376           {
10377             if (player->inventory_size > 0)
10378             {
10379               for (j = 0; j < player->inventory_size - 1; j++)
10380                 player->inventory_element[j] = player->inventory_element[j + 1];
10381
10382               player->inventory_size--;
10383             }
10384           }
10385           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10386           {
10387             if (player->inventory_size > 0)
10388               player->inventory_size--;
10389           }
10390           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10391           {
10392             player->inventory_infinite_element = EL_UNDEFINED;
10393             player->inventory_size = 0;
10394           }
10395           else if (action_arg == CA_ARG_INVENTORY_RESET)
10396           {
10397             player->inventory_infinite_element = EL_UNDEFINED;
10398             player->inventory_size = 0;
10399
10400             if (level.use_initial_inventory[i])
10401             {
10402               for (j = 0; j < level.initial_inventory_size[i]; j++)
10403               {
10404                 int element = level.initial_inventory_content[i][j];
10405                 int collect_count = element_info[element].collect_count_initial;
10406
10407                 if (!IS_CUSTOM_ELEMENT(element))
10408                   collect_count = 1;
10409
10410                 if (collect_count == 0)
10411                   player->inventory_infinite_element = element;
10412                 else
10413                   for (k = 0; k < collect_count; k++)
10414                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10415                       player->inventory_element[player->inventory_size++] =
10416                         element;
10417               }
10418             }
10419           }
10420         }
10421       }
10422
10423       break;
10424     }
10425
10426     // ---------- CE actions  -------------------------------------------------
10427
10428     case CA_SET_CE_VALUE:
10429     {
10430       int last_ce_value = CustomValue[x][y];
10431
10432       CustomValue[x][y] = action_arg_number_new;
10433
10434       if (CustomValue[x][y] != last_ce_value)
10435       {
10436         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10437         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10438
10439         if (CustomValue[x][y] == 0)
10440         {
10441           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10442           ChangeCount[x][y] = 0;        // allow at least one more change
10443
10444           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10445           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10446         }
10447       }
10448
10449       break;
10450     }
10451
10452     case CA_SET_CE_SCORE:
10453     {
10454       int last_ce_score = ei->collect_score;
10455
10456       ei->collect_score = action_arg_number_new;
10457
10458       if (ei->collect_score != last_ce_score)
10459       {
10460         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10461         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10462
10463         if (ei->collect_score == 0)
10464         {
10465           int xx, yy;
10466
10467           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10468           ChangeCount[x][y] = 0;        // allow at least one more change
10469
10470           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10471           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10472
10473           /*
10474             This is a very special case that seems to be a mixture between
10475             CheckElementChange() and CheckTriggeredElementChange(): while
10476             the first one only affects single elements that are triggered
10477             directly, the second one affects multiple elements in the playfield
10478             that are triggered indirectly by another element. This is a third
10479             case: Changing the CE score always affects multiple identical CEs,
10480             so every affected CE must be checked, not only the single CE for
10481             which the CE score was changed in the first place (as every instance
10482             of that CE shares the same CE score, and therefore also can change)!
10483           */
10484           SCAN_PLAYFIELD(xx, yy)
10485           {
10486             if (Tile[xx][yy] == element)
10487               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10488                                  CE_SCORE_GETS_ZERO);
10489           }
10490         }
10491       }
10492
10493       break;
10494     }
10495
10496     case CA_SET_CE_ARTWORK:
10497     {
10498       int artwork_element = action_arg_element;
10499       boolean reset_frame = FALSE;
10500       int xx, yy;
10501
10502       if (action_arg == CA_ARG_ELEMENT_RESET)
10503         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10504                            element);
10505
10506       if (ei->gfx_element != artwork_element)
10507         reset_frame = TRUE;
10508
10509       ei->gfx_element = artwork_element;
10510
10511       SCAN_PLAYFIELD(xx, yy)
10512       {
10513         if (Tile[xx][yy] == element)
10514         {
10515           if (reset_frame)
10516           {
10517             ResetGfxAnimation(xx, yy);
10518             ResetRandomAnimationValue(xx, yy);
10519           }
10520
10521           TEST_DrawLevelField(xx, yy);
10522         }
10523       }
10524
10525       break;
10526     }
10527
10528     // ---------- engine actions  ---------------------------------------------
10529
10530     case CA_SET_ENGINE_SCAN_MODE:
10531     {
10532       InitPlayfieldScanMode(action_arg);
10533
10534       break;
10535     }
10536
10537     default:
10538       break;
10539   }
10540 }
10541
10542 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10543 {
10544   int old_element = Tile[x][y];
10545   int new_element = GetElementFromGroupElement(element);
10546   int previous_move_direction = MovDir[x][y];
10547   int last_ce_value = CustomValue[x][y];
10548   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10549   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10550   boolean add_player_onto_element = (new_element_is_player &&
10551                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10552                                      IS_WALKABLE(old_element));
10553
10554   if (!add_player_onto_element)
10555   {
10556     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10557       RemoveMovingField(x, y);
10558     else
10559       RemoveField(x, y);
10560
10561     Tile[x][y] = new_element;
10562
10563     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10564       MovDir[x][y] = previous_move_direction;
10565
10566     if (element_info[new_element].use_last_ce_value)
10567       CustomValue[x][y] = last_ce_value;
10568
10569     InitField_WithBug1(x, y, FALSE);
10570
10571     new_element = Tile[x][y];   // element may have changed
10572
10573     ResetGfxAnimation(x, y);
10574     ResetRandomAnimationValue(x, y);
10575
10576     TEST_DrawLevelField(x, y);
10577
10578     if (GFX_CRUMBLED(new_element))
10579       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10580   }
10581
10582   // check if element under the player changes from accessible to unaccessible
10583   // (needed for special case of dropping element which then changes)
10584   // (must be checked after creating new element for walkable group elements)
10585   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10586       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10587   {
10588     Bang(x, y);
10589
10590     return;
10591   }
10592
10593   // "ChangeCount" not set yet to allow "entered by player" change one time
10594   if (new_element_is_player)
10595     RelocatePlayer(x, y, new_element);
10596
10597   if (is_change)
10598     ChangeCount[x][y]++;        // count number of changes in the same frame
10599
10600   TestIfBadThingTouchesPlayer(x, y);
10601   TestIfPlayerTouchesCustomElement(x, y);
10602   TestIfElementTouchesCustomElement(x, y);
10603 }
10604
10605 static void CreateField(int x, int y, int element)
10606 {
10607   CreateFieldExt(x, y, element, FALSE);
10608 }
10609
10610 static void CreateElementFromChange(int x, int y, int element)
10611 {
10612   element = GET_VALID_RUNTIME_ELEMENT(element);
10613
10614   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10615   {
10616     int old_element = Tile[x][y];
10617
10618     // prevent changed element from moving in same engine frame
10619     // unless both old and new element can either fall or move
10620     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10621         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10622       Stop[x][y] = TRUE;
10623   }
10624
10625   CreateFieldExt(x, y, element, TRUE);
10626 }
10627
10628 static boolean ChangeElement(int x, int y, int element, int page)
10629 {
10630   struct ElementInfo *ei = &element_info[element];
10631   struct ElementChangeInfo *change = &ei->change_page[page];
10632   int ce_value = CustomValue[x][y];
10633   int ce_score = ei->collect_score;
10634   int target_element;
10635   int old_element = Tile[x][y];
10636
10637   // always use default change event to prevent running into a loop
10638   if (ChangeEvent[x][y] == -1)
10639     ChangeEvent[x][y] = CE_DELAY;
10640
10641   if (ChangeEvent[x][y] == CE_DELAY)
10642   {
10643     // reset actual trigger element, trigger player and action element
10644     change->actual_trigger_element = EL_EMPTY;
10645     change->actual_trigger_player = EL_EMPTY;
10646     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10647     change->actual_trigger_side = CH_SIDE_NONE;
10648     change->actual_trigger_ce_value = 0;
10649     change->actual_trigger_ce_score = 0;
10650   }
10651
10652   // do not change elements more than a specified maximum number of changes
10653   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10654     return FALSE;
10655
10656   ChangeCount[x][y]++;          // count number of changes in the same frame
10657
10658   if (change->explode)
10659   {
10660     Bang(x, y);
10661
10662     return TRUE;
10663   }
10664
10665   if (change->use_target_content)
10666   {
10667     boolean complete_replace = TRUE;
10668     boolean can_replace[3][3];
10669     int xx, yy;
10670
10671     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10672     {
10673       boolean is_empty;
10674       boolean is_walkable;
10675       boolean is_diggable;
10676       boolean is_collectible;
10677       boolean is_removable;
10678       boolean is_destructible;
10679       int ex = x + xx - 1;
10680       int ey = y + yy - 1;
10681       int content_element = change->target_content.e[xx][yy];
10682       int e;
10683
10684       can_replace[xx][yy] = TRUE;
10685
10686       if (ex == x && ey == y)   // do not check changing element itself
10687         continue;
10688
10689       if (content_element == EL_EMPTY_SPACE)
10690       {
10691         can_replace[xx][yy] = FALSE;    // do not replace border with space
10692
10693         continue;
10694       }
10695
10696       if (!IN_LEV_FIELD(ex, ey))
10697       {
10698         can_replace[xx][yy] = FALSE;
10699         complete_replace = FALSE;
10700
10701         continue;
10702       }
10703
10704       e = Tile[ex][ey];
10705
10706       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10707         e = MovingOrBlocked2Element(ex, ey);
10708
10709       is_empty = (IS_FREE(ex, ey) ||
10710                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10711
10712       is_walkable     = (is_empty || IS_WALKABLE(e));
10713       is_diggable     = (is_empty || IS_DIGGABLE(e));
10714       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10715       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10716       is_removable    = (is_diggable || is_collectible);
10717
10718       can_replace[xx][yy] =
10719         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10720           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10721           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10722           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10723           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10724           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10725          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10726
10727       if (!can_replace[xx][yy])
10728         complete_replace = FALSE;
10729     }
10730
10731     if (!change->only_if_complete || complete_replace)
10732     {
10733       boolean something_has_changed = FALSE;
10734
10735       if (change->only_if_complete && change->use_random_replace &&
10736           RND(100) < change->random_percentage)
10737         return FALSE;
10738
10739       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10740       {
10741         int ex = x + xx - 1;
10742         int ey = y + yy - 1;
10743         int content_element;
10744
10745         if (can_replace[xx][yy] && (!change->use_random_replace ||
10746                                     RND(100) < change->random_percentage))
10747         {
10748           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10749             RemoveMovingField(ex, ey);
10750
10751           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10752
10753           content_element = change->target_content.e[xx][yy];
10754           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10755                                               ce_value, ce_score);
10756
10757           CreateElementFromChange(ex, ey, target_element);
10758
10759           something_has_changed = TRUE;
10760
10761           // for symmetry reasons, freeze newly created border elements
10762           if (ex != x || ey != y)
10763             Stop[ex][ey] = TRUE;        // no more moving in this frame
10764         }
10765       }
10766
10767       if (something_has_changed)
10768       {
10769         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10770         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10771       }
10772     }
10773   }
10774   else
10775   {
10776     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10777                                         ce_value, ce_score);
10778
10779     if (element == EL_DIAGONAL_GROWING ||
10780         element == EL_DIAGONAL_SHRINKING)
10781     {
10782       target_element = Store[x][y];
10783
10784       Store[x][y] = EL_EMPTY;
10785     }
10786
10787     CreateElementFromChange(x, y, target_element);
10788
10789     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10790     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10791   }
10792
10793   // this uses direct change before indirect change
10794   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10795
10796   return TRUE;
10797 }
10798
10799 static void HandleElementChange(int x, int y, int page)
10800 {
10801   int element = MovingOrBlocked2Element(x, y);
10802   struct ElementInfo *ei = &element_info[element];
10803   struct ElementChangeInfo *change = &ei->change_page[page];
10804   boolean handle_action_before_change = FALSE;
10805
10806 #ifdef DEBUG
10807   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10808       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10809   {
10810     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10811           x, y, element, element_info[element].token_name);
10812     Debug("game:playing:HandleElementChange", "This should never happen!");
10813   }
10814 #endif
10815
10816   // this can happen with classic bombs on walkable, changing elements
10817   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10818   {
10819     return;
10820   }
10821
10822   if (ChangeDelay[x][y] == 0)           // initialize element change
10823   {
10824     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10825
10826     if (change->can_change)
10827     {
10828       // !!! not clear why graphic animation should be reset at all here !!!
10829       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10830       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10831
10832       /*
10833         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10834
10835         When using an animation frame delay of 1 (this only happens with
10836         "sp_zonk.moving.left/right" in the classic graphics), the default
10837         (non-moving) animation shows wrong animation frames (while the
10838         moving animation, like "sp_zonk.moving.left/right", is correct,
10839         so this graphical bug never shows up with the classic graphics).
10840         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10841         be drawn instead of the correct frames 0,1,2,3. This is caused by
10842         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10843         an element change: First when the change delay ("ChangeDelay[][]")
10844         counter has reached zero after decrementing, then a second time in
10845         the next frame (after "GfxFrame[][]" was already incremented) when
10846         "ChangeDelay[][]" is reset to the initial delay value again.
10847
10848         This causes frame 0 to be drawn twice, while the last frame won't
10849         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10850
10851         As some animations may already be cleverly designed around this bug
10852         (at least the "Snake Bite" snake tail animation does this), it cannot
10853         simply be fixed here without breaking such existing animations.
10854         Unfortunately, it cannot easily be detected if a graphics set was
10855         designed "before" or "after" the bug was fixed. As a workaround,
10856         a new graphics set option "game.graphics_engine_version" was added
10857         to be able to specify the game's major release version for which the
10858         graphics set was designed, which can then be used to decide if the
10859         bugfix should be used (version 4 and above) or not (version 3 or
10860         below, or if no version was specified at all, as with old sets).
10861
10862         (The wrong/fixed animation frames can be tested with the test level set
10863         "test_gfxframe" and level "000", which contains a specially prepared
10864         custom element at level position (x/y) == (11/9) which uses the zonk
10865         animation mentioned above. Using "game.graphics_engine_version: 4"
10866         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10867         This can also be seen from the debug output for this test element.)
10868       */
10869
10870       // when a custom element is about to change (for example by change delay),
10871       // do not reset graphic animation when the custom element is moving
10872       if (game.graphics_engine_version < 4 &&
10873           !IS_MOVING(x, y))
10874       {
10875         ResetGfxAnimation(x, y);
10876         ResetRandomAnimationValue(x, y);
10877       }
10878
10879       if (change->pre_change_function)
10880         change->pre_change_function(x, y);
10881     }
10882   }
10883
10884   ChangeDelay[x][y]--;
10885
10886   if (ChangeDelay[x][y] != 0)           // continue element change
10887   {
10888     if (change->can_change)
10889     {
10890       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10891
10892       if (IS_ANIMATED(graphic))
10893         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10894
10895       if (change->change_function)
10896         change->change_function(x, y);
10897     }
10898   }
10899   else                                  // finish element change
10900   {
10901     if (ChangePage[x][y] != -1)         // remember page from delayed change
10902     {
10903       page = ChangePage[x][y];
10904       ChangePage[x][y] = -1;
10905
10906       change = &ei->change_page[page];
10907     }
10908
10909     if (IS_MOVING(x, y))                // never change a running system ;-)
10910     {
10911       ChangeDelay[x][y] = 1;            // try change after next move step
10912       ChangePage[x][y] = page;          // remember page to use for change
10913
10914       return;
10915     }
10916
10917     // special case: set new level random seed before changing element
10918     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10919       handle_action_before_change = TRUE;
10920
10921     if (change->has_action && handle_action_before_change)
10922       ExecuteCustomElementAction(x, y, element, page);
10923
10924     if (change->can_change)
10925     {
10926       if (ChangeElement(x, y, element, page))
10927       {
10928         if (change->post_change_function)
10929           change->post_change_function(x, y);
10930       }
10931     }
10932
10933     if (change->has_action && !handle_action_before_change)
10934       ExecuteCustomElementAction(x, y, element, page);
10935   }
10936 }
10937
10938 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10939                                               int trigger_element,
10940                                               int trigger_event,
10941                                               int trigger_player,
10942                                               int trigger_side,
10943                                               int trigger_page)
10944 {
10945   boolean change_done_any = FALSE;
10946   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10947   int i;
10948
10949   if (!(trigger_events[trigger_element][trigger_event]))
10950     return FALSE;
10951
10952   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10953
10954   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10955   {
10956     int element = EL_CUSTOM_START + i;
10957     boolean change_done = FALSE;
10958     int p;
10959
10960     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10961         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10962       continue;
10963
10964     for (p = 0; p < element_info[element].num_change_pages; p++)
10965     {
10966       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10967
10968       if (change->can_change_or_has_action &&
10969           change->has_event[trigger_event] &&
10970           change->trigger_side & trigger_side &&
10971           change->trigger_player & trigger_player &&
10972           change->trigger_page & trigger_page_bits &&
10973           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10974       {
10975         change->actual_trigger_element = trigger_element;
10976         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10977         change->actual_trigger_player_bits = trigger_player;
10978         change->actual_trigger_side = trigger_side;
10979         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10980         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10981
10982         if ((change->can_change && !change_done) || change->has_action)
10983         {
10984           int x, y;
10985
10986           SCAN_PLAYFIELD(x, y)
10987           {
10988             if (Tile[x][y] == element)
10989             {
10990               if (change->can_change && !change_done)
10991               {
10992                 // if element already changed in this frame, not only prevent
10993                 // another element change (checked in ChangeElement()), but
10994                 // also prevent additional element actions for this element
10995
10996                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10997                     !level.use_action_after_change_bug)
10998                   continue;
10999
11000                 ChangeDelay[x][y] = 1;
11001                 ChangeEvent[x][y] = trigger_event;
11002
11003                 HandleElementChange(x, y, p);
11004               }
11005               else if (change->has_action)
11006               {
11007                 // if element already changed in this frame, not only prevent
11008                 // another element change (checked in ChangeElement()), but
11009                 // also prevent additional element actions for this element
11010
11011                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11012                     !level.use_action_after_change_bug)
11013                   continue;
11014
11015                 ExecuteCustomElementAction(x, y, element, p);
11016                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11017               }
11018             }
11019           }
11020
11021           if (change->can_change)
11022           {
11023             change_done = TRUE;
11024             change_done_any = TRUE;
11025           }
11026         }
11027       }
11028     }
11029   }
11030
11031   RECURSION_LOOP_DETECTION_END();
11032
11033   return change_done_any;
11034 }
11035
11036 static boolean CheckElementChangeExt(int x, int y,
11037                                      int element,
11038                                      int trigger_element,
11039                                      int trigger_event,
11040                                      int trigger_player,
11041                                      int trigger_side)
11042 {
11043   boolean change_done = FALSE;
11044   int p;
11045
11046   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11047       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11048     return FALSE;
11049
11050   if (Tile[x][y] == EL_BLOCKED)
11051   {
11052     Blocked2Moving(x, y, &x, &y);
11053     element = Tile[x][y];
11054   }
11055
11056   // check if element has already changed or is about to change after moving
11057   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11058        Tile[x][y] != element) ||
11059
11060       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11061        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11062         ChangePage[x][y] != -1)))
11063     return FALSE;
11064
11065   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11066
11067   for (p = 0; p < element_info[element].num_change_pages; p++)
11068   {
11069     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11070
11071     /* check trigger element for all events where the element that is checked
11072        for changing interacts with a directly adjacent element -- this is
11073        different to element changes that affect other elements to change on the
11074        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11075     boolean check_trigger_element =
11076       (trigger_event == CE_TOUCHING_X ||
11077        trigger_event == CE_HITTING_X ||
11078        trigger_event == CE_HIT_BY_X ||
11079        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11080
11081     if (change->can_change_or_has_action &&
11082         change->has_event[trigger_event] &&
11083         change->trigger_side & trigger_side &&
11084         change->trigger_player & trigger_player &&
11085         (!check_trigger_element ||
11086          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11087     {
11088       change->actual_trigger_element = trigger_element;
11089       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11090       change->actual_trigger_player_bits = trigger_player;
11091       change->actual_trigger_side = trigger_side;
11092       change->actual_trigger_ce_value = CustomValue[x][y];
11093       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11094
11095       // special case: trigger element not at (x,y) position for some events
11096       if (check_trigger_element)
11097       {
11098         static struct
11099         {
11100           int dx, dy;
11101         } move_xy[] =
11102           {
11103             {  0,  0 },
11104             { -1,  0 },
11105             { +1,  0 },
11106             {  0,  0 },
11107             {  0, -1 },
11108             {  0,  0 }, { 0, 0 }, { 0, 0 },
11109             {  0, +1 }
11110           };
11111
11112         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11113         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11114
11115         change->actual_trigger_ce_value = CustomValue[xx][yy];
11116         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11117       }
11118
11119       if (change->can_change && !change_done)
11120       {
11121         ChangeDelay[x][y] = 1;
11122         ChangeEvent[x][y] = trigger_event;
11123
11124         HandleElementChange(x, y, p);
11125
11126         change_done = TRUE;
11127       }
11128       else if (change->has_action)
11129       {
11130         ExecuteCustomElementAction(x, y, element, p);
11131         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11132       }
11133     }
11134   }
11135
11136   RECURSION_LOOP_DETECTION_END();
11137
11138   return change_done;
11139 }
11140
11141 static void PlayPlayerSound(struct PlayerInfo *player)
11142 {
11143   int jx = player->jx, jy = player->jy;
11144   int sound_element = player->artwork_element;
11145   int last_action = player->last_action_waiting;
11146   int action = player->action_waiting;
11147
11148   if (player->is_waiting)
11149   {
11150     if (action != last_action)
11151       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11152     else
11153       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11154   }
11155   else
11156   {
11157     if (action != last_action)
11158       StopSound(element_info[sound_element].sound[last_action]);
11159
11160     if (last_action == ACTION_SLEEPING)
11161       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11162   }
11163 }
11164
11165 static void PlayAllPlayersSound(void)
11166 {
11167   int i;
11168
11169   for (i = 0; i < MAX_PLAYERS; i++)
11170     if (stored_player[i].active)
11171       PlayPlayerSound(&stored_player[i]);
11172 }
11173
11174 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11175 {
11176   boolean last_waiting = player->is_waiting;
11177   int move_dir = player->MovDir;
11178
11179   player->dir_waiting = move_dir;
11180   player->last_action_waiting = player->action_waiting;
11181
11182   if (is_waiting)
11183   {
11184     if (!last_waiting)          // not waiting -> waiting
11185     {
11186       player->is_waiting = TRUE;
11187
11188       player->frame_counter_bored =
11189         FrameCounter +
11190         game.player_boring_delay_fixed +
11191         GetSimpleRandom(game.player_boring_delay_random);
11192       player->frame_counter_sleeping =
11193         FrameCounter +
11194         game.player_sleeping_delay_fixed +
11195         GetSimpleRandom(game.player_sleeping_delay_random);
11196
11197       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11198     }
11199
11200     if (game.player_sleeping_delay_fixed +
11201         game.player_sleeping_delay_random > 0 &&
11202         player->anim_delay_counter == 0 &&
11203         player->post_delay_counter == 0 &&
11204         FrameCounter >= player->frame_counter_sleeping)
11205       player->is_sleeping = TRUE;
11206     else if (game.player_boring_delay_fixed +
11207              game.player_boring_delay_random > 0 &&
11208              FrameCounter >= player->frame_counter_bored)
11209       player->is_bored = TRUE;
11210
11211     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11212                               player->is_bored ? ACTION_BORING :
11213                               ACTION_WAITING);
11214
11215     if (player->is_sleeping && player->use_murphy)
11216     {
11217       // special case for sleeping Murphy when leaning against non-free tile
11218
11219       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11220           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11221            !IS_MOVING(player->jx - 1, player->jy)))
11222         move_dir = MV_LEFT;
11223       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11224                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11225                 !IS_MOVING(player->jx + 1, player->jy)))
11226         move_dir = MV_RIGHT;
11227       else
11228         player->is_sleeping = FALSE;
11229
11230       player->dir_waiting = move_dir;
11231     }
11232
11233     if (player->is_sleeping)
11234     {
11235       if (player->num_special_action_sleeping > 0)
11236       {
11237         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11238         {
11239           int last_special_action = player->special_action_sleeping;
11240           int num_special_action = player->num_special_action_sleeping;
11241           int special_action =
11242             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11243              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11244              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11245              last_special_action + 1 : ACTION_SLEEPING);
11246           int special_graphic =
11247             el_act_dir2img(player->artwork_element, special_action, move_dir);
11248
11249           player->anim_delay_counter =
11250             graphic_info[special_graphic].anim_delay_fixed +
11251             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11252           player->post_delay_counter =
11253             graphic_info[special_graphic].post_delay_fixed +
11254             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11255
11256           player->special_action_sleeping = special_action;
11257         }
11258
11259         if (player->anim_delay_counter > 0)
11260         {
11261           player->action_waiting = player->special_action_sleeping;
11262           player->anim_delay_counter--;
11263         }
11264         else if (player->post_delay_counter > 0)
11265         {
11266           player->post_delay_counter--;
11267         }
11268       }
11269     }
11270     else if (player->is_bored)
11271     {
11272       if (player->num_special_action_bored > 0)
11273       {
11274         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11275         {
11276           int special_action =
11277             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11278           int special_graphic =
11279             el_act_dir2img(player->artwork_element, special_action, move_dir);
11280
11281           player->anim_delay_counter =
11282             graphic_info[special_graphic].anim_delay_fixed +
11283             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11284           player->post_delay_counter =
11285             graphic_info[special_graphic].post_delay_fixed +
11286             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11287
11288           player->special_action_bored = special_action;
11289         }
11290
11291         if (player->anim_delay_counter > 0)
11292         {
11293           player->action_waiting = player->special_action_bored;
11294           player->anim_delay_counter--;
11295         }
11296         else if (player->post_delay_counter > 0)
11297         {
11298           player->post_delay_counter--;
11299         }
11300       }
11301     }
11302   }
11303   else if (last_waiting)        // waiting -> not waiting
11304   {
11305     player->is_waiting = FALSE;
11306     player->is_bored = FALSE;
11307     player->is_sleeping = FALSE;
11308
11309     player->frame_counter_bored = -1;
11310     player->frame_counter_sleeping = -1;
11311
11312     player->anim_delay_counter = 0;
11313     player->post_delay_counter = 0;
11314
11315     player->dir_waiting = player->MovDir;
11316     player->action_waiting = ACTION_DEFAULT;
11317
11318     player->special_action_bored = ACTION_DEFAULT;
11319     player->special_action_sleeping = ACTION_DEFAULT;
11320   }
11321 }
11322
11323 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11324 {
11325   if ((!player->is_moving  && player->was_moving) ||
11326       (player->MovPos == 0 && player->was_moving) ||
11327       (player->is_snapping && !player->was_snapping) ||
11328       (player->is_dropping && !player->was_dropping))
11329   {
11330     if (!CheckSaveEngineSnapshotToList())
11331       return;
11332
11333     player->was_moving = FALSE;
11334     player->was_snapping = TRUE;
11335     player->was_dropping = TRUE;
11336   }
11337   else
11338   {
11339     if (player->is_moving)
11340       player->was_moving = TRUE;
11341
11342     if (!player->is_snapping)
11343       player->was_snapping = FALSE;
11344
11345     if (!player->is_dropping)
11346       player->was_dropping = FALSE;
11347   }
11348
11349   static struct MouseActionInfo mouse_action_last = { 0 };
11350   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11351   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11352
11353   if (new_released)
11354     CheckSaveEngineSnapshotToList();
11355
11356   mouse_action_last = mouse_action;
11357 }
11358
11359 static void CheckSingleStepMode(struct PlayerInfo *player)
11360 {
11361   if (tape.single_step && tape.recording && !tape.pausing)
11362   {
11363     // as it is called "single step mode", just return to pause mode when the
11364     // player stopped moving after one tile (or never starts moving at all)
11365     // (reverse logic needed here in case single step mode used in team mode)
11366     if (player->is_moving ||
11367         player->is_pushing ||
11368         player->is_dropping_pressed ||
11369         player->effective_mouse_action.button)
11370       game.enter_single_step_mode = FALSE;
11371   }
11372
11373   CheckSaveEngineSnapshot(player);
11374 }
11375
11376 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11377 {
11378   int left      = player_action & JOY_LEFT;
11379   int right     = player_action & JOY_RIGHT;
11380   int up        = player_action & JOY_UP;
11381   int down      = player_action & JOY_DOWN;
11382   int button1   = player_action & JOY_BUTTON_1;
11383   int button2   = player_action & JOY_BUTTON_2;
11384   int dx        = (left ? -1 : right ? 1 : 0);
11385   int dy        = (up   ? -1 : down  ? 1 : 0);
11386
11387   if (!player->active || tape.pausing)
11388     return 0;
11389
11390   if (player_action)
11391   {
11392     if (button1)
11393       SnapField(player, dx, dy);
11394     else
11395     {
11396       if (button2)
11397         DropElement(player);
11398
11399       MovePlayer(player, dx, dy);
11400     }
11401
11402     CheckSingleStepMode(player);
11403
11404     SetPlayerWaiting(player, FALSE);
11405
11406     return player_action;
11407   }
11408   else
11409   {
11410     // no actions for this player (no input at player's configured device)
11411
11412     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11413     SnapField(player, 0, 0);
11414     CheckGravityMovementWhenNotMoving(player);
11415
11416     if (player->MovPos == 0)
11417       SetPlayerWaiting(player, TRUE);
11418
11419     if (player->MovPos == 0)    // needed for tape.playing
11420       player->is_moving = FALSE;
11421
11422     player->is_dropping = FALSE;
11423     player->is_dropping_pressed = FALSE;
11424     player->drop_pressed_delay = 0;
11425
11426     CheckSingleStepMode(player);
11427
11428     return 0;
11429   }
11430 }
11431
11432 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11433                                          byte *tape_action)
11434 {
11435   if (!tape.use_mouse_actions)
11436     return;
11437
11438   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11439   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11440   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11441 }
11442
11443 static void SetTapeActionFromMouseAction(byte *tape_action,
11444                                          struct MouseActionInfo *mouse_action)
11445 {
11446   if (!tape.use_mouse_actions)
11447     return;
11448
11449   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11450   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11451   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11452 }
11453
11454 static void CheckLevelSolved(void)
11455 {
11456   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11457   {
11458     if (game_em.level_solved &&
11459         !game_em.game_over)                             // game won
11460     {
11461       LevelSolved();
11462
11463       game_em.game_over = TRUE;
11464
11465       game.all_players_gone = TRUE;
11466     }
11467
11468     if (game_em.game_over)                              // game lost
11469       game.all_players_gone = TRUE;
11470   }
11471   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11472   {
11473     if (game_sp.level_solved &&
11474         !game_sp.game_over)                             // game won
11475     {
11476       LevelSolved();
11477
11478       game_sp.game_over = TRUE;
11479
11480       game.all_players_gone = TRUE;
11481     }
11482
11483     if (game_sp.game_over)                              // game lost
11484       game.all_players_gone = TRUE;
11485   }
11486   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11487   {
11488     if (game_mm.level_solved &&
11489         !game_mm.game_over)                             // game won
11490     {
11491       LevelSolved();
11492
11493       game_mm.game_over = TRUE;
11494
11495       game.all_players_gone = TRUE;
11496     }
11497
11498     if (game_mm.game_over)                              // game lost
11499       game.all_players_gone = TRUE;
11500   }
11501 }
11502
11503 static void CheckLevelTime(void)
11504 {
11505   int i;
11506
11507   if (TimeFrames >= FRAMES_PER_SECOND)
11508   {
11509     TimeFrames = 0;
11510     TapeTime++;
11511
11512     for (i = 0; i < MAX_PLAYERS; i++)
11513     {
11514       struct PlayerInfo *player = &stored_player[i];
11515
11516       if (SHIELD_ON(player))
11517       {
11518         player->shield_normal_time_left--;
11519
11520         if (player->shield_deadly_time_left > 0)
11521           player->shield_deadly_time_left--;
11522       }
11523     }
11524
11525     if (!game.LevelSolved && !level.use_step_counter)
11526     {
11527       TimePlayed++;
11528
11529       if (TimeLeft > 0)
11530       {
11531         TimeLeft--;
11532
11533         if (TimeLeft <= 10 && setup.time_limit)
11534           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11535
11536         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11537            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11538
11539         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11540
11541         if (!TimeLeft && setup.time_limit)
11542         {
11543           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11544             game_em.lev->killed_out_of_time = TRUE;
11545           else
11546             for (i = 0; i < MAX_PLAYERS; i++)
11547               KillPlayer(&stored_player[i]);
11548         }
11549       }
11550       else if (game.no_time_limit && !game.all_players_gone)
11551       {
11552         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11553       }
11554
11555       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11556     }
11557
11558     if (tape.recording || tape.playing)
11559       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11560   }
11561
11562   if (tape.recording || tape.playing)
11563     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11564
11565   UpdateAndDisplayGameControlValues();
11566 }
11567
11568 void AdvanceFrameAndPlayerCounters(int player_nr)
11569 {
11570   int i;
11571
11572   // advance frame counters (global frame counter and time frame counter)
11573   FrameCounter++;
11574   TimeFrames++;
11575
11576   // advance player counters (counters for move delay, move animation etc.)
11577   for (i = 0; i < MAX_PLAYERS; i++)
11578   {
11579     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11580     int move_delay_value = stored_player[i].move_delay_value;
11581     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11582
11583     if (!advance_player_counters)       // not all players may be affected
11584       continue;
11585
11586     if (move_frames == 0)       // less than one move per game frame
11587     {
11588       int stepsize = TILEX / move_delay_value;
11589       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11590       int count = (stored_player[i].is_moving ?
11591                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11592
11593       if (count % delay == 0)
11594         move_frames = 1;
11595     }
11596
11597     stored_player[i].Frame += move_frames;
11598
11599     if (stored_player[i].MovPos != 0)
11600       stored_player[i].StepFrame += move_frames;
11601
11602     if (stored_player[i].move_delay > 0)
11603       stored_player[i].move_delay--;
11604
11605     // due to bugs in previous versions, counter must count up, not down
11606     if (stored_player[i].push_delay != -1)
11607       stored_player[i].push_delay++;
11608
11609     if (stored_player[i].drop_delay > 0)
11610       stored_player[i].drop_delay--;
11611
11612     if (stored_player[i].is_dropping_pressed)
11613       stored_player[i].drop_pressed_delay++;
11614   }
11615 }
11616
11617 void StartGameActions(boolean init_network_game, boolean record_tape,
11618                       int random_seed)
11619 {
11620   unsigned int new_random_seed = InitRND(random_seed);
11621
11622   if (record_tape)
11623     TapeStartRecording(new_random_seed);
11624
11625   if (init_network_game)
11626   {
11627     SendToServer_LevelFile();
11628     SendToServer_StartPlaying();
11629
11630     return;
11631   }
11632
11633   InitGame();
11634 }
11635
11636 static void GameActionsExt(void)
11637 {
11638 #if 0
11639   static unsigned int game_frame_delay = 0;
11640 #endif
11641   unsigned int game_frame_delay_value;
11642   byte *recorded_player_action;
11643   byte summarized_player_action = 0;
11644   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11645   int i;
11646
11647   // detect endless loops, caused by custom element programming
11648   if (recursion_loop_detected && recursion_loop_depth == 0)
11649   {
11650     char *message = getStringCat3("Internal Error! Element ",
11651                                   EL_NAME(recursion_loop_element),
11652                                   " caused endless loop! Quit the game?");
11653
11654     Warn("element '%s' caused endless loop in game engine",
11655          EL_NAME(recursion_loop_element));
11656
11657     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11658
11659     recursion_loop_detected = FALSE;    // if game should be continued
11660
11661     free(message);
11662
11663     return;
11664   }
11665
11666   if (game.restart_level)
11667     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11668
11669   CheckLevelSolved();
11670
11671   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11672     GameWon();
11673
11674   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11675     TapeStop();
11676
11677   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11678     return;
11679
11680   game_frame_delay_value =
11681     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11682
11683   if (tape.playing && tape.warp_forward && !tape.pausing)
11684     game_frame_delay_value = 0;
11685
11686   SetVideoFrameDelay(game_frame_delay_value);
11687
11688   // (de)activate virtual buttons depending on current game status
11689   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11690   {
11691     if (game.all_players_gone)  // if no players there to be controlled anymore
11692       SetOverlayActive(FALSE);
11693     else if (!tape.playing)     // if game continues after tape stopped playing
11694       SetOverlayActive(TRUE);
11695   }
11696
11697 #if 0
11698 #if 0
11699   // ---------- main game synchronization point ----------
11700
11701   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11702
11703   Debug("game:playing:skip", "skip == %d", skip);
11704
11705 #else
11706   // ---------- main game synchronization point ----------
11707
11708   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11709 #endif
11710 #endif
11711
11712   if (network_playing && !network_player_action_received)
11713   {
11714     // try to get network player actions in time
11715
11716     // last chance to get network player actions without main loop delay
11717     HandleNetworking();
11718
11719     // game was quit by network peer
11720     if (game_status != GAME_MODE_PLAYING)
11721       return;
11722
11723     // check if network player actions still missing and game still running
11724     if (!network_player_action_received && !checkGameEnded())
11725       return;           // failed to get network player actions in time
11726
11727     // do not yet reset "network_player_action_received" (for tape.pausing)
11728   }
11729
11730   if (tape.pausing)
11731     return;
11732
11733   // at this point we know that we really continue executing the game
11734
11735   network_player_action_received = FALSE;
11736
11737   // when playing tape, read previously recorded player input from tape data
11738   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11739
11740   local_player->effective_mouse_action = local_player->mouse_action;
11741
11742   if (recorded_player_action != NULL)
11743     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11744                                  recorded_player_action);
11745
11746   // TapePlayAction() may return NULL when toggling to "pause before death"
11747   if (tape.pausing)
11748     return;
11749
11750   if (tape.set_centered_player)
11751   {
11752     game.centered_player_nr_next = tape.centered_player_nr_next;
11753     game.set_centered_player = TRUE;
11754   }
11755
11756   for (i = 0; i < MAX_PLAYERS; i++)
11757   {
11758     summarized_player_action |= stored_player[i].action;
11759
11760     if (!network_playing && (game.team_mode || tape.playing))
11761       stored_player[i].effective_action = stored_player[i].action;
11762   }
11763
11764   if (network_playing && !checkGameEnded())
11765     SendToServer_MovePlayer(summarized_player_action);
11766
11767   // summarize all actions at local players mapped input device position
11768   // (this allows using different input devices in single player mode)
11769   if (!network.enabled && !game.team_mode)
11770     stored_player[map_player_action[local_player->index_nr]].effective_action =
11771       summarized_player_action;
11772
11773   // summarize all actions at centered player in local team mode
11774   if (tape.recording &&
11775       setup.team_mode && !network.enabled &&
11776       setup.input_on_focus &&
11777       game.centered_player_nr != -1)
11778   {
11779     for (i = 0; i < MAX_PLAYERS; i++)
11780       stored_player[map_player_action[i]].effective_action =
11781         (i == game.centered_player_nr ? summarized_player_action : 0);
11782   }
11783
11784   if (recorded_player_action != NULL)
11785     for (i = 0; i < MAX_PLAYERS; i++)
11786       stored_player[i].effective_action = recorded_player_action[i];
11787
11788   for (i = 0; i < MAX_PLAYERS; i++)
11789   {
11790     tape_action[i] = stored_player[i].effective_action;
11791
11792     /* (this may happen in the RND game engine if a player was not present on
11793        the playfield on level start, but appeared later from a custom element */
11794     if (setup.team_mode &&
11795         tape.recording &&
11796         tape_action[i] &&
11797         !tape.player_participates[i])
11798       tape.player_participates[i] = TRUE;
11799   }
11800
11801   SetTapeActionFromMouseAction(tape_action,
11802                                &local_player->effective_mouse_action);
11803
11804   // only record actions from input devices, but not programmed actions
11805   if (tape.recording)
11806     TapeRecordAction(tape_action);
11807
11808   // remember if game was played (especially after tape stopped playing)
11809   if (!tape.playing && summarized_player_action)
11810     game.GamePlayed = TRUE;
11811
11812 #if USE_NEW_PLAYER_ASSIGNMENTS
11813   // !!! also map player actions in single player mode !!!
11814   // if (game.team_mode)
11815   if (1)
11816   {
11817     byte mapped_action[MAX_PLAYERS];
11818
11819 #if DEBUG_PLAYER_ACTIONS
11820     for (i = 0; i < MAX_PLAYERS; i++)
11821       DebugContinued("", "%d, ", stored_player[i].effective_action);
11822 #endif
11823
11824     for (i = 0; i < MAX_PLAYERS; i++)
11825       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11826
11827     for (i = 0; i < MAX_PLAYERS; i++)
11828       stored_player[i].effective_action = mapped_action[i];
11829
11830 #if DEBUG_PLAYER_ACTIONS
11831     DebugContinued("", "=> ");
11832     for (i = 0; i < MAX_PLAYERS; i++)
11833       DebugContinued("", "%d, ", stored_player[i].effective_action);
11834     DebugContinued("game:playing:player", "\n");
11835 #endif
11836   }
11837 #if DEBUG_PLAYER_ACTIONS
11838   else
11839   {
11840     for (i = 0; i < MAX_PLAYERS; i++)
11841       DebugContinued("", "%d, ", stored_player[i].effective_action);
11842     DebugContinued("game:playing:player", "\n");
11843   }
11844 #endif
11845 #endif
11846
11847   for (i = 0; i < MAX_PLAYERS; i++)
11848   {
11849     // allow engine snapshot in case of changed movement attempt
11850     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11851         (stored_player[i].effective_action & KEY_MOTION))
11852       game.snapshot.changed_action = TRUE;
11853
11854     // allow engine snapshot in case of snapping/dropping attempt
11855     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11856         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11857       game.snapshot.changed_action = TRUE;
11858
11859     game.snapshot.last_action[i] = stored_player[i].effective_action;
11860   }
11861
11862   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11863   {
11864     GameActions_EM_Main();
11865   }
11866   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11867   {
11868     GameActions_SP_Main();
11869   }
11870   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11871   {
11872     GameActions_MM_Main();
11873   }
11874   else
11875   {
11876     GameActions_RND_Main();
11877   }
11878
11879   BlitScreenToBitmap(backbuffer);
11880
11881   CheckLevelSolved();
11882   CheckLevelTime();
11883
11884   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11885
11886   if (global.show_frames_per_second)
11887   {
11888     static unsigned int fps_counter = 0;
11889     static int fps_frames = 0;
11890     unsigned int fps_delay_ms = Counter() - fps_counter;
11891
11892     fps_frames++;
11893
11894     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11895     {
11896       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11897
11898       fps_frames = 0;
11899       fps_counter = Counter();
11900
11901       // always draw FPS to screen after FPS value was updated
11902       redraw_mask |= REDRAW_FPS;
11903     }
11904
11905     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11906     if (GetDrawDeactivationMask() == REDRAW_NONE)
11907       redraw_mask |= REDRAW_FPS;
11908   }
11909 }
11910
11911 static void GameActions_CheckSaveEngineSnapshot(void)
11912 {
11913   if (!game.snapshot.save_snapshot)
11914     return;
11915
11916   // clear flag for saving snapshot _before_ saving snapshot
11917   game.snapshot.save_snapshot = FALSE;
11918
11919   SaveEngineSnapshotToList();
11920 }
11921
11922 void GameActions(void)
11923 {
11924   GameActionsExt();
11925
11926   GameActions_CheckSaveEngineSnapshot();
11927 }
11928
11929 void GameActions_EM_Main(void)
11930 {
11931   byte effective_action[MAX_PLAYERS];
11932   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11933   int i;
11934
11935   for (i = 0; i < MAX_PLAYERS; i++)
11936     effective_action[i] = stored_player[i].effective_action;
11937
11938   GameActions_EM(effective_action, warp_mode);
11939 }
11940
11941 void GameActions_SP_Main(void)
11942 {
11943   byte effective_action[MAX_PLAYERS];
11944   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11945   int i;
11946
11947   for (i = 0; i < MAX_PLAYERS; i++)
11948     effective_action[i] = stored_player[i].effective_action;
11949
11950   GameActions_SP(effective_action, warp_mode);
11951
11952   for (i = 0; i < MAX_PLAYERS; i++)
11953   {
11954     if (stored_player[i].force_dropping)
11955       stored_player[i].action |= KEY_BUTTON_DROP;
11956
11957     stored_player[i].force_dropping = FALSE;
11958   }
11959 }
11960
11961 void GameActions_MM_Main(void)
11962 {
11963   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11964
11965   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11966 }
11967
11968 void GameActions_RND_Main(void)
11969 {
11970   GameActions_RND();
11971 }
11972
11973 void GameActions_RND(void)
11974 {
11975   static struct MouseActionInfo mouse_action_last = { 0 };
11976   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11977   int magic_wall_x = 0, magic_wall_y = 0;
11978   int i, x, y, element, graphic, last_gfx_frame;
11979
11980   InitPlayfieldScanModeVars();
11981
11982   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11983   {
11984     SCAN_PLAYFIELD(x, y)
11985     {
11986       ChangeCount[x][y] = 0;
11987       ChangeEvent[x][y] = -1;
11988     }
11989   }
11990
11991   if (game.set_centered_player)
11992   {
11993     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11994
11995     // switching to "all players" only possible if all players fit to screen
11996     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11997     {
11998       game.centered_player_nr_next = game.centered_player_nr;
11999       game.set_centered_player = FALSE;
12000     }
12001
12002     // do not switch focus to non-existing (or non-active) player
12003     if (game.centered_player_nr_next >= 0 &&
12004         !stored_player[game.centered_player_nr_next].active)
12005     {
12006       game.centered_player_nr_next = game.centered_player_nr;
12007       game.set_centered_player = FALSE;
12008     }
12009   }
12010
12011   if (game.set_centered_player &&
12012       ScreenMovPos == 0)        // screen currently aligned at tile position
12013   {
12014     int sx, sy;
12015
12016     if (game.centered_player_nr_next == -1)
12017     {
12018       setScreenCenteredToAllPlayers(&sx, &sy);
12019     }
12020     else
12021     {
12022       sx = stored_player[game.centered_player_nr_next].jx;
12023       sy = stored_player[game.centered_player_nr_next].jy;
12024     }
12025
12026     game.centered_player_nr = game.centered_player_nr_next;
12027     game.set_centered_player = FALSE;
12028
12029     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12030     DrawGameDoorValues();
12031   }
12032
12033   // check single step mode (set flag and clear again if any player is active)
12034   game.enter_single_step_mode =
12035     (tape.single_step && tape.recording && !tape.pausing);
12036
12037   for (i = 0; i < MAX_PLAYERS; i++)
12038   {
12039     int actual_player_action = stored_player[i].effective_action;
12040
12041 #if 1
12042     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12043        - rnd_equinox_tetrachloride 048
12044        - rnd_equinox_tetrachloride_ii 096
12045        - rnd_emanuel_schmieg 002
12046        - doctor_sloan_ww 001, 020
12047     */
12048     if (stored_player[i].MovPos == 0)
12049       CheckGravityMovement(&stored_player[i]);
12050 #endif
12051
12052     // overwrite programmed action with tape action
12053     if (stored_player[i].programmed_action)
12054       actual_player_action = stored_player[i].programmed_action;
12055
12056     PlayerActions(&stored_player[i], actual_player_action);
12057
12058     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12059   }
12060
12061   // single step pause mode may already have been toggled by "ScrollPlayer()"
12062   if (game.enter_single_step_mode && !tape.pausing)
12063     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12064
12065   ScrollScreen(NULL, SCROLL_GO_ON);
12066
12067   /* for backwards compatibility, the following code emulates a fixed bug that
12068      occured when pushing elements (causing elements that just made their last
12069      pushing step to already (if possible) make their first falling step in the
12070      same game frame, which is bad); this code is also needed to use the famous
12071      "spring push bug" which is used in older levels and might be wanted to be
12072      used also in newer levels, but in this case the buggy pushing code is only
12073      affecting the "spring" element and no other elements */
12074
12075   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12076   {
12077     for (i = 0; i < MAX_PLAYERS; i++)
12078     {
12079       struct PlayerInfo *player = &stored_player[i];
12080       int x = player->jx;
12081       int y = player->jy;
12082
12083       if (player->active && player->is_pushing && player->is_moving &&
12084           IS_MOVING(x, y) &&
12085           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12086            Tile[x][y] == EL_SPRING))
12087       {
12088         ContinueMoving(x, y);
12089
12090         // continue moving after pushing (this is actually a bug)
12091         if (!IS_MOVING(x, y))
12092           Stop[x][y] = FALSE;
12093       }
12094     }
12095   }
12096
12097   SCAN_PLAYFIELD(x, y)
12098   {
12099     Last[x][y] = Tile[x][y];
12100
12101     ChangeCount[x][y] = 0;
12102     ChangeEvent[x][y] = -1;
12103
12104     // this must be handled before main playfield loop
12105     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12106     {
12107       MovDelay[x][y]--;
12108       if (MovDelay[x][y] <= 0)
12109         RemoveField(x, y);
12110     }
12111
12112     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12113     {
12114       MovDelay[x][y]--;
12115       if (MovDelay[x][y] <= 0)
12116       {
12117         int element = Store[x][y];
12118         int move_direction = MovDir[x][y];
12119         int player_index_bit = Store2[x][y];
12120
12121         Store[x][y] = 0;
12122         Store2[x][y] = 0;
12123
12124         RemoveField(x, y);
12125         TEST_DrawLevelField(x, y);
12126
12127         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12128       }
12129     }
12130
12131 #if DEBUG
12132     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12133     {
12134       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12135             x, y);
12136       Debug("game:playing:GameActions_RND", "This should never happen!");
12137
12138       ChangePage[x][y] = -1;
12139     }
12140 #endif
12141
12142     Stop[x][y] = FALSE;
12143     if (WasJustMoving[x][y] > 0)
12144       WasJustMoving[x][y]--;
12145     if (WasJustFalling[x][y] > 0)
12146       WasJustFalling[x][y]--;
12147     if (CheckCollision[x][y] > 0)
12148       CheckCollision[x][y]--;
12149     if (CheckImpact[x][y] > 0)
12150       CheckImpact[x][y]--;
12151
12152     GfxFrame[x][y]++;
12153
12154     /* reset finished pushing action (not done in ContinueMoving() to allow
12155        continuous pushing animation for elements with zero push delay) */
12156     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12157     {
12158       ResetGfxAnimation(x, y);
12159       TEST_DrawLevelField(x, y);
12160     }
12161
12162 #if DEBUG
12163     if (IS_BLOCKED(x, y))
12164     {
12165       int oldx, oldy;
12166
12167       Blocked2Moving(x, y, &oldx, &oldy);
12168       if (!IS_MOVING(oldx, oldy))
12169       {
12170         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12171         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12172         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12173         Debug("game:playing:GameActions_RND", "This should never happen!");
12174       }
12175     }
12176 #endif
12177   }
12178
12179   if (mouse_action.button)
12180   {
12181     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12182     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12183
12184     x = mouse_action.lx;
12185     y = mouse_action.ly;
12186     element = Tile[x][y];
12187
12188     if (new_button)
12189     {
12190       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12191       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12192                                          ch_button);
12193     }
12194
12195     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12196     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12197                                        ch_button);
12198   }
12199
12200   SCAN_PLAYFIELD(x, y)
12201   {
12202     element = Tile[x][y];
12203     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12204     last_gfx_frame = GfxFrame[x][y];
12205
12206     ResetGfxFrame(x, y);
12207
12208     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12209       DrawLevelGraphicAnimation(x, y, graphic);
12210
12211     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12212         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12213       ResetRandomAnimationValue(x, y);
12214
12215     SetRandomAnimationValue(x, y);
12216
12217     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12218
12219     if (IS_INACTIVE(element))
12220     {
12221       if (IS_ANIMATED(graphic))
12222         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12223
12224       continue;
12225     }
12226
12227     // this may take place after moving, so 'element' may have changed
12228     if (IS_CHANGING(x, y) &&
12229         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12230     {
12231       int page = element_info[element].event_page_nr[CE_DELAY];
12232
12233       HandleElementChange(x, y, page);
12234
12235       element = Tile[x][y];
12236       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12237     }
12238
12239     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12240     {
12241       StartMoving(x, y);
12242
12243       element = Tile[x][y];
12244       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12245
12246       if (IS_ANIMATED(graphic) &&
12247           !IS_MOVING(x, y) &&
12248           !Stop[x][y])
12249         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12250
12251       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12252         TEST_DrawTwinkleOnField(x, y);
12253     }
12254     else if (element == EL_ACID)
12255     {
12256       if (!Stop[x][y])
12257         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12258     }
12259     else if ((element == EL_EXIT_OPEN ||
12260               element == EL_EM_EXIT_OPEN ||
12261               element == EL_SP_EXIT_OPEN ||
12262               element == EL_STEEL_EXIT_OPEN ||
12263               element == EL_EM_STEEL_EXIT_OPEN ||
12264               element == EL_SP_TERMINAL ||
12265               element == EL_SP_TERMINAL_ACTIVE ||
12266               element == EL_EXTRA_TIME ||
12267               element == EL_SHIELD_NORMAL ||
12268               element == EL_SHIELD_DEADLY) &&
12269              IS_ANIMATED(graphic))
12270       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12271     else if (IS_MOVING(x, y))
12272       ContinueMoving(x, y);
12273     else if (IS_ACTIVE_BOMB(element))
12274       CheckDynamite(x, y);
12275     else if (element == EL_AMOEBA_GROWING)
12276       AmoebaGrowing(x, y);
12277     else if (element == EL_AMOEBA_SHRINKING)
12278       AmoebaShrinking(x, y);
12279
12280 #if !USE_NEW_AMOEBA_CODE
12281     else if (IS_AMOEBALIVE(element))
12282       AmoebaReproduce(x, y);
12283 #endif
12284
12285     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12286       Life(x, y);
12287     else if (element == EL_EXIT_CLOSED)
12288       CheckExit(x, y);
12289     else if (element == EL_EM_EXIT_CLOSED)
12290       CheckExitEM(x, y);
12291     else if (element == EL_STEEL_EXIT_CLOSED)
12292       CheckExitSteel(x, y);
12293     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12294       CheckExitSteelEM(x, y);
12295     else if (element == EL_SP_EXIT_CLOSED)
12296       CheckExitSP(x, y);
12297     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12298              element == EL_EXPANDABLE_STEELWALL_GROWING)
12299       MauerWaechst(x, y);
12300     else if (element == EL_EXPANDABLE_WALL ||
12301              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12302              element == EL_EXPANDABLE_WALL_VERTICAL ||
12303              element == EL_EXPANDABLE_WALL_ANY ||
12304              element == EL_BD_EXPANDABLE_WALL)
12305       MauerAbleger(x, y);
12306     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12307              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12308              element == EL_EXPANDABLE_STEELWALL_ANY)
12309       MauerAblegerStahl(x, y);
12310     else if (element == EL_FLAMES)
12311       CheckForDragon(x, y);
12312     else if (element == EL_EXPLOSION)
12313       ; // drawing of correct explosion animation is handled separately
12314     else if (element == EL_ELEMENT_SNAPPING ||
12315              element == EL_DIAGONAL_SHRINKING ||
12316              element == EL_DIAGONAL_GROWING)
12317     {
12318       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12319
12320       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12321     }
12322     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12323       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12324
12325     if (IS_BELT_ACTIVE(element))
12326       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12327
12328     if (game.magic_wall_active)
12329     {
12330       int jx = local_player->jx, jy = local_player->jy;
12331
12332       // play the element sound at the position nearest to the player
12333       if ((element == EL_MAGIC_WALL_FULL ||
12334            element == EL_MAGIC_WALL_ACTIVE ||
12335            element == EL_MAGIC_WALL_EMPTYING ||
12336            element == EL_BD_MAGIC_WALL_FULL ||
12337            element == EL_BD_MAGIC_WALL_ACTIVE ||
12338            element == EL_BD_MAGIC_WALL_EMPTYING ||
12339            element == EL_DC_MAGIC_WALL_FULL ||
12340            element == EL_DC_MAGIC_WALL_ACTIVE ||
12341            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12342           ABS(x - jx) + ABS(y - jy) <
12343           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12344       {
12345         magic_wall_x = x;
12346         magic_wall_y = y;
12347       }
12348     }
12349   }
12350
12351 #if USE_NEW_AMOEBA_CODE
12352   // new experimental amoeba growth stuff
12353   if (!(FrameCounter % 8))
12354   {
12355     static unsigned int random = 1684108901;
12356
12357     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12358     {
12359       x = RND(lev_fieldx);
12360       y = RND(lev_fieldy);
12361       element = Tile[x][y];
12362
12363       if (!IS_PLAYER(x,y) &&
12364           (element == EL_EMPTY ||
12365            CAN_GROW_INTO(element) ||
12366            element == EL_QUICKSAND_EMPTY ||
12367            element == EL_QUICKSAND_FAST_EMPTY ||
12368            element == EL_ACID_SPLASH_LEFT ||
12369            element == EL_ACID_SPLASH_RIGHT))
12370       {
12371         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12372             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12373             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12374             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12375           Tile[x][y] = EL_AMOEBA_DROP;
12376       }
12377
12378       random = random * 129 + 1;
12379     }
12380   }
12381 #endif
12382
12383   game.explosions_delayed = FALSE;
12384
12385   SCAN_PLAYFIELD(x, y)
12386   {
12387     element = Tile[x][y];
12388
12389     if (ExplodeField[x][y])
12390       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12391     else if (element == EL_EXPLOSION)
12392       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12393
12394     ExplodeField[x][y] = EX_TYPE_NONE;
12395   }
12396
12397   game.explosions_delayed = TRUE;
12398
12399   if (game.magic_wall_active)
12400   {
12401     if (!(game.magic_wall_time_left % 4))
12402     {
12403       int element = Tile[magic_wall_x][magic_wall_y];
12404
12405       if (element == EL_BD_MAGIC_WALL_FULL ||
12406           element == EL_BD_MAGIC_WALL_ACTIVE ||
12407           element == EL_BD_MAGIC_WALL_EMPTYING)
12408         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12409       else if (element == EL_DC_MAGIC_WALL_FULL ||
12410                element == EL_DC_MAGIC_WALL_ACTIVE ||
12411                element == EL_DC_MAGIC_WALL_EMPTYING)
12412         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12413       else
12414         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12415     }
12416
12417     if (game.magic_wall_time_left > 0)
12418     {
12419       game.magic_wall_time_left--;
12420
12421       if (!game.magic_wall_time_left)
12422       {
12423         SCAN_PLAYFIELD(x, y)
12424         {
12425           element = Tile[x][y];
12426
12427           if (element == EL_MAGIC_WALL_ACTIVE ||
12428               element == EL_MAGIC_WALL_FULL)
12429           {
12430             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12431             TEST_DrawLevelField(x, y);
12432           }
12433           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12434                    element == EL_BD_MAGIC_WALL_FULL)
12435           {
12436             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12437             TEST_DrawLevelField(x, y);
12438           }
12439           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12440                    element == EL_DC_MAGIC_WALL_FULL)
12441           {
12442             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12443             TEST_DrawLevelField(x, y);
12444           }
12445         }
12446
12447         game.magic_wall_active = FALSE;
12448       }
12449     }
12450   }
12451
12452   if (game.light_time_left > 0)
12453   {
12454     game.light_time_left--;
12455
12456     if (game.light_time_left == 0)
12457       RedrawAllLightSwitchesAndInvisibleElements();
12458   }
12459
12460   if (game.timegate_time_left > 0)
12461   {
12462     game.timegate_time_left--;
12463
12464     if (game.timegate_time_left == 0)
12465       CloseAllOpenTimegates();
12466   }
12467
12468   if (game.lenses_time_left > 0)
12469   {
12470     game.lenses_time_left--;
12471
12472     if (game.lenses_time_left == 0)
12473       RedrawAllInvisibleElementsForLenses();
12474   }
12475
12476   if (game.magnify_time_left > 0)
12477   {
12478     game.magnify_time_left--;
12479
12480     if (game.magnify_time_left == 0)
12481       RedrawAllInvisibleElementsForMagnifier();
12482   }
12483
12484   for (i = 0; i < MAX_PLAYERS; i++)
12485   {
12486     struct PlayerInfo *player = &stored_player[i];
12487
12488     if (SHIELD_ON(player))
12489     {
12490       if (player->shield_deadly_time_left)
12491         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12492       else if (player->shield_normal_time_left)
12493         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12494     }
12495   }
12496
12497 #if USE_DELAYED_GFX_REDRAW
12498   SCAN_PLAYFIELD(x, y)
12499   {
12500     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12501     {
12502       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12503          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12504
12505       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12506         DrawLevelField(x, y);
12507
12508       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12509         DrawLevelFieldCrumbled(x, y);
12510
12511       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12512         DrawLevelFieldCrumbledNeighbours(x, y);
12513
12514       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12515         DrawTwinkleOnField(x, y);
12516     }
12517
12518     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12519   }
12520 #endif
12521
12522   DrawAllPlayers();
12523   PlayAllPlayersSound();
12524
12525   for (i = 0; i < MAX_PLAYERS; i++)
12526   {
12527     struct PlayerInfo *player = &stored_player[i];
12528
12529     if (player->show_envelope != 0 && (!player->active ||
12530                                        player->MovPos == 0))
12531     {
12532       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12533
12534       player->show_envelope = 0;
12535     }
12536   }
12537
12538   // use random number generator in every frame to make it less predictable
12539   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12540     RND(1);
12541
12542   mouse_action_last = mouse_action;
12543 }
12544
12545 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12546 {
12547   int min_x = x, min_y = y, max_x = x, max_y = y;
12548   int scr_fieldx = getScreenFieldSizeX();
12549   int scr_fieldy = getScreenFieldSizeY();
12550   int i;
12551
12552   for (i = 0; i < MAX_PLAYERS; i++)
12553   {
12554     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12555
12556     if (!stored_player[i].active || &stored_player[i] == player)
12557       continue;
12558
12559     min_x = MIN(min_x, jx);
12560     min_y = MIN(min_y, jy);
12561     max_x = MAX(max_x, jx);
12562     max_y = MAX(max_y, jy);
12563   }
12564
12565   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12566 }
12567
12568 static boolean AllPlayersInVisibleScreen(void)
12569 {
12570   int i;
12571
12572   for (i = 0; i < MAX_PLAYERS; i++)
12573   {
12574     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12575
12576     if (!stored_player[i].active)
12577       continue;
12578
12579     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12580       return FALSE;
12581   }
12582
12583   return TRUE;
12584 }
12585
12586 void ScrollLevel(int dx, int dy)
12587 {
12588   int scroll_offset = 2 * TILEX_VAR;
12589   int x, y;
12590
12591   BlitBitmap(drawto_field, drawto_field,
12592              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12593              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12594              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12595              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12596              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12597              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12598
12599   if (dx != 0)
12600   {
12601     x = (dx == 1 ? BX1 : BX2);
12602     for (y = BY1; y <= BY2; y++)
12603       DrawScreenField(x, y);
12604   }
12605
12606   if (dy != 0)
12607   {
12608     y = (dy == 1 ? BY1 : BY2);
12609     for (x = BX1; x <= BX2; x++)
12610       DrawScreenField(x, y);
12611   }
12612
12613   redraw_mask |= REDRAW_FIELD;
12614 }
12615
12616 static boolean canFallDown(struct PlayerInfo *player)
12617 {
12618   int jx = player->jx, jy = player->jy;
12619
12620   return (IN_LEV_FIELD(jx, jy + 1) &&
12621           (IS_FREE(jx, jy + 1) ||
12622            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12623           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12624           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12625 }
12626
12627 static boolean canPassField(int x, int y, int move_dir)
12628 {
12629   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12630   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12631   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12632   int nextx = x + dx;
12633   int nexty = y + dy;
12634   int element = Tile[x][y];
12635
12636   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12637           !CAN_MOVE(element) &&
12638           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12639           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12640           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12641 }
12642
12643 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12644 {
12645   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12646   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12647   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12648   int newx = x + dx;
12649   int newy = y + dy;
12650
12651   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12652           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12653           (IS_DIGGABLE(Tile[newx][newy]) ||
12654            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12655            canPassField(newx, newy, move_dir)));
12656 }
12657
12658 static void CheckGravityMovement(struct PlayerInfo *player)
12659 {
12660   if (player->gravity && !player->programmed_action)
12661   {
12662     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12663     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12664     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12665     int jx = player->jx, jy = player->jy;
12666     boolean player_is_moving_to_valid_field =
12667       (!player_is_snapping &&
12668        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12669         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12670     boolean player_can_fall_down = canFallDown(player);
12671
12672     if (player_can_fall_down &&
12673         !player_is_moving_to_valid_field)
12674       player->programmed_action = MV_DOWN;
12675   }
12676 }
12677
12678 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12679 {
12680   return CheckGravityMovement(player);
12681
12682   if (player->gravity && !player->programmed_action)
12683   {
12684     int jx = player->jx, jy = player->jy;
12685     boolean field_under_player_is_free =
12686       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12687     boolean player_is_standing_on_valid_field =
12688       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12689        (IS_WALKABLE(Tile[jx][jy]) &&
12690         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12691
12692     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12693       player->programmed_action = MV_DOWN;
12694   }
12695 }
12696
12697 /*
12698   MovePlayerOneStep()
12699   -----------------------------------------------------------------------------
12700   dx, dy:               direction (non-diagonal) to try to move the player to
12701   real_dx, real_dy:     direction as read from input device (can be diagonal)
12702 */
12703
12704 boolean MovePlayerOneStep(struct PlayerInfo *player,
12705                           int dx, int dy, int real_dx, int real_dy)
12706 {
12707   int jx = player->jx, jy = player->jy;
12708   int new_jx = jx + dx, new_jy = jy + dy;
12709   int can_move;
12710   boolean player_can_move = !player->cannot_move;
12711
12712   if (!player->active || (!dx && !dy))
12713     return MP_NO_ACTION;
12714
12715   player->MovDir = (dx < 0 ? MV_LEFT :
12716                     dx > 0 ? MV_RIGHT :
12717                     dy < 0 ? MV_UP :
12718                     dy > 0 ? MV_DOWN :  MV_NONE);
12719
12720   if (!IN_LEV_FIELD(new_jx, new_jy))
12721     return MP_NO_ACTION;
12722
12723   if (!player_can_move)
12724   {
12725     if (player->MovPos == 0)
12726     {
12727       player->is_moving = FALSE;
12728       player->is_digging = FALSE;
12729       player->is_collecting = FALSE;
12730       player->is_snapping = FALSE;
12731       player->is_pushing = FALSE;
12732     }
12733   }
12734
12735   if (!network.enabled && game.centered_player_nr == -1 &&
12736       !AllPlayersInSight(player, new_jx, new_jy))
12737     return MP_NO_ACTION;
12738
12739   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12740   if (can_move != MP_MOVING)
12741     return can_move;
12742
12743   // check if DigField() has caused relocation of the player
12744   if (player->jx != jx || player->jy != jy)
12745     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12746
12747   StorePlayer[jx][jy] = 0;
12748   player->last_jx = jx;
12749   player->last_jy = jy;
12750   player->jx = new_jx;
12751   player->jy = new_jy;
12752   StorePlayer[new_jx][new_jy] = player->element_nr;
12753
12754   if (player->move_delay_value_next != -1)
12755   {
12756     player->move_delay_value = player->move_delay_value_next;
12757     player->move_delay_value_next = -1;
12758   }
12759
12760   player->MovPos =
12761     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12762
12763   player->step_counter++;
12764
12765   PlayerVisit[jx][jy] = FrameCounter;
12766
12767   player->is_moving = TRUE;
12768
12769 #if 1
12770   // should better be called in MovePlayer(), but this breaks some tapes
12771   ScrollPlayer(player, SCROLL_INIT);
12772 #endif
12773
12774   return MP_MOVING;
12775 }
12776
12777 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12778 {
12779   int jx = player->jx, jy = player->jy;
12780   int old_jx = jx, old_jy = jy;
12781   int moved = MP_NO_ACTION;
12782
12783   if (!player->active)
12784     return FALSE;
12785
12786   if (!dx && !dy)
12787   {
12788     if (player->MovPos == 0)
12789     {
12790       player->is_moving = FALSE;
12791       player->is_digging = FALSE;
12792       player->is_collecting = FALSE;
12793       player->is_snapping = FALSE;
12794       player->is_pushing = FALSE;
12795     }
12796
12797     return FALSE;
12798   }
12799
12800   if (player->move_delay > 0)
12801     return FALSE;
12802
12803   player->move_delay = -1;              // set to "uninitialized" value
12804
12805   // store if player is automatically moved to next field
12806   player->is_auto_moving = (player->programmed_action != MV_NONE);
12807
12808   // remove the last programmed player action
12809   player->programmed_action = 0;
12810
12811   if (player->MovPos)
12812   {
12813     // should only happen if pre-1.2 tape recordings are played
12814     // this is only for backward compatibility
12815
12816     int original_move_delay_value = player->move_delay_value;
12817
12818 #if DEBUG
12819     Debug("game:playing:MovePlayer",
12820           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12821           tape.counter);
12822 #endif
12823
12824     // scroll remaining steps with finest movement resolution
12825     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12826
12827     while (player->MovPos)
12828     {
12829       ScrollPlayer(player, SCROLL_GO_ON);
12830       ScrollScreen(NULL, SCROLL_GO_ON);
12831
12832       AdvanceFrameAndPlayerCounters(player->index_nr);
12833
12834       DrawAllPlayers();
12835       BackToFront_WithFrameDelay(0);
12836     }
12837
12838     player->move_delay_value = original_move_delay_value;
12839   }
12840
12841   player->is_active = FALSE;
12842
12843   if (player->last_move_dir & MV_HORIZONTAL)
12844   {
12845     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12846       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12847   }
12848   else
12849   {
12850     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12851       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12852   }
12853
12854   if (!moved && !player->is_active)
12855   {
12856     player->is_moving = FALSE;
12857     player->is_digging = FALSE;
12858     player->is_collecting = FALSE;
12859     player->is_snapping = FALSE;
12860     player->is_pushing = FALSE;
12861   }
12862
12863   jx = player->jx;
12864   jy = player->jy;
12865
12866   if (moved & MP_MOVING && !ScreenMovPos &&
12867       (player->index_nr == game.centered_player_nr ||
12868        game.centered_player_nr == -1))
12869   {
12870     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12871
12872     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12873     {
12874       // actual player has left the screen -- scroll in that direction
12875       if (jx != old_jx)         // player has moved horizontally
12876         scroll_x += (jx - old_jx);
12877       else                      // player has moved vertically
12878         scroll_y += (jy - old_jy);
12879     }
12880     else
12881     {
12882       int offset_raw = game.scroll_delay_value;
12883
12884       if (jx != old_jx)         // player has moved horizontally
12885       {
12886         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12887         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12888         int new_scroll_x = jx - MIDPOSX + offset_x;
12889
12890         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12891             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12892           scroll_x = new_scroll_x;
12893
12894         // don't scroll over playfield boundaries
12895         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12896
12897         // don't scroll more than one field at a time
12898         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12899
12900         // don't scroll against the player's moving direction
12901         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12902             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12903           scroll_x = old_scroll_x;
12904       }
12905       else                      // player has moved vertically
12906       {
12907         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12908         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12909         int new_scroll_y = jy - MIDPOSY + offset_y;
12910
12911         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12912             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12913           scroll_y = new_scroll_y;
12914
12915         // don't scroll over playfield boundaries
12916         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12917
12918         // don't scroll more than one field at a time
12919         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12920
12921         // don't scroll against the player's moving direction
12922         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12923             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12924           scroll_y = old_scroll_y;
12925       }
12926     }
12927
12928     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12929     {
12930       if (!network.enabled && game.centered_player_nr == -1 &&
12931           !AllPlayersInVisibleScreen())
12932       {
12933         scroll_x = old_scroll_x;
12934         scroll_y = old_scroll_y;
12935       }
12936       else
12937       {
12938         ScrollScreen(player, SCROLL_INIT);
12939         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12940       }
12941     }
12942   }
12943
12944   player->StepFrame = 0;
12945
12946   if (moved & MP_MOVING)
12947   {
12948     if (old_jx != jx && old_jy == jy)
12949       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12950     else if (old_jx == jx && old_jy != jy)
12951       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12952
12953     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12954
12955     player->last_move_dir = player->MovDir;
12956     player->is_moving = TRUE;
12957     player->is_snapping = FALSE;
12958     player->is_switching = FALSE;
12959     player->is_dropping = FALSE;
12960     player->is_dropping_pressed = FALSE;
12961     player->drop_pressed_delay = 0;
12962
12963 #if 0
12964     // should better be called here than above, but this breaks some tapes
12965     ScrollPlayer(player, SCROLL_INIT);
12966 #endif
12967   }
12968   else
12969   {
12970     CheckGravityMovementWhenNotMoving(player);
12971
12972     player->is_moving = FALSE;
12973
12974     /* at this point, the player is allowed to move, but cannot move right now
12975        (e.g. because of something blocking the way) -- ensure that the player
12976        is also allowed to move in the next frame (in old versions before 3.1.1,
12977        the player was forced to wait again for eight frames before next try) */
12978
12979     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12980       player->move_delay = 0;   // allow direct movement in the next frame
12981   }
12982
12983   if (player->move_delay == -1)         // not yet initialized by DigField()
12984     player->move_delay = player->move_delay_value;
12985
12986   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12987   {
12988     TestIfPlayerTouchesBadThing(jx, jy);
12989     TestIfPlayerTouchesCustomElement(jx, jy);
12990   }
12991
12992   if (!player->active)
12993     RemovePlayer(player);
12994
12995   return moved;
12996 }
12997
12998 void ScrollPlayer(struct PlayerInfo *player, int mode)
12999 {
13000   int jx = player->jx, jy = player->jy;
13001   int last_jx = player->last_jx, last_jy = player->last_jy;
13002   int move_stepsize = TILEX / player->move_delay_value;
13003
13004   if (!player->active)
13005     return;
13006
13007   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13008     return;
13009
13010   if (mode == SCROLL_INIT)
13011   {
13012     player->actual_frame_counter = FrameCounter;
13013     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13014
13015     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13016         Tile[last_jx][last_jy] == EL_EMPTY)
13017     {
13018       int last_field_block_delay = 0;   // start with no blocking at all
13019       int block_delay_adjustment = player->block_delay_adjustment;
13020
13021       // if player blocks last field, add delay for exactly one move
13022       if (player->block_last_field)
13023       {
13024         last_field_block_delay += player->move_delay_value;
13025
13026         // when blocking enabled, prevent moving up despite gravity
13027         if (player->gravity && player->MovDir == MV_UP)
13028           block_delay_adjustment = -1;
13029       }
13030
13031       // add block delay adjustment (also possible when not blocking)
13032       last_field_block_delay += block_delay_adjustment;
13033
13034       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13035       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13036     }
13037
13038     if (player->MovPos != 0)    // player has not yet reached destination
13039       return;
13040   }
13041   else if (!FrameReached(&player->actual_frame_counter, 1))
13042     return;
13043
13044   if (player->MovPos != 0)
13045   {
13046     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13047     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13048
13049     // before DrawPlayer() to draw correct player graphic for this case
13050     if (player->MovPos == 0)
13051       CheckGravityMovement(player);
13052   }
13053
13054   if (player->MovPos == 0)      // player reached destination field
13055   {
13056     if (player->move_delay_reset_counter > 0)
13057     {
13058       player->move_delay_reset_counter--;
13059
13060       if (player->move_delay_reset_counter == 0)
13061       {
13062         // continue with normal speed after quickly moving through gate
13063         HALVE_PLAYER_SPEED(player);
13064
13065         // be able to make the next move without delay
13066         player->move_delay = 0;
13067       }
13068     }
13069
13070     player->last_jx = jx;
13071     player->last_jy = jy;
13072
13073     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13074         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13075         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13076         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13077         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13078         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13079         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13080         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13081     {
13082       ExitPlayer(player);
13083
13084       if (game.players_still_needed == 0 &&
13085           (game.friends_still_needed == 0 ||
13086            IS_SP_ELEMENT(Tile[jx][jy])))
13087         LevelSolved();
13088     }
13089
13090     // this breaks one level: "machine", level 000
13091     {
13092       int move_direction = player->MovDir;
13093       int enter_side = MV_DIR_OPPOSITE(move_direction);
13094       int leave_side = move_direction;
13095       int old_jx = last_jx;
13096       int old_jy = last_jy;
13097       int old_element = Tile[old_jx][old_jy];
13098       int new_element = Tile[jx][jy];
13099
13100       if (IS_CUSTOM_ELEMENT(old_element))
13101         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13102                                    CE_LEFT_BY_PLAYER,
13103                                    player->index_bit, leave_side);
13104
13105       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13106                                           CE_PLAYER_LEAVES_X,
13107                                           player->index_bit, leave_side);
13108
13109       if (IS_CUSTOM_ELEMENT(new_element))
13110         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13111                                    player->index_bit, enter_side);
13112
13113       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13114                                           CE_PLAYER_ENTERS_X,
13115                                           player->index_bit, enter_side);
13116
13117       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13118                                         CE_MOVE_OF_X, move_direction);
13119     }
13120
13121     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13122     {
13123       TestIfPlayerTouchesBadThing(jx, jy);
13124       TestIfPlayerTouchesCustomElement(jx, jy);
13125
13126       /* needed because pushed element has not yet reached its destination,
13127          so it would trigger a change event at its previous field location */
13128       if (!player->is_pushing)
13129         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13130
13131       if (level.finish_dig_collect &&
13132           (player->is_digging || player->is_collecting))
13133       {
13134         int last_element = player->last_removed_element;
13135         int move_direction = player->MovDir;
13136         int enter_side = MV_DIR_OPPOSITE(move_direction);
13137         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13138                             CE_PLAYER_COLLECTS_X);
13139
13140         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13141                                             player->index_bit, enter_side);
13142
13143         player->last_removed_element = EL_UNDEFINED;
13144       }
13145
13146       if (!player->active)
13147         RemovePlayer(player);
13148     }
13149
13150     if (level.use_step_counter)
13151     {
13152       int i;
13153
13154       TimePlayed++;
13155
13156       if (TimeLeft > 0)
13157       {
13158         TimeLeft--;
13159
13160         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13161           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13162
13163         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13164
13165         DisplayGameControlValues();
13166
13167         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13168           for (i = 0; i < MAX_PLAYERS; i++)
13169             KillPlayer(&stored_player[i]);
13170       }
13171       else if (game.no_time_limit && !game.all_players_gone)
13172       {
13173         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13174
13175         DisplayGameControlValues();
13176       }
13177     }
13178
13179     if (tape.single_step && tape.recording && !tape.pausing &&
13180         !player->programmed_action)
13181       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13182
13183     if (!player->programmed_action)
13184       CheckSaveEngineSnapshot(player);
13185   }
13186 }
13187
13188 void ScrollScreen(struct PlayerInfo *player, int mode)
13189 {
13190   static unsigned int screen_frame_counter = 0;
13191
13192   if (mode == SCROLL_INIT)
13193   {
13194     // set scrolling step size according to actual player's moving speed
13195     ScrollStepSize = TILEX / player->move_delay_value;
13196
13197     screen_frame_counter = FrameCounter;
13198     ScreenMovDir = player->MovDir;
13199     ScreenMovPos = player->MovPos;
13200     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13201     return;
13202   }
13203   else if (!FrameReached(&screen_frame_counter, 1))
13204     return;
13205
13206   if (ScreenMovPos)
13207   {
13208     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13209     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13210     redraw_mask |= REDRAW_FIELD;
13211   }
13212   else
13213     ScreenMovDir = MV_NONE;
13214 }
13215
13216 void TestIfPlayerTouchesCustomElement(int x, int y)
13217 {
13218   static int xy[4][2] =
13219   {
13220     { 0, -1 },
13221     { -1, 0 },
13222     { +1, 0 },
13223     { 0, +1 }
13224   };
13225   static int trigger_sides[4][2] =
13226   {
13227     // center side       border side
13228     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13229     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13230     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13231     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13232   };
13233   static int touch_dir[4] =
13234   {
13235     MV_LEFT | MV_RIGHT,
13236     MV_UP   | MV_DOWN,
13237     MV_UP   | MV_DOWN,
13238     MV_LEFT | MV_RIGHT
13239   };
13240   int center_element = Tile[x][y];      // should always be non-moving!
13241   int i;
13242
13243   for (i = 0; i < NUM_DIRECTIONS; i++)
13244   {
13245     int xx = x + xy[i][0];
13246     int yy = y + xy[i][1];
13247     int center_side = trigger_sides[i][0];
13248     int border_side = trigger_sides[i][1];
13249     int border_element;
13250
13251     if (!IN_LEV_FIELD(xx, yy))
13252       continue;
13253
13254     if (IS_PLAYER(x, y))                // player found at center element
13255     {
13256       struct PlayerInfo *player = PLAYERINFO(x, y);
13257
13258       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13259         border_element = Tile[xx][yy];          // may be moving!
13260       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13261         border_element = Tile[xx][yy];
13262       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13263         border_element = MovingOrBlocked2Element(xx, yy);
13264       else
13265         continue;               // center and border element do not touch
13266
13267       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13268                                  player->index_bit, border_side);
13269       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13270                                           CE_PLAYER_TOUCHES_X,
13271                                           player->index_bit, border_side);
13272
13273       {
13274         /* use player element that is initially defined in the level playfield,
13275            not the player element that corresponds to the runtime player number
13276            (example: a level that contains EL_PLAYER_3 as the only player would
13277            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13278         int player_element = PLAYERINFO(x, y)->initial_element;
13279
13280         CheckElementChangeBySide(xx, yy, border_element, player_element,
13281                                  CE_TOUCHING_X, border_side);
13282       }
13283     }
13284     else if (IS_PLAYER(xx, yy))         // player found at border element
13285     {
13286       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13287
13288       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13289       {
13290         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13291           continue;             // center and border element do not touch
13292       }
13293
13294       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13295                                  player->index_bit, center_side);
13296       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13297                                           CE_PLAYER_TOUCHES_X,
13298                                           player->index_bit, center_side);
13299
13300       {
13301         /* use player element that is initially defined in the level playfield,
13302            not the player element that corresponds to the runtime player number
13303            (example: a level that contains EL_PLAYER_3 as the only player would
13304            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13305         int player_element = PLAYERINFO(xx, yy)->initial_element;
13306
13307         CheckElementChangeBySide(x, y, center_element, player_element,
13308                                  CE_TOUCHING_X, center_side);
13309       }
13310
13311       break;
13312     }
13313   }
13314 }
13315
13316 void TestIfElementTouchesCustomElement(int x, int y)
13317 {
13318   static int xy[4][2] =
13319   {
13320     { 0, -1 },
13321     { -1, 0 },
13322     { +1, 0 },
13323     { 0, +1 }
13324   };
13325   static int trigger_sides[4][2] =
13326   {
13327     // center side      border side
13328     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13329     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13330     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13331     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13332   };
13333   static int touch_dir[4] =
13334   {
13335     MV_LEFT | MV_RIGHT,
13336     MV_UP   | MV_DOWN,
13337     MV_UP   | MV_DOWN,
13338     MV_LEFT | MV_RIGHT
13339   };
13340   boolean change_center_element = FALSE;
13341   int center_element = Tile[x][y];      // should always be non-moving!
13342   int border_element_old[NUM_DIRECTIONS];
13343   int i;
13344
13345   for (i = 0; i < NUM_DIRECTIONS; i++)
13346   {
13347     int xx = x + xy[i][0];
13348     int yy = y + xy[i][1];
13349     int border_element;
13350
13351     border_element_old[i] = -1;
13352
13353     if (!IN_LEV_FIELD(xx, yy))
13354       continue;
13355
13356     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13357       border_element = Tile[xx][yy];    // may be moving!
13358     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13359       border_element = Tile[xx][yy];
13360     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13361       border_element = MovingOrBlocked2Element(xx, yy);
13362     else
13363       continue;                 // center and border element do not touch
13364
13365     border_element_old[i] = border_element;
13366   }
13367
13368   for (i = 0; i < NUM_DIRECTIONS; i++)
13369   {
13370     int xx = x + xy[i][0];
13371     int yy = y + xy[i][1];
13372     int center_side = trigger_sides[i][0];
13373     int border_element = border_element_old[i];
13374
13375     if (border_element == -1)
13376       continue;
13377
13378     // check for change of border element
13379     CheckElementChangeBySide(xx, yy, border_element, center_element,
13380                              CE_TOUCHING_X, center_side);
13381
13382     // (center element cannot be player, so we dont have to check this here)
13383   }
13384
13385   for (i = 0; i < NUM_DIRECTIONS; i++)
13386   {
13387     int xx = x + xy[i][0];
13388     int yy = y + xy[i][1];
13389     int border_side = trigger_sides[i][1];
13390     int border_element = border_element_old[i];
13391
13392     if (border_element == -1)
13393       continue;
13394
13395     // check for change of center element (but change it only once)
13396     if (!change_center_element)
13397       change_center_element =
13398         CheckElementChangeBySide(x, y, center_element, border_element,
13399                                  CE_TOUCHING_X, border_side);
13400
13401     if (IS_PLAYER(xx, yy))
13402     {
13403       /* use player element that is initially defined in the level playfield,
13404          not the player element that corresponds to the runtime player number
13405          (example: a level that contains EL_PLAYER_3 as the only player would
13406          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13407       int player_element = PLAYERINFO(xx, yy)->initial_element;
13408
13409       CheckElementChangeBySide(x, y, center_element, player_element,
13410                                CE_TOUCHING_X, border_side);
13411     }
13412   }
13413 }
13414
13415 void TestIfElementHitsCustomElement(int x, int y, int direction)
13416 {
13417   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13418   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13419   int hitx = x + dx, hity = y + dy;
13420   int hitting_element = Tile[x][y];
13421   int touched_element;
13422
13423   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13424     return;
13425
13426   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13427                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13428
13429   if (IN_LEV_FIELD(hitx, hity))
13430   {
13431     int opposite_direction = MV_DIR_OPPOSITE(direction);
13432     int hitting_side = direction;
13433     int touched_side = opposite_direction;
13434     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13435                           MovDir[hitx][hity] != direction ||
13436                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13437
13438     object_hit = TRUE;
13439
13440     if (object_hit)
13441     {
13442       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13443                                CE_HITTING_X, touched_side);
13444
13445       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13446                                CE_HIT_BY_X, hitting_side);
13447
13448       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13449                                CE_HIT_BY_SOMETHING, opposite_direction);
13450
13451       if (IS_PLAYER(hitx, hity))
13452       {
13453         /* use player element that is initially defined in the level playfield,
13454            not the player element that corresponds to the runtime player number
13455            (example: a level that contains EL_PLAYER_3 as the only player would
13456            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13457         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13458
13459         CheckElementChangeBySide(x, y, hitting_element, player_element,
13460                                  CE_HITTING_X, touched_side);
13461       }
13462     }
13463   }
13464
13465   // "hitting something" is also true when hitting the playfield border
13466   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13467                            CE_HITTING_SOMETHING, direction);
13468 }
13469
13470 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13471 {
13472   int i, kill_x = -1, kill_y = -1;
13473
13474   int bad_element = -1;
13475   static int test_xy[4][2] =
13476   {
13477     { 0, -1 },
13478     { -1, 0 },
13479     { +1, 0 },
13480     { 0, +1 }
13481   };
13482   static int test_dir[4] =
13483   {
13484     MV_UP,
13485     MV_LEFT,
13486     MV_RIGHT,
13487     MV_DOWN
13488   };
13489
13490   for (i = 0; i < NUM_DIRECTIONS; i++)
13491   {
13492     int test_x, test_y, test_move_dir, test_element;
13493
13494     test_x = good_x + test_xy[i][0];
13495     test_y = good_y + test_xy[i][1];
13496
13497     if (!IN_LEV_FIELD(test_x, test_y))
13498       continue;
13499
13500     test_move_dir =
13501       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13502
13503     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13504
13505     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13506        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13507     */
13508     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13509         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13510     {
13511       kill_x = test_x;
13512       kill_y = test_y;
13513       bad_element = test_element;
13514
13515       break;
13516     }
13517   }
13518
13519   if (kill_x != -1 || kill_y != -1)
13520   {
13521     if (IS_PLAYER(good_x, good_y))
13522     {
13523       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13524
13525       if (player->shield_deadly_time_left > 0 &&
13526           !IS_INDESTRUCTIBLE(bad_element))
13527         Bang(kill_x, kill_y);
13528       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13529         KillPlayer(player);
13530     }
13531     else
13532       Bang(good_x, good_y);
13533   }
13534 }
13535
13536 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13537 {
13538   int i, kill_x = -1, kill_y = -1;
13539   int bad_element = Tile[bad_x][bad_y];
13540   static int test_xy[4][2] =
13541   {
13542     { 0, -1 },
13543     { -1, 0 },
13544     { +1, 0 },
13545     { 0, +1 }
13546   };
13547   static int touch_dir[4] =
13548   {
13549     MV_LEFT | MV_RIGHT,
13550     MV_UP   | MV_DOWN,
13551     MV_UP   | MV_DOWN,
13552     MV_LEFT | MV_RIGHT
13553   };
13554   static int test_dir[4] =
13555   {
13556     MV_UP,
13557     MV_LEFT,
13558     MV_RIGHT,
13559     MV_DOWN
13560   };
13561
13562   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13563     return;
13564
13565   for (i = 0; i < NUM_DIRECTIONS; i++)
13566   {
13567     int test_x, test_y, test_move_dir, test_element;
13568
13569     test_x = bad_x + test_xy[i][0];
13570     test_y = bad_y + test_xy[i][1];
13571
13572     if (!IN_LEV_FIELD(test_x, test_y))
13573       continue;
13574
13575     test_move_dir =
13576       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13577
13578     test_element = Tile[test_x][test_y];
13579
13580     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13581        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13582     */
13583     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13584         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13585     {
13586       // good thing is player or penguin that does not move away
13587       if (IS_PLAYER(test_x, test_y))
13588       {
13589         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13590
13591         if (bad_element == EL_ROBOT && player->is_moving)
13592           continue;     // robot does not kill player if he is moving
13593
13594         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13595         {
13596           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13597             continue;           // center and border element do not touch
13598         }
13599
13600         kill_x = test_x;
13601         kill_y = test_y;
13602
13603         break;
13604       }
13605       else if (test_element == EL_PENGUIN)
13606       {
13607         kill_x = test_x;
13608         kill_y = test_y;
13609
13610         break;
13611       }
13612     }
13613   }
13614
13615   if (kill_x != -1 || kill_y != -1)
13616   {
13617     if (IS_PLAYER(kill_x, kill_y))
13618     {
13619       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13620
13621       if (player->shield_deadly_time_left > 0 &&
13622           !IS_INDESTRUCTIBLE(bad_element))
13623         Bang(bad_x, bad_y);
13624       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13625         KillPlayer(player);
13626     }
13627     else
13628       Bang(kill_x, kill_y);
13629   }
13630 }
13631
13632 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13633 {
13634   int bad_element = Tile[bad_x][bad_y];
13635   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13636   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13637   int test_x = bad_x + dx, test_y = bad_y + dy;
13638   int test_move_dir, test_element;
13639   int kill_x = -1, kill_y = -1;
13640
13641   if (!IN_LEV_FIELD(test_x, test_y))
13642     return;
13643
13644   test_move_dir =
13645     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13646
13647   test_element = Tile[test_x][test_y];
13648
13649   if (test_move_dir != bad_move_dir)
13650   {
13651     // good thing can be player or penguin that does not move away
13652     if (IS_PLAYER(test_x, test_y))
13653     {
13654       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13655
13656       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13657          player as being hit when he is moving towards the bad thing, because
13658          the "get hit by" condition would be lost after the player stops) */
13659       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13660         return;         // player moves away from bad thing
13661
13662       kill_x = test_x;
13663       kill_y = test_y;
13664     }
13665     else if (test_element == EL_PENGUIN)
13666     {
13667       kill_x = test_x;
13668       kill_y = test_y;
13669     }
13670   }
13671
13672   if (kill_x != -1 || kill_y != -1)
13673   {
13674     if (IS_PLAYER(kill_x, kill_y))
13675     {
13676       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13677
13678       if (player->shield_deadly_time_left > 0 &&
13679           !IS_INDESTRUCTIBLE(bad_element))
13680         Bang(bad_x, bad_y);
13681       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13682         KillPlayer(player);
13683     }
13684     else
13685       Bang(kill_x, kill_y);
13686   }
13687 }
13688
13689 void TestIfPlayerTouchesBadThing(int x, int y)
13690 {
13691   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13692 }
13693
13694 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13695 {
13696   TestIfGoodThingHitsBadThing(x, y, move_dir);
13697 }
13698
13699 void TestIfBadThingTouchesPlayer(int x, int y)
13700 {
13701   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13702 }
13703
13704 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13705 {
13706   TestIfBadThingHitsGoodThing(x, y, move_dir);
13707 }
13708
13709 void TestIfFriendTouchesBadThing(int x, int y)
13710 {
13711   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13712 }
13713
13714 void TestIfBadThingTouchesFriend(int x, int y)
13715 {
13716   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13717 }
13718
13719 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13720 {
13721   int i, kill_x = bad_x, kill_y = bad_y;
13722   static int xy[4][2] =
13723   {
13724     { 0, -1 },
13725     { -1, 0 },
13726     { +1, 0 },
13727     { 0, +1 }
13728   };
13729
13730   for (i = 0; i < NUM_DIRECTIONS; i++)
13731   {
13732     int x, y, element;
13733
13734     x = bad_x + xy[i][0];
13735     y = bad_y + xy[i][1];
13736     if (!IN_LEV_FIELD(x, y))
13737       continue;
13738
13739     element = Tile[x][y];
13740     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13741         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13742     {
13743       kill_x = x;
13744       kill_y = y;
13745       break;
13746     }
13747   }
13748
13749   if (kill_x != bad_x || kill_y != bad_y)
13750     Bang(bad_x, bad_y);
13751 }
13752
13753 void KillPlayer(struct PlayerInfo *player)
13754 {
13755   int jx = player->jx, jy = player->jy;
13756
13757   if (!player->active)
13758     return;
13759
13760 #if 0
13761   Debug("game:playing:KillPlayer",
13762         "0: killed == %d, active == %d, reanimated == %d",
13763         player->killed, player->active, player->reanimated);
13764 #endif
13765
13766   /* the following code was introduced to prevent an infinite loop when calling
13767      -> Bang()
13768      -> CheckTriggeredElementChangeExt()
13769      -> ExecuteCustomElementAction()
13770      -> KillPlayer()
13771      -> (infinitely repeating the above sequence of function calls)
13772      which occurs when killing the player while having a CE with the setting
13773      "kill player X when explosion of <player X>"; the solution using a new
13774      field "player->killed" was chosen for backwards compatibility, although
13775      clever use of the fields "player->active" etc. would probably also work */
13776 #if 1
13777   if (player->killed)
13778     return;
13779 #endif
13780
13781   player->killed = TRUE;
13782
13783   // remove accessible field at the player's position
13784   Tile[jx][jy] = EL_EMPTY;
13785
13786   // deactivate shield (else Bang()/Explode() would not work right)
13787   player->shield_normal_time_left = 0;
13788   player->shield_deadly_time_left = 0;
13789
13790 #if 0
13791   Debug("game:playing:KillPlayer",
13792         "1: killed == %d, active == %d, reanimated == %d",
13793         player->killed, player->active, player->reanimated);
13794 #endif
13795
13796   Bang(jx, jy);
13797
13798 #if 0
13799   Debug("game:playing:KillPlayer",
13800         "2: killed == %d, active == %d, reanimated == %d",
13801         player->killed, player->active, player->reanimated);
13802 #endif
13803
13804   if (player->reanimated)       // killed player may have been reanimated
13805     player->killed = player->reanimated = FALSE;
13806   else
13807     BuryPlayer(player);
13808 }
13809
13810 static void KillPlayerUnlessEnemyProtected(int x, int y)
13811 {
13812   if (!PLAYER_ENEMY_PROTECTED(x, y))
13813     KillPlayer(PLAYERINFO(x, y));
13814 }
13815
13816 static void KillPlayerUnlessExplosionProtected(int x, int y)
13817 {
13818   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13819     KillPlayer(PLAYERINFO(x, y));
13820 }
13821
13822 void BuryPlayer(struct PlayerInfo *player)
13823 {
13824   int jx = player->jx, jy = player->jy;
13825
13826   if (!player->active)
13827     return;
13828
13829   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13830   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13831
13832   RemovePlayer(player);
13833
13834   player->buried = TRUE;
13835
13836   if (game.all_players_gone)
13837     game.GameOver = TRUE;
13838 }
13839
13840 void RemovePlayer(struct PlayerInfo *player)
13841 {
13842   int jx = player->jx, jy = player->jy;
13843   int i, found = FALSE;
13844
13845   player->present = FALSE;
13846   player->active = FALSE;
13847
13848   // required for some CE actions (even if the player is not active anymore)
13849   player->MovPos = 0;
13850
13851   if (!ExplodeField[jx][jy])
13852     StorePlayer[jx][jy] = 0;
13853
13854   if (player->is_moving)
13855     TEST_DrawLevelField(player->last_jx, player->last_jy);
13856
13857   for (i = 0; i < MAX_PLAYERS; i++)
13858     if (stored_player[i].active)
13859       found = TRUE;
13860
13861   if (!found)
13862   {
13863     game.all_players_gone = TRUE;
13864     game.GameOver = TRUE;
13865   }
13866
13867   game.exit_x = game.robot_wheel_x = jx;
13868   game.exit_y = game.robot_wheel_y = jy;
13869 }
13870
13871 void ExitPlayer(struct PlayerInfo *player)
13872 {
13873   DrawPlayer(player);   // needed here only to cleanup last field
13874   RemovePlayer(player);
13875
13876   if (game.players_still_needed > 0)
13877     game.players_still_needed--;
13878 }
13879
13880 static void SetFieldForSnapping(int x, int y, int element, int direction,
13881                                 int player_index_bit)
13882 {
13883   struct ElementInfo *ei = &element_info[element];
13884   int direction_bit = MV_DIR_TO_BIT(direction);
13885   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13886   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13887                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13888
13889   Tile[x][y] = EL_ELEMENT_SNAPPING;
13890   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13891   MovDir[x][y] = direction;
13892   Store[x][y] = element;
13893   Store2[x][y] = player_index_bit;
13894
13895   ResetGfxAnimation(x, y);
13896
13897   GfxElement[x][y] = element;
13898   GfxAction[x][y] = action;
13899   GfxDir[x][y] = direction;
13900   GfxFrame[x][y] = -1;
13901 }
13902
13903 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13904                                    int player_index_bit)
13905 {
13906   TestIfElementTouchesCustomElement(x, y);      // for empty space
13907
13908   if (level.finish_dig_collect)
13909   {
13910     int dig_side = MV_DIR_OPPOSITE(direction);
13911
13912     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13913                                         player_index_bit, dig_side);
13914   }
13915 }
13916
13917 /*
13918   =============================================================================
13919   checkDiagonalPushing()
13920   -----------------------------------------------------------------------------
13921   check if diagonal input device direction results in pushing of object
13922   (by checking if the alternative direction is walkable, diggable, ...)
13923   =============================================================================
13924 */
13925
13926 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13927                                     int x, int y, int real_dx, int real_dy)
13928 {
13929   int jx, jy, dx, dy, xx, yy;
13930
13931   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13932     return TRUE;
13933
13934   // diagonal direction: check alternative direction
13935   jx = player->jx;
13936   jy = player->jy;
13937   dx = x - jx;
13938   dy = y - jy;
13939   xx = jx + (dx == 0 ? real_dx : 0);
13940   yy = jy + (dy == 0 ? real_dy : 0);
13941
13942   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13943 }
13944
13945 /*
13946   =============================================================================
13947   DigField()
13948   -----------------------------------------------------------------------------
13949   x, y:                 field next to player (non-diagonal) to try to dig to
13950   real_dx, real_dy:     direction as read from input device (can be diagonal)
13951   =============================================================================
13952 */
13953
13954 static int DigField(struct PlayerInfo *player,
13955                     int oldx, int oldy, int x, int y,
13956                     int real_dx, int real_dy, int mode)
13957 {
13958   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13959   boolean player_was_pushing = player->is_pushing;
13960   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13961   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13962   int jx = oldx, jy = oldy;
13963   int dx = x - jx, dy = y - jy;
13964   int nextx = x + dx, nexty = y + dy;
13965   int move_direction = (dx == -1 ? MV_LEFT  :
13966                         dx == +1 ? MV_RIGHT :
13967                         dy == -1 ? MV_UP    :
13968                         dy == +1 ? MV_DOWN  : MV_NONE);
13969   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13970   int dig_side = MV_DIR_OPPOSITE(move_direction);
13971   int old_element = Tile[jx][jy];
13972   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13973   int collect_count;
13974
13975   if (is_player)                // function can also be called by EL_PENGUIN
13976   {
13977     if (player->MovPos == 0)
13978     {
13979       player->is_digging = FALSE;
13980       player->is_collecting = FALSE;
13981     }
13982
13983     if (player->MovPos == 0)    // last pushing move finished
13984       player->is_pushing = FALSE;
13985
13986     if (mode == DF_NO_PUSH)     // player just stopped pushing
13987     {
13988       player->is_switching = FALSE;
13989       player->push_delay = -1;
13990
13991       return MP_NO_ACTION;
13992     }
13993   }
13994
13995   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13996     old_element = Back[jx][jy];
13997
13998   // in case of element dropped at player position, check background
13999   else if (Back[jx][jy] != EL_EMPTY &&
14000            game.engine_version >= VERSION_IDENT(2,2,0,0))
14001     old_element = Back[jx][jy];
14002
14003   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14004     return MP_NO_ACTION;        // field has no opening in this direction
14005
14006   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14007     return MP_NO_ACTION;        // field has no opening in this direction
14008
14009   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14010   {
14011     SplashAcid(x, y);
14012
14013     Tile[jx][jy] = player->artwork_element;
14014     InitMovingField(jx, jy, MV_DOWN);
14015     Store[jx][jy] = EL_ACID;
14016     ContinueMoving(jx, jy);
14017     BuryPlayer(player);
14018
14019     return MP_DONT_RUN_INTO;
14020   }
14021
14022   if (player_can_move && DONT_RUN_INTO(element))
14023   {
14024     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14025
14026     return MP_DONT_RUN_INTO;
14027   }
14028
14029   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14030     return MP_NO_ACTION;
14031
14032   collect_count = element_info[element].collect_count_initial;
14033
14034   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14035     return MP_NO_ACTION;
14036
14037   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14038     player_can_move = player_can_move_or_snap;
14039
14040   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14041       game.engine_version >= VERSION_IDENT(2,2,0,0))
14042   {
14043     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14044                                player->index_bit, dig_side);
14045     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14046                                         player->index_bit, dig_side);
14047
14048     if (element == EL_DC_LANDMINE)
14049       Bang(x, y);
14050
14051     if (Tile[x][y] != element)          // field changed by snapping
14052       return MP_ACTION;
14053
14054     return MP_NO_ACTION;
14055   }
14056
14057   if (player->gravity && is_player && !player->is_auto_moving &&
14058       canFallDown(player) && move_direction != MV_DOWN &&
14059       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14060     return MP_NO_ACTION;        // player cannot walk here due to gravity
14061
14062   if (player_can_move &&
14063       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14064   {
14065     int sound_element = SND_ELEMENT(element);
14066     int sound_action = ACTION_WALKING;
14067
14068     if (IS_RND_GATE(element))
14069     {
14070       if (!player->key[RND_GATE_NR(element)])
14071         return MP_NO_ACTION;
14072     }
14073     else if (IS_RND_GATE_GRAY(element))
14074     {
14075       if (!player->key[RND_GATE_GRAY_NR(element)])
14076         return MP_NO_ACTION;
14077     }
14078     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14079     {
14080       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14081         return MP_NO_ACTION;
14082     }
14083     else if (element == EL_EXIT_OPEN ||
14084              element == EL_EM_EXIT_OPEN ||
14085              element == EL_EM_EXIT_OPENING ||
14086              element == EL_STEEL_EXIT_OPEN ||
14087              element == EL_EM_STEEL_EXIT_OPEN ||
14088              element == EL_EM_STEEL_EXIT_OPENING ||
14089              element == EL_SP_EXIT_OPEN ||
14090              element == EL_SP_EXIT_OPENING)
14091     {
14092       sound_action = ACTION_PASSING;    // player is passing exit
14093     }
14094     else if (element == EL_EMPTY)
14095     {
14096       sound_action = ACTION_MOVING;             // nothing to walk on
14097     }
14098
14099     // play sound from background or player, whatever is available
14100     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14101       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14102     else
14103       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14104   }
14105   else if (player_can_move &&
14106            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14107   {
14108     if (!ACCESS_FROM(element, opposite_direction))
14109       return MP_NO_ACTION;      // field not accessible from this direction
14110
14111     if (CAN_MOVE(element))      // only fixed elements can be passed!
14112       return MP_NO_ACTION;
14113
14114     if (IS_EM_GATE(element))
14115     {
14116       if (!player->key[EM_GATE_NR(element)])
14117         return MP_NO_ACTION;
14118     }
14119     else if (IS_EM_GATE_GRAY(element))
14120     {
14121       if (!player->key[EM_GATE_GRAY_NR(element)])
14122         return MP_NO_ACTION;
14123     }
14124     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14125     {
14126       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14127         return MP_NO_ACTION;
14128     }
14129     else if (IS_EMC_GATE(element))
14130     {
14131       if (!player->key[EMC_GATE_NR(element)])
14132         return MP_NO_ACTION;
14133     }
14134     else if (IS_EMC_GATE_GRAY(element))
14135     {
14136       if (!player->key[EMC_GATE_GRAY_NR(element)])
14137         return MP_NO_ACTION;
14138     }
14139     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14140     {
14141       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14142         return MP_NO_ACTION;
14143     }
14144     else if (element == EL_DC_GATE_WHITE ||
14145              element == EL_DC_GATE_WHITE_GRAY ||
14146              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14147     {
14148       if (player->num_white_keys == 0)
14149         return MP_NO_ACTION;
14150
14151       player->num_white_keys--;
14152     }
14153     else if (IS_SP_PORT(element))
14154     {
14155       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14156           element == EL_SP_GRAVITY_PORT_RIGHT ||
14157           element == EL_SP_GRAVITY_PORT_UP ||
14158           element == EL_SP_GRAVITY_PORT_DOWN)
14159         player->gravity = !player->gravity;
14160       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14161                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14162                element == EL_SP_GRAVITY_ON_PORT_UP ||
14163                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14164         player->gravity = TRUE;
14165       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14166                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14167                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14168                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14169         player->gravity = FALSE;
14170     }
14171
14172     // automatically move to the next field with double speed
14173     player->programmed_action = move_direction;
14174
14175     if (player->move_delay_reset_counter == 0)
14176     {
14177       player->move_delay_reset_counter = 2;     // two double speed steps
14178
14179       DOUBLE_PLAYER_SPEED(player);
14180     }
14181
14182     PlayLevelSoundAction(x, y, ACTION_PASSING);
14183   }
14184   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14185   {
14186     RemoveField(x, y);
14187
14188     if (mode != DF_SNAP)
14189     {
14190       GfxElement[x][y] = GFX_ELEMENT(element);
14191       player->is_digging = TRUE;
14192     }
14193
14194     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14195
14196     // use old behaviour for old levels (digging)
14197     if (!level.finish_dig_collect)
14198     {
14199       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14200                                           player->index_bit, dig_side);
14201
14202       // if digging triggered player relocation, finish digging tile
14203       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14204         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14205     }
14206
14207     if (mode == DF_SNAP)
14208     {
14209       if (level.block_snap_field)
14210         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14211       else
14212         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14213
14214       // use old behaviour for old levels (snapping)
14215       if (!level.finish_dig_collect)
14216         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14217                                             player->index_bit, dig_side);
14218     }
14219   }
14220   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14221   {
14222     RemoveField(x, y);
14223
14224     if (is_player && mode != DF_SNAP)
14225     {
14226       GfxElement[x][y] = element;
14227       player->is_collecting = TRUE;
14228     }
14229
14230     if (element == EL_SPEED_PILL)
14231     {
14232       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14233     }
14234     else if (element == EL_EXTRA_TIME && level.time > 0)
14235     {
14236       TimeLeft += level.extra_time;
14237
14238       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14239
14240       DisplayGameControlValues();
14241     }
14242     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14243     {
14244       player->shield_normal_time_left += level.shield_normal_time;
14245       if (element == EL_SHIELD_DEADLY)
14246         player->shield_deadly_time_left += level.shield_deadly_time;
14247     }
14248     else if (element == EL_DYNAMITE ||
14249              element == EL_EM_DYNAMITE ||
14250              element == EL_SP_DISK_RED)
14251     {
14252       if (player->inventory_size < MAX_INVENTORY_SIZE)
14253         player->inventory_element[player->inventory_size++] = element;
14254
14255       DrawGameDoorValues();
14256     }
14257     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14258     {
14259       player->dynabomb_count++;
14260       player->dynabombs_left++;
14261     }
14262     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14263     {
14264       player->dynabomb_size++;
14265     }
14266     else if (element == EL_DYNABOMB_INCREASE_POWER)
14267     {
14268       player->dynabomb_xl = TRUE;
14269     }
14270     else if (IS_KEY(element))
14271     {
14272       player->key[KEY_NR(element)] = TRUE;
14273
14274       DrawGameDoorValues();
14275     }
14276     else if (element == EL_DC_KEY_WHITE)
14277     {
14278       player->num_white_keys++;
14279
14280       // display white keys?
14281       // DrawGameDoorValues();
14282     }
14283     else if (IS_ENVELOPE(element))
14284     {
14285       player->show_envelope = element;
14286     }
14287     else if (element == EL_EMC_LENSES)
14288     {
14289       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14290
14291       RedrawAllInvisibleElementsForLenses();
14292     }
14293     else if (element == EL_EMC_MAGNIFIER)
14294     {
14295       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14296
14297       RedrawAllInvisibleElementsForMagnifier();
14298     }
14299     else if (IS_DROPPABLE(element) ||
14300              IS_THROWABLE(element))     // can be collected and dropped
14301     {
14302       int i;
14303
14304       if (collect_count == 0)
14305         player->inventory_infinite_element = element;
14306       else
14307         for (i = 0; i < collect_count; i++)
14308           if (player->inventory_size < MAX_INVENTORY_SIZE)
14309             player->inventory_element[player->inventory_size++] = element;
14310
14311       DrawGameDoorValues();
14312     }
14313     else if (collect_count > 0)
14314     {
14315       game.gems_still_needed -= collect_count;
14316       if (game.gems_still_needed < 0)
14317         game.gems_still_needed = 0;
14318
14319       game.snapshot.collected_item = TRUE;
14320
14321       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14322
14323       DisplayGameControlValues();
14324     }
14325
14326     RaiseScoreElement(element);
14327     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14328
14329     // use old behaviour for old levels (collecting)
14330     if (!level.finish_dig_collect && is_player)
14331     {
14332       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14333                                           player->index_bit, dig_side);
14334
14335       // if collecting triggered player relocation, finish collecting tile
14336       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14337         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14338     }
14339
14340     if (mode == DF_SNAP)
14341     {
14342       if (level.block_snap_field)
14343         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14344       else
14345         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14346
14347       // use old behaviour for old levels (snapping)
14348       if (!level.finish_dig_collect)
14349         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14350                                             player->index_bit, dig_side);
14351     }
14352   }
14353   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14354   {
14355     if (mode == DF_SNAP && element != EL_BD_ROCK)
14356       return MP_NO_ACTION;
14357
14358     if (CAN_FALL(element) && dy)
14359       return MP_NO_ACTION;
14360
14361     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14362         !(element == EL_SPRING && level.use_spring_bug))
14363       return MP_NO_ACTION;
14364
14365     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14366         ((move_direction & MV_VERTICAL &&
14367           ((element_info[element].move_pattern & MV_LEFT &&
14368             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14369            (element_info[element].move_pattern & MV_RIGHT &&
14370             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14371          (move_direction & MV_HORIZONTAL &&
14372           ((element_info[element].move_pattern & MV_UP &&
14373             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14374            (element_info[element].move_pattern & MV_DOWN &&
14375             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14376       return MP_NO_ACTION;
14377
14378     // do not push elements already moving away faster than player
14379     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14380         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14381       return MP_NO_ACTION;
14382
14383     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14384     {
14385       if (player->push_delay_value == -1 || !player_was_pushing)
14386         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14387     }
14388     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14389     {
14390       if (player->push_delay_value == -1)
14391         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14392     }
14393     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14394     {
14395       if (!player->is_pushing)
14396         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14397     }
14398
14399     player->is_pushing = TRUE;
14400     player->is_active = TRUE;
14401
14402     if (!(IN_LEV_FIELD(nextx, nexty) &&
14403           (IS_FREE(nextx, nexty) ||
14404            (IS_SB_ELEMENT(element) &&
14405             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14406            (IS_CUSTOM_ELEMENT(element) &&
14407             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14408       return MP_NO_ACTION;
14409
14410     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14411       return MP_NO_ACTION;
14412
14413     if (player->push_delay == -1)       // new pushing; restart delay
14414       player->push_delay = 0;
14415
14416     if (player->push_delay < player->push_delay_value &&
14417         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14418         element != EL_SPRING && element != EL_BALLOON)
14419     {
14420       // make sure that there is no move delay before next try to push
14421       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14422         player->move_delay = 0;
14423
14424       return MP_NO_ACTION;
14425     }
14426
14427     if (IS_CUSTOM_ELEMENT(element) &&
14428         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14429     {
14430       if (!DigFieldByCE(nextx, nexty, element))
14431         return MP_NO_ACTION;
14432     }
14433
14434     if (IS_SB_ELEMENT(element))
14435     {
14436       boolean sokoban_task_solved = FALSE;
14437
14438       if (element == EL_SOKOBAN_FIELD_FULL)
14439       {
14440         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14441
14442         IncrementSokobanFieldsNeeded();
14443         IncrementSokobanObjectsNeeded();
14444       }
14445
14446       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14447       {
14448         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14449
14450         DecrementSokobanFieldsNeeded();
14451         DecrementSokobanObjectsNeeded();
14452
14453         // sokoban object was pushed from empty field to sokoban field
14454         if (Back[x][y] == EL_EMPTY)
14455           sokoban_task_solved = TRUE;
14456       }
14457
14458       Tile[x][y] = EL_SOKOBAN_OBJECT;
14459
14460       if (Back[x][y] == Back[nextx][nexty])
14461         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14462       else if (Back[x][y] != 0)
14463         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14464                                     ACTION_EMPTYING);
14465       else
14466         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14467                                     ACTION_FILLING);
14468
14469       if (sokoban_task_solved &&
14470           game.sokoban_fields_still_needed == 0 &&
14471           game.sokoban_objects_still_needed == 0 &&
14472           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14473       {
14474         game.players_still_needed = 0;
14475
14476         LevelSolved();
14477
14478         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14479       }
14480     }
14481     else
14482       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14483
14484     InitMovingField(x, y, move_direction);
14485     GfxAction[x][y] = ACTION_PUSHING;
14486
14487     if (mode == DF_SNAP)
14488       ContinueMoving(x, y);
14489     else
14490       MovPos[x][y] = (dx != 0 ? dx : dy);
14491
14492     Pushed[x][y] = TRUE;
14493     Pushed[nextx][nexty] = TRUE;
14494
14495     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14496       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14497     else
14498       player->push_delay_value = -1;    // get new value later
14499
14500     // check for element change _after_ element has been pushed
14501     if (game.use_change_when_pushing_bug)
14502     {
14503       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14504                                  player->index_bit, dig_side);
14505       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14506                                           player->index_bit, dig_side);
14507     }
14508   }
14509   else if (IS_SWITCHABLE(element))
14510   {
14511     if (PLAYER_SWITCHING(player, x, y))
14512     {
14513       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14514                                           player->index_bit, dig_side);
14515
14516       return MP_ACTION;
14517     }
14518
14519     player->is_switching = TRUE;
14520     player->switch_x = x;
14521     player->switch_y = y;
14522
14523     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14524
14525     if (element == EL_ROBOT_WHEEL)
14526     {
14527       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14528
14529       game.robot_wheel_x = x;
14530       game.robot_wheel_y = y;
14531       game.robot_wheel_active = TRUE;
14532
14533       TEST_DrawLevelField(x, y);
14534     }
14535     else if (element == EL_SP_TERMINAL)
14536     {
14537       int xx, yy;
14538
14539       SCAN_PLAYFIELD(xx, yy)
14540       {
14541         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14542         {
14543           Bang(xx, yy);
14544         }
14545         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14546         {
14547           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14548
14549           ResetGfxAnimation(xx, yy);
14550           TEST_DrawLevelField(xx, yy);
14551         }
14552       }
14553     }
14554     else if (IS_BELT_SWITCH(element))
14555     {
14556       ToggleBeltSwitch(x, y);
14557     }
14558     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14559              element == EL_SWITCHGATE_SWITCH_DOWN ||
14560              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14561              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14562     {
14563       ToggleSwitchgateSwitch(x, y);
14564     }
14565     else if (element == EL_LIGHT_SWITCH ||
14566              element == EL_LIGHT_SWITCH_ACTIVE)
14567     {
14568       ToggleLightSwitch(x, y);
14569     }
14570     else if (element == EL_TIMEGATE_SWITCH ||
14571              element == EL_DC_TIMEGATE_SWITCH)
14572     {
14573       ActivateTimegateSwitch(x, y);
14574     }
14575     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14576              element == EL_BALLOON_SWITCH_RIGHT ||
14577              element == EL_BALLOON_SWITCH_UP    ||
14578              element == EL_BALLOON_SWITCH_DOWN  ||
14579              element == EL_BALLOON_SWITCH_NONE  ||
14580              element == EL_BALLOON_SWITCH_ANY)
14581     {
14582       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14583                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14584                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14585                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14586                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14587                              move_direction);
14588     }
14589     else if (element == EL_LAMP)
14590     {
14591       Tile[x][y] = EL_LAMP_ACTIVE;
14592       game.lights_still_needed--;
14593
14594       ResetGfxAnimation(x, y);
14595       TEST_DrawLevelField(x, y);
14596     }
14597     else if (element == EL_TIME_ORB_FULL)
14598     {
14599       Tile[x][y] = EL_TIME_ORB_EMPTY;
14600
14601       if (level.time > 0 || level.use_time_orb_bug)
14602       {
14603         TimeLeft += level.time_orb_time;
14604         game.no_time_limit = FALSE;
14605
14606         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14607
14608         DisplayGameControlValues();
14609       }
14610
14611       ResetGfxAnimation(x, y);
14612       TEST_DrawLevelField(x, y);
14613     }
14614     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14615              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14616     {
14617       int xx, yy;
14618
14619       game.ball_active = !game.ball_active;
14620
14621       SCAN_PLAYFIELD(xx, yy)
14622       {
14623         int e = Tile[xx][yy];
14624
14625         if (game.ball_active)
14626         {
14627           if (e == EL_EMC_MAGIC_BALL)
14628             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14629           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14630             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14631         }
14632         else
14633         {
14634           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14635             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14636           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14637             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14638         }
14639       }
14640     }
14641
14642     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14643                                         player->index_bit, dig_side);
14644
14645     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14646                                         player->index_bit, dig_side);
14647
14648     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14649                                         player->index_bit, dig_side);
14650
14651     return MP_ACTION;
14652   }
14653   else
14654   {
14655     if (!PLAYER_SWITCHING(player, x, y))
14656     {
14657       player->is_switching = TRUE;
14658       player->switch_x = x;
14659       player->switch_y = y;
14660
14661       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14662                                  player->index_bit, dig_side);
14663       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14664                                           player->index_bit, dig_side);
14665
14666       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14667                                  player->index_bit, dig_side);
14668       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14669                                           player->index_bit, dig_side);
14670     }
14671
14672     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14673                                player->index_bit, dig_side);
14674     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14675                                         player->index_bit, dig_side);
14676
14677     return MP_NO_ACTION;
14678   }
14679
14680   player->push_delay = -1;
14681
14682   if (is_player)                // function can also be called by EL_PENGUIN
14683   {
14684     if (Tile[x][y] != element)          // really digged/collected something
14685     {
14686       player->is_collecting = !player->is_digging;
14687       player->is_active = TRUE;
14688
14689       player->last_removed_element = element;
14690     }
14691   }
14692
14693   return MP_MOVING;
14694 }
14695
14696 static boolean DigFieldByCE(int x, int y, int digging_element)
14697 {
14698   int element = Tile[x][y];
14699
14700   if (!IS_FREE(x, y))
14701   {
14702     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14703                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14704                   ACTION_BREAKING);
14705
14706     // no element can dig solid indestructible elements
14707     if (IS_INDESTRUCTIBLE(element) &&
14708         !IS_DIGGABLE(element) &&
14709         !IS_COLLECTIBLE(element))
14710       return FALSE;
14711
14712     if (AmoebaNr[x][y] &&
14713         (element == EL_AMOEBA_FULL ||
14714          element == EL_BD_AMOEBA ||
14715          element == EL_AMOEBA_GROWING))
14716     {
14717       AmoebaCnt[AmoebaNr[x][y]]--;
14718       AmoebaCnt2[AmoebaNr[x][y]]--;
14719     }
14720
14721     if (IS_MOVING(x, y))
14722       RemoveMovingField(x, y);
14723     else
14724     {
14725       RemoveField(x, y);
14726       TEST_DrawLevelField(x, y);
14727     }
14728
14729     // if digged element was about to explode, prevent the explosion
14730     ExplodeField[x][y] = EX_TYPE_NONE;
14731
14732     PlayLevelSoundAction(x, y, action);
14733   }
14734
14735   Store[x][y] = EL_EMPTY;
14736
14737   // this makes it possible to leave the removed element again
14738   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14739     Store[x][y] = element;
14740
14741   return TRUE;
14742 }
14743
14744 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14745 {
14746   int jx = player->jx, jy = player->jy;
14747   int x = jx + dx, y = jy + dy;
14748   int snap_direction = (dx == -1 ? MV_LEFT  :
14749                         dx == +1 ? MV_RIGHT :
14750                         dy == -1 ? MV_UP    :
14751                         dy == +1 ? MV_DOWN  : MV_NONE);
14752   boolean can_continue_snapping = (level.continuous_snapping &&
14753                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14754
14755   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14756     return FALSE;
14757
14758   if (!player->active || !IN_LEV_FIELD(x, y))
14759     return FALSE;
14760
14761   if (dx && dy)
14762     return FALSE;
14763
14764   if (!dx && !dy)
14765   {
14766     if (player->MovPos == 0)
14767       player->is_pushing = FALSE;
14768
14769     player->is_snapping = FALSE;
14770
14771     if (player->MovPos == 0)
14772     {
14773       player->is_moving = FALSE;
14774       player->is_digging = FALSE;
14775       player->is_collecting = FALSE;
14776     }
14777
14778     return FALSE;
14779   }
14780
14781   // prevent snapping with already pressed snap key when not allowed
14782   if (player->is_snapping && !can_continue_snapping)
14783     return FALSE;
14784
14785   player->MovDir = snap_direction;
14786
14787   if (player->MovPos == 0)
14788   {
14789     player->is_moving = FALSE;
14790     player->is_digging = FALSE;
14791     player->is_collecting = FALSE;
14792   }
14793
14794   player->is_dropping = FALSE;
14795   player->is_dropping_pressed = FALSE;
14796   player->drop_pressed_delay = 0;
14797
14798   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14799     return FALSE;
14800
14801   player->is_snapping = TRUE;
14802   player->is_active = TRUE;
14803
14804   if (player->MovPos == 0)
14805   {
14806     player->is_moving = FALSE;
14807     player->is_digging = FALSE;
14808     player->is_collecting = FALSE;
14809   }
14810
14811   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14812     TEST_DrawLevelField(player->last_jx, player->last_jy);
14813
14814   TEST_DrawLevelField(x, y);
14815
14816   return TRUE;
14817 }
14818
14819 static boolean DropElement(struct PlayerInfo *player)
14820 {
14821   int old_element, new_element;
14822   int dropx = player->jx, dropy = player->jy;
14823   int drop_direction = player->MovDir;
14824   int drop_side = drop_direction;
14825   int drop_element = get_next_dropped_element(player);
14826
14827   /* do not drop an element on top of another element; when holding drop key
14828      pressed without moving, dropped element must move away before the next
14829      element can be dropped (this is especially important if the next element
14830      is dynamite, which can be placed on background for historical reasons) */
14831   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14832     return MP_ACTION;
14833
14834   if (IS_THROWABLE(drop_element))
14835   {
14836     dropx += GET_DX_FROM_DIR(drop_direction);
14837     dropy += GET_DY_FROM_DIR(drop_direction);
14838
14839     if (!IN_LEV_FIELD(dropx, dropy))
14840       return FALSE;
14841   }
14842
14843   old_element = Tile[dropx][dropy];     // old element at dropping position
14844   new_element = drop_element;           // default: no change when dropping
14845
14846   // check if player is active, not moving and ready to drop
14847   if (!player->active || player->MovPos || player->drop_delay > 0)
14848     return FALSE;
14849
14850   // check if player has anything that can be dropped
14851   if (new_element == EL_UNDEFINED)
14852     return FALSE;
14853
14854   // only set if player has anything that can be dropped
14855   player->is_dropping_pressed = TRUE;
14856
14857   // check if drop key was pressed long enough for EM style dynamite
14858   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14859     return FALSE;
14860
14861   // check if anything can be dropped at the current position
14862   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14863     return FALSE;
14864
14865   // collected custom elements can only be dropped on empty fields
14866   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14867     return FALSE;
14868
14869   if (old_element != EL_EMPTY)
14870     Back[dropx][dropy] = old_element;   // store old element on this field
14871
14872   ResetGfxAnimation(dropx, dropy);
14873   ResetRandomAnimationValue(dropx, dropy);
14874
14875   if (player->inventory_size > 0 ||
14876       player->inventory_infinite_element != EL_UNDEFINED)
14877   {
14878     if (player->inventory_size > 0)
14879     {
14880       player->inventory_size--;
14881
14882       DrawGameDoorValues();
14883
14884       if (new_element == EL_DYNAMITE)
14885         new_element = EL_DYNAMITE_ACTIVE;
14886       else if (new_element == EL_EM_DYNAMITE)
14887         new_element = EL_EM_DYNAMITE_ACTIVE;
14888       else if (new_element == EL_SP_DISK_RED)
14889         new_element = EL_SP_DISK_RED_ACTIVE;
14890     }
14891
14892     Tile[dropx][dropy] = new_element;
14893
14894     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14895       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14896                           el2img(Tile[dropx][dropy]), 0);
14897
14898     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14899
14900     // needed if previous element just changed to "empty" in the last frame
14901     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14902
14903     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14904                                player->index_bit, drop_side);
14905     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14906                                         CE_PLAYER_DROPS_X,
14907                                         player->index_bit, drop_side);
14908
14909     TestIfElementTouchesCustomElement(dropx, dropy);
14910   }
14911   else          // player is dropping a dyna bomb
14912   {
14913     player->dynabombs_left--;
14914
14915     Tile[dropx][dropy] = new_element;
14916
14917     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14918       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14919                           el2img(Tile[dropx][dropy]), 0);
14920
14921     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14922   }
14923
14924   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14925     InitField_WithBug1(dropx, dropy, FALSE);
14926
14927   new_element = Tile[dropx][dropy];     // element might have changed
14928
14929   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14930       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14931   {
14932     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14933       MovDir[dropx][dropy] = drop_direction;
14934
14935     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14936
14937     // do not cause impact style collision by dropping elements that can fall
14938     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14939   }
14940
14941   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14942   player->is_dropping = TRUE;
14943
14944   player->drop_pressed_delay = 0;
14945   player->is_dropping_pressed = FALSE;
14946
14947   player->drop_x = dropx;
14948   player->drop_y = dropy;
14949
14950   return TRUE;
14951 }
14952
14953 // ----------------------------------------------------------------------------
14954 // game sound playing functions
14955 // ----------------------------------------------------------------------------
14956
14957 static int *loop_sound_frame = NULL;
14958 static int *loop_sound_volume = NULL;
14959
14960 void InitPlayLevelSound(void)
14961 {
14962   int num_sounds = getSoundListSize();
14963
14964   checked_free(loop_sound_frame);
14965   checked_free(loop_sound_volume);
14966
14967   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14968   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14969 }
14970
14971 static void PlayLevelSound(int x, int y, int nr)
14972 {
14973   int sx = SCREENX(x), sy = SCREENY(y);
14974   int volume, stereo_position;
14975   int max_distance = 8;
14976   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14977
14978   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14979       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14980     return;
14981
14982   if (!IN_LEV_FIELD(x, y) ||
14983       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14984       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14985     return;
14986
14987   volume = SOUND_MAX_VOLUME;
14988
14989   if (!IN_SCR_FIELD(sx, sy))
14990   {
14991     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14992     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14993
14994     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14995   }
14996
14997   stereo_position = (SOUND_MAX_LEFT +
14998                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14999                      (SCR_FIELDX + 2 * max_distance));
15000
15001   if (IS_LOOP_SOUND(nr))
15002   {
15003     /* This assures that quieter loop sounds do not overwrite louder ones,
15004        while restarting sound volume comparison with each new game frame. */
15005
15006     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15007       return;
15008
15009     loop_sound_volume[nr] = volume;
15010     loop_sound_frame[nr] = FrameCounter;
15011   }
15012
15013   PlaySoundExt(nr, volume, stereo_position, type);
15014 }
15015
15016 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15017 {
15018   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15019                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15020                  y < LEVELY(BY1) ? LEVELY(BY1) :
15021                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15022                  sound_action);
15023 }
15024
15025 static void PlayLevelSoundAction(int x, int y, int action)
15026 {
15027   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15028 }
15029
15030 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15031 {
15032   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15033
15034   if (sound_effect != SND_UNDEFINED)
15035     PlayLevelSound(x, y, sound_effect);
15036 }
15037
15038 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15039                                               int action)
15040 {
15041   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15042
15043   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15044     PlayLevelSound(x, y, sound_effect);
15045 }
15046
15047 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15048 {
15049   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15050
15051   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15052     PlayLevelSound(x, y, sound_effect);
15053 }
15054
15055 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15056 {
15057   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15058
15059   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15060     StopSound(sound_effect);
15061 }
15062
15063 static int getLevelMusicNr(void)
15064 {
15065   if (levelset.music[level_nr] != MUS_UNDEFINED)
15066     return levelset.music[level_nr];            // from config file
15067   else
15068     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15069 }
15070
15071 static void FadeLevelSounds(void)
15072 {
15073   FadeSounds();
15074 }
15075
15076 static void FadeLevelMusic(void)
15077 {
15078   int music_nr = getLevelMusicNr();
15079   char *curr_music = getCurrentlyPlayingMusicFilename();
15080   char *next_music = getMusicInfoEntryFilename(music_nr);
15081
15082   if (!strEqual(curr_music, next_music))
15083     FadeMusic();
15084 }
15085
15086 void FadeLevelSoundsAndMusic(void)
15087 {
15088   FadeLevelSounds();
15089   FadeLevelMusic();
15090 }
15091
15092 static void PlayLevelMusic(void)
15093 {
15094   int music_nr = getLevelMusicNr();
15095   char *curr_music = getCurrentlyPlayingMusicFilename();
15096   char *next_music = getMusicInfoEntryFilename(music_nr);
15097
15098   if (!strEqual(curr_music, next_music))
15099     PlayMusicLoop(music_nr);
15100 }
15101
15102 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15103 {
15104   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15105   int offset = 0;
15106   int x = xx - offset;
15107   int y = yy - offset;
15108
15109   switch (sample)
15110   {
15111     case SOUND_blank:
15112       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15113       break;
15114
15115     case SOUND_roll:
15116       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15117       break;
15118
15119     case SOUND_stone:
15120       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15121       break;
15122
15123     case SOUND_nut:
15124       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15125       break;
15126
15127     case SOUND_crack:
15128       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15129       break;
15130
15131     case SOUND_bug:
15132       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15133       break;
15134
15135     case SOUND_tank:
15136       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15137       break;
15138
15139     case SOUND_android_clone:
15140       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15141       break;
15142
15143     case SOUND_android_move:
15144       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15145       break;
15146
15147     case SOUND_spring:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15149       break;
15150
15151     case SOUND_slurp:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15153       break;
15154
15155     case SOUND_eater:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15157       break;
15158
15159     case SOUND_eater_eat:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15161       break;
15162
15163     case SOUND_alien:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15165       break;
15166
15167     case SOUND_collect:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15169       break;
15170
15171     case SOUND_diamond:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15173       break;
15174
15175     case SOUND_squash:
15176       // !!! CHECK THIS !!!
15177 #if 1
15178       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15179 #else
15180       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15181 #endif
15182       break;
15183
15184     case SOUND_wonderfall:
15185       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15186       break;
15187
15188     case SOUND_drip:
15189       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15190       break;
15191
15192     case SOUND_push:
15193       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15194       break;
15195
15196     case SOUND_dirt:
15197       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15198       break;
15199
15200     case SOUND_acid:
15201       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15202       break;
15203
15204     case SOUND_ball:
15205       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15206       break;
15207
15208     case SOUND_slide:
15209       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15210       break;
15211
15212     case SOUND_wonder:
15213       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15214       break;
15215
15216     case SOUND_door:
15217       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15218       break;
15219
15220     case SOUND_exit_open:
15221       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15222       break;
15223
15224     case SOUND_exit_leave:
15225       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15226       break;
15227
15228     case SOUND_dynamite:
15229       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15230       break;
15231
15232     case SOUND_tick:
15233       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15234       break;
15235
15236     case SOUND_press:
15237       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15238       break;
15239
15240     case SOUND_wheel:
15241       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15242       break;
15243
15244     case SOUND_boom:
15245       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15246       break;
15247
15248     case SOUND_die:
15249       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15250       break;
15251
15252     case SOUND_time:
15253       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15254       break;
15255
15256     default:
15257       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15258       break;
15259   }
15260 }
15261
15262 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15263 {
15264   int element = map_element_SP_to_RND(element_sp);
15265   int action = map_action_SP_to_RND(action_sp);
15266   int offset = (setup.sp_show_border_elements ? 0 : 1);
15267   int x = xx - offset;
15268   int y = yy - offset;
15269
15270   PlayLevelSoundElementAction(x, y, element, action);
15271 }
15272
15273 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15274 {
15275   int element = map_element_MM_to_RND(element_mm);
15276   int action = map_action_MM_to_RND(action_mm);
15277   int offset = 0;
15278   int x = xx - offset;
15279   int y = yy - offset;
15280
15281   if (!IS_MM_ELEMENT(element))
15282     element = EL_MM_DEFAULT;
15283
15284   PlayLevelSoundElementAction(x, y, element, action);
15285 }
15286
15287 void PlaySound_MM(int sound_mm)
15288 {
15289   int sound = map_sound_MM_to_RND(sound_mm);
15290
15291   if (sound == SND_UNDEFINED)
15292     return;
15293
15294   PlaySound(sound);
15295 }
15296
15297 void PlaySoundLoop_MM(int sound_mm)
15298 {
15299   int sound = map_sound_MM_to_RND(sound_mm);
15300
15301   if (sound == SND_UNDEFINED)
15302     return;
15303
15304   PlaySoundLoop(sound);
15305 }
15306
15307 void StopSound_MM(int sound_mm)
15308 {
15309   int sound = map_sound_MM_to_RND(sound_mm);
15310
15311   if (sound == SND_UNDEFINED)
15312     return;
15313
15314   StopSound(sound);
15315 }
15316
15317 void RaiseScore(int value)
15318 {
15319   game.score += value;
15320
15321   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15322
15323   DisplayGameControlValues();
15324 }
15325
15326 void RaiseScoreElement(int element)
15327 {
15328   switch (element)
15329   {
15330     case EL_EMERALD:
15331     case EL_BD_DIAMOND:
15332     case EL_EMERALD_YELLOW:
15333     case EL_EMERALD_RED:
15334     case EL_EMERALD_PURPLE:
15335     case EL_SP_INFOTRON:
15336       RaiseScore(level.score[SC_EMERALD]);
15337       break;
15338     case EL_DIAMOND:
15339       RaiseScore(level.score[SC_DIAMOND]);
15340       break;
15341     case EL_CRYSTAL:
15342       RaiseScore(level.score[SC_CRYSTAL]);
15343       break;
15344     case EL_PEARL:
15345       RaiseScore(level.score[SC_PEARL]);
15346       break;
15347     case EL_BUG:
15348     case EL_BD_BUTTERFLY:
15349     case EL_SP_ELECTRON:
15350       RaiseScore(level.score[SC_BUG]);
15351       break;
15352     case EL_SPACESHIP:
15353     case EL_BD_FIREFLY:
15354     case EL_SP_SNIKSNAK:
15355       RaiseScore(level.score[SC_SPACESHIP]);
15356       break;
15357     case EL_YAMYAM:
15358     case EL_DARK_YAMYAM:
15359       RaiseScore(level.score[SC_YAMYAM]);
15360       break;
15361     case EL_ROBOT:
15362       RaiseScore(level.score[SC_ROBOT]);
15363       break;
15364     case EL_PACMAN:
15365       RaiseScore(level.score[SC_PACMAN]);
15366       break;
15367     case EL_NUT:
15368       RaiseScore(level.score[SC_NUT]);
15369       break;
15370     case EL_DYNAMITE:
15371     case EL_EM_DYNAMITE:
15372     case EL_SP_DISK_RED:
15373     case EL_DYNABOMB_INCREASE_NUMBER:
15374     case EL_DYNABOMB_INCREASE_SIZE:
15375     case EL_DYNABOMB_INCREASE_POWER:
15376       RaiseScore(level.score[SC_DYNAMITE]);
15377       break;
15378     case EL_SHIELD_NORMAL:
15379     case EL_SHIELD_DEADLY:
15380       RaiseScore(level.score[SC_SHIELD]);
15381       break;
15382     case EL_EXTRA_TIME:
15383       RaiseScore(level.extra_time_score);
15384       break;
15385     case EL_KEY_1:
15386     case EL_KEY_2:
15387     case EL_KEY_3:
15388     case EL_KEY_4:
15389     case EL_EM_KEY_1:
15390     case EL_EM_KEY_2:
15391     case EL_EM_KEY_3:
15392     case EL_EM_KEY_4:
15393     case EL_EMC_KEY_5:
15394     case EL_EMC_KEY_6:
15395     case EL_EMC_KEY_7:
15396     case EL_EMC_KEY_8:
15397     case EL_DC_KEY_WHITE:
15398       RaiseScore(level.score[SC_KEY]);
15399       break;
15400     default:
15401       RaiseScore(element_info[element].collect_score);
15402       break;
15403   }
15404 }
15405
15406 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15407 {
15408   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15409   {
15410     if (!quick_quit)
15411     {
15412       // prevent short reactivation of overlay buttons while closing door
15413       SetOverlayActive(FALSE);
15414
15415       // door may still be open due to skipped or envelope style request
15416       CloseDoor(DOOR_CLOSE_1);
15417     }
15418
15419     if (network.enabled)
15420       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15421     else
15422     {
15423       if (quick_quit)
15424         FadeSkipNextFadeIn();
15425
15426       SetGameStatus(GAME_MODE_MAIN);
15427
15428       DrawMainMenu();
15429     }
15430   }
15431   else          // continue playing the game
15432   {
15433     if (tape.playing && tape.deactivate_display)
15434       TapeDeactivateDisplayOff(TRUE);
15435
15436     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15437
15438     if (tape.playing && tape.deactivate_display)
15439       TapeDeactivateDisplayOn();
15440   }
15441 }
15442
15443 void RequestQuitGame(boolean escape_key_pressed)
15444 {
15445   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15446   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15447                         level_editor_test_game);
15448   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15449                           quick_quit);
15450
15451   RequestQuitGameExt(skip_request, quick_quit,
15452                      "Do you really want to quit the game?");
15453 }
15454
15455 void RequestRestartGame(char *message)
15456 {
15457   game.restart_game_message = NULL;
15458
15459   boolean has_started_game = hasStartedNetworkGame();
15460   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15461
15462   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15463   {
15464     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15465   }
15466   else
15467   {
15468     // needed in case of envelope request to close game panel
15469     CloseDoor(DOOR_CLOSE_1);
15470
15471     SetGameStatus(GAME_MODE_MAIN);
15472
15473     DrawMainMenu();
15474   }
15475 }
15476
15477 void CheckGameOver(void)
15478 {
15479   static boolean last_game_over = FALSE;
15480   static int game_over_delay = 0;
15481   int game_over_delay_value = 50;
15482   boolean game_over = checkGameFailed();
15483
15484   // do not handle game over if request dialog is already active
15485   if (game.request_active)
15486     return;
15487
15488   // do not ask to play again if game was never actually played
15489   if (!game.GamePlayed)
15490     return;
15491
15492   if (!game_over)
15493   {
15494     last_game_over = FALSE;
15495     game_over_delay = game_over_delay_value;
15496
15497     return;
15498   }
15499
15500   if (game_over_delay > 0)
15501   {
15502     game_over_delay--;
15503
15504     return;
15505   }
15506
15507   if (last_game_over != game_over)
15508     game.restart_game_message = (hasStartedNetworkGame() ?
15509                                  "Game over! Play it again?" :
15510                                  "Game over!");
15511
15512   last_game_over = game_over;
15513 }
15514
15515 boolean checkGameSolved(void)
15516 {
15517   // set for all game engines if level was solved
15518   return game.LevelSolved_GameEnd;
15519 }
15520
15521 boolean checkGameFailed(void)
15522 {
15523   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15524     return (game_em.game_over && !game_em.level_solved);
15525   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15526     return (game_sp.game_over && !game_sp.level_solved);
15527   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15528     return (game_mm.game_over && !game_mm.level_solved);
15529   else                          // GAME_ENGINE_TYPE_RND
15530     return (game.GameOver && !game.LevelSolved);
15531 }
15532
15533 boolean checkGameEnded(void)
15534 {
15535   return (checkGameSolved() || checkGameFailed());
15536 }
15537
15538
15539 // ----------------------------------------------------------------------------
15540 // random generator functions
15541 // ----------------------------------------------------------------------------
15542
15543 unsigned int InitEngineRandom_RND(int seed)
15544 {
15545   game.num_random_calls = 0;
15546
15547   return InitEngineRandom(seed);
15548 }
15549
15550 unsigned int RND(int max)
15551 {
15552   if (max > 0)
15553   {
15554     game.num_random_calls++;
15555
15556     return GetEngineRandom(max);
15557   }
15558
15559   return 0;
15560 }
15561
15562
15563 // ----------------------------------------------------------------------------
15564 // game engine snapshot handling functions
15565 // ----------------------------------------------------------------------------
15566
15567 struct EngineSnapshotInfo
15568 {
15569   // runtime values for custom element collect score
15570   int collect_score[NUM_CUSTOM_ELEMENTS];
15571
15572   // runtime values for group element choice position
15573   int choice_pos[NUM_GROUP_ELEMENTS];
15574
15575   // runtime values for belt position animations
15576   int belt_graphic[4][NUM_BELT_PARTS];
15577   int belt_anim_mode[4][NUM_BELT_PARTS];
15578 };
15579
15580 static struct EngineSnapshotInfo engine_snapshot_rnd;
15581 static char *snapshot_level_identifier = NULL;
15582 static int snapshot_level_nr = -1;
15583
15584 static void SaveEngineSnapshotValues_RND(void)
15585 {
15586   static int belt_base_active_element[4] =
15587   {
15588     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15589     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15590     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15591     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15592   };
15593   int i, j;
15594
15595   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15596   {
15597     int element = EL_CUSTOM_START + i;
15598
15599     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15600   }
15601
15602   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15603   {
15604     int element = EL_GROUP_START + i;
15605
15606     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15607   }
15608
15609   for (i = 0; i < 4; i++)
15610   {
15611     for (j = 0; j < NUM_BELT_PARTS; j++)
15612     {
15613       int element = belt_base_active_element[i] + j;
15614       int graphic = el2img(element);
15615       int anim_mode = graphic_info[graphic].anim_mode;
15616
15617       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15618       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15619     }
15620   }
15621 }
15622
15623 static void LoadEngineSnapshotValues_RND(void)
15624 {
15625   unsigned int num_random_calls = game.num_random_calls;
15626   int i, j;
15627
15628   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15629   {
15630     int element = EL_CUSTOM_START + i;
15631
15632     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15633   }
15634
15635   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15636   {
15637     int element = EL_GROUP_START + i;
15638
15639     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15640   }
15641
15642   for (i = 0; i < 4; i++)
15643   {
15644     for (j = 0; j < NUM_BELT_PARTS; j++)
15645     {
15646       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15647       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15648
15649       graphic_info[graphic].anim_mode = anim_mode;
15650     }
15651   }
15652
15653   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15654   {
15655     InitRND(tape.random_seed);
15656     for (i = 0; i < num_random_calls; i++)
15657       RND(1);
15658   }
15659
15660   if (game.num_random_calls != num_random_calls)
15661   {
15662     Error("number of random calls out of sync");
15663     Error("number of random calls should be %d", num_random_calls);
15664     Error("number of random calls is %d", game.num_random_calls);
15665
15666     Fail("this should not happen -- please debug");
15667   }
15668 }
15669
15670 void FreeEngineSnapshotSingle(void)
15671 {
15672   FreeSnapshotSingle();
15673
15674   setString(&snapshot_level_identifier, NULL);
15675   snapshot_level_nr = -1;
15676 }
15677
15678 void FreeEngineSnapshotList(void)
15679 {
15680   FreeSnapshotList();
15681 }
15682
15683 static ListNode *SaveEngineSnapshotBuffers(void)
15684 {
15685   ListNode *buffers = NULL;
15686
15687   // copy some special values to a structure better suited for the snapshot
15688
15689   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15690     SaveEngineSnapshotValues_RND();
15691   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15692     SaveEngineSnapshotValues_EM();
15693   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15694     SaveEngineSnapshotValues_SP(&buffers);
15695   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15696     SaveEngineSnapshotValues_MM(&buffers);
15697
15698   // save values stored in special snapshot structure
15699
15700   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15701     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15702   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15703     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15704   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15705     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15706   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15707     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15708
15709   // save further RND engine values
15710
15711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15714
15715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15720
15721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15724
15725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15726
15727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15729
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15748
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15751
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15755
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15758
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15764
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15767
15768 #if 0
15769   ListNode *node = engine_snapshot_list_rnd;
15770   int num_bytes = 0;
15771
15772   while (node != NULL)
15773   {
15774     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15775
15776     node = node->next;
15777   }
15778
15779   Debug("game:playing:SaveEngineSnapshotBuffers",
15780         "size of engine snapshot: %d bytes", num_bytes);
15781 #endif
15782
15783   return buffers;
15784 }
15785
15786 void SaveEngineSnapshotSingle(void)
15787 {
15788   ListNode *buffers = SaveEngineSnapshotBuffers();
15789
15790   // finally save all snapshot buffers to single snapshot
15791   SaveSnapshotSingle(buffers);
15792
15793   // save level identification information
15794   setString(&snapshot_level_identifier, leveldir_current->identifier);
15795   snapshot_level_nr = level_nr;
15796 }
15797
15798 boolean CheckSaveEngineSnapshotToList(void)
15799 {
15800   boolean save_snapshot =
15801     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15802      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15803       game.snapshot.changed_action) ||
15804      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15805       game.snapshot.collected_item));
15806
15807   game.snapshot.changed_action = FALSE;
15808   game.snapshot.collected_item = FALSE;
15809   game.snapshot.save_snapshot = save_snapshot;
15810
15811   return save_snapshot;
15812 }
15813
15814 void SaveEngineSnapshotToList(void)
15815 {
15816   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15817       tape.quick_resume)
15818     return;
15819
15820   ListNode *buffers = SaveEngineSnapshotBuffers();
15821
15822   // finally save all snapshot buffers to snapshot list
15823   SaveSnapshotToList(buffers);
15824 }
15825
15826 void SaveEngineSnapshotToListInitial(void)
15827 {
15828   FreeEngineSnapshotList();
15829
15830   SaveEngineSnapshotToList();
15831 }
15832
15833 static void LoadEngineSnapshotValues(void)
15834 {
15835   // restore special values from snapshot structure
15836
15837   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15838     LoadEngineSnapshotValues_RND();
15839   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15840     LoadEngineSnapshotValues_EM();
15841   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15842     LoadEngineSnapshotValues_SP();
15843   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15844     LoadEngineSnapshotValues_MM();
15845 }
15846
15847 void LoadEngineSnapshotSingle(void)
15848 {
15849   LoadSnapshotSingle();
15850
15851   LoadEngineSnapshotValues();
15852 }
15853
15854 static void LoadEngineSnapshot_Undo(int steps)
15855 {
15856   LoadSnapshotFromList_Older(steps);
15857
15858   LoadEngineSnapshotValues();
15859 }
15860
15861 static void LoadEngineSnapshot_Redo(int steps)
15862 {
15863   LoadSnapshotFromList_Newer(steps);
15864
15865   LoadEngineSnapshotValues();
15866 }
15867
15868 boolean CheckEngineSnapshotSingle(void)
15869 {
15870   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15871           snapshot_level_nr == level_nr);
15872 }
15873
15874 boolean CheckEngineSnapshotList(void)
15875 {
15876   return CheckSnapshotList();
15877 }
15878
15879
15880 // ---------- new game button stuff -------------------------------------------
15881
15882 static struct
15883 {
15884   int graphic;
15885   struct XY *pos;
15886   int gadget_id;
15887   boolean *setup_value;
15888   boolean allowed_on_tape;
15889   boolean is_touch_button;
15890   char *infotext;
15891 } gamebutton_info[NUM_GAME_BUTTONS] =
15892 {
15893   {
15894     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15895     GAME_CTRL_ID_STOP,                          NULL,
15896     TRUE, FALSE,                                "stop game"
15897   },
15898   {
15899     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15900     GAME_CTRL_ID_PAUSE,                         NULL,
15901     TRUE, FALSE,                                "pause game"
15902   },
15903   {
15904     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15905     GAME_CTRL_ID_PLAY,                          NULL,
15906     TRUE, FALSE,                                "play game"
15907   },
15908   {
15909     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15910     GAME_CTRL_ID_UNDO,                          NULL,
15911     TRUE, FALSE,                                "undo step"
15912   },
15913   {
15914     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15915     GAME_CTRL_ID_REDO,                          NULL,
15916     TRUE, FALSE,                                "redo step"
15917   },
15918   {
15919     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15920     GAME_CTRL_ID_SAVE,                          NULL,
15921     TRUE, FALSE,                                "save game"
15922   },
15923   {
15924     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15925     GAME_CTRL_ID_PAUSE2,                        NULL,
15926     TRUE, FALSE,                                "pause game"
15927   },
15928   {
15929     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15930     GAME_CTRL_ID_LOAD,                          NULL,
15931     TRUE, FALSE,                                "load game"
15932   },
15933   {
15934     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15935     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15936     FALSE, FALSE,                               "stop game"
15937   },
15938   {
15939     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15940     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15941     FALSE, FALSE,                               "pause game"
15942   },
15943   {
15944     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15945     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15946     FALSE, FALSE,                               "play game"
15947   },
15948   {
15949     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15950     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15951     FALSE, TRUE,                                "stop game"
15952   },
15953   {
15954     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15955     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15956     FALSE, TRUE,                                "pause game"
15957   },
15958   {
15959     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15960     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15961     TRUE, FALSE,                                "background music on/off"
15962   },
15963   {
15964     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15965     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15966     TRUE, FALSE,                                "sound loops on/off"
15967   },
15968   {
15969     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15970     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15971     TRUE, FALSE,                                "normal sounds on/off"
15972   },
15973   {
15974     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15975     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15976     FALSE, FALSE,                               "background music on/off"
15977   },
15978   {
15979     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15980     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15981     FALSE, FALSE,                               "sound loops on/off"
15982   },
15983   {
15984     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15985     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15986     FALSE, FALSE,                               "normal sounds on/off"
15987   }
15988 };
15989
15990 void CreateGameButtons(void)
15991 {
15992   int i;
15993
15994   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15995   {
15996     int graphic = gamebutton_info[i].graphic;
15997     struct GraphicInfo *gfx = &graphic_info[graphic];
15998     struct XY *pos = gamebutton_info[i].pos;
15999     struct GadgetInfo *gi;
16000     int button_type;
16001     boolean checked;
16002     unsigned int event_mask;
16003     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16004     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16005     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16006     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16007     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16008     int gd_x   = gfx->src_x;
16009     int gd_y   = gfx->src_y;
16010     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16011     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16012     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16013     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16014     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16015     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16016     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16017     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16018     int id = i;
16019
16020     if (gfx->bitmap == NULL)
16021     {
16022       game_gadget[id] = NULL;
16023
16024       continue;
16025     }
16026
16027     if (id == GAME_CTRL_ID_STOP ||
16028         id == GAME_CTRL_ID_PANEL_STOP ||
16029         id == GAME_CTRL_ID_TOUCH_STOP ||
16030         id == GAME_CTRL_ID_PLAY ||
16031         id == GAME_CTRL_ID_PANEL_PLAY ||
16032         id == GAME_CTRL_ID_SAVE ||
16033         id == GAME_CTRL_ID_LOAD)
16034     {
16035       button_type = GD_TYPE_NORMAL_BUTTON;
16036       checked = FALSE;
16037       event_mask = GD_EVENT_RELEASED;
16038     }
16039     else if (id == GAME_CTRL_ID_UNDO ||
16040              id == GAME_CTRL_ID_REDO)
16041     {
16042       button_type = GD_TYPE_NORMAL_BUTTON;
16043       checked = FALSE;
16044       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16045     }
16046     else
16047     {
16048       button_type = GD_TYPE_CHECK_BUTTON;
16049       checked = (gamebutton_info[i].setup_value != NULL ?
16050                  *gamebutton_info[i].setup_value : FALSE);
16051       event_mask = GD_EVENT_PRESSED;
16052     }
16053
16054     gi = CreateGadget(GDI_CUSTOM_ID, id,
16055                       GDI_IMAGE_ID, graphic,
16056                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16057                       GDI_X, base_x + x,
16058                       GDI_Y, base_y + y,
16059                       GDI_WIDTH, gfx->width,
16060                       GDI_HEIGHT, gfx->height,
16061                       GDI_TYPE, button_type,
16062                       GDI_STATE, GD_BUTTON_UNPRESSED,
16063                       GDI_CHECKED, checked,
16064                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16065                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16066                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16067                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16068                       GDI_DIRECT_DRAW, FALSE,
16069                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16070                       GDI_EVENT_MASK, event_mask,
16071                       GDI_CALLBACK_ACTION, HandleGameButtons,
16072                       GDI_END);
16073
16074     if (gi == NULL)
16075       Fail("cannot create gadget");
16076
16077     game_gadget[id] = gi;
16078   }
16079 }
16080
16081 void FreeGameButtons(void)
16082 {
16083   int i;
16084
16085   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16086     FreeGadget(game_gadget[i]);
16087 }
16088
16089 static void UnmapGameButtonsAtSamePosition(int id)
16090 {
16091   int i;
16092
16093   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16094     if (i != id &&
16095         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16096         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16097       UnmapGadget(game_gadget[i]);
16098 }
16099
16100 static void UnmapGameButtonsAtSamePosition_All(void)
16101 {
16102   if (setup.show_snapshot_buttons)
16103   {
16104     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16105     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16106     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16107   }
16108   else
16109   {
16110     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16111     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16112     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16113
16114     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16115     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16116     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16117   }
16118 }
16119
16120 static void MapGameButtonsAtSamePosition(int id)
16121 {
16122   int i;
16123
16124   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16125     if (i != id &&
16126         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16127         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16128       MapGadget(game_gadget[i]);
16129
16130   UnmapGameButtonsAtSamePosition_All();
16131 }
16132
16133 void MapUndoRedoButtons(void)
16134 {
16135   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16136   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16137
16138   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16139   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16140 }
16141
16142 void UnmapUndoRedoButtons(void)
16143 {
16144   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16145   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16146
16147   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16148   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16149 }
16150
16151 void ModifyPauseButtons(void)
16152 {
16153   static int ids[] =
16154   {
16155     GAME_CTRL_ID_PAUSE,
16156     GAME_CTRL_ID_PAUSE2,
16157     GAME_CTRL_ID_PANEL_PAUSE,
16158     GAME_CTRL_ID_TOUCH_PAUSE,
16159     -1
16160   };
16161   int i;
16162
16163   for (i = 0; ids[i] > -1; i++)
16164     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16165 }
16166
16167 static void MapGameButtonsExt(boolean on_tape)
16168 {
16169   int i;
16170
16171   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16172     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16173         i != GAME_CTRL_ID_UNDO &&
16174         i != GAME_CTRL_ID_REDO)
16175       MapGadget(game_gadget[i]);
16176
16177   UnmapGameButtonsAtSamePosition_All();
16178
16179   RedrawGameButtons();
16180 }
16181
16182 static void UnmapGameButtonsExt(boolean on_tape)
16183 {
16184   int i;
16185
16186   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16187     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16188       UnmapGadget(game_gadget[i]);
16189 }
16190
16191 static void RedrawGameButtonsExt(boolean on_tape)
16192 {
16193   int i;
16194
16195   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16196     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16197       RedrawGadget(game_gadget[i]);
16198 }
16199
16200 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16201 {
16202   if (gi == NULL)
16203     return;
16204
16205   gi->checked = state;
16206 }
16207
16208 static void RedrawSoundButtonGadget(int id)
16209 {
16210   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16211              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16212              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16213              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16214              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16215              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16216              id);
16217
16218   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16219   RedrawGadget(game_gadget[id2]);
16220 }
16221
16222 void MapGameButtons(void)
16223 {
16224   MapGameButtonsExt(FALSE);
16225 }
16226
16227 void UnmapGameButtons(void)
16228 {
16229   UnmapGameButtonsExt(FALSE);
16230 }
16231
16232 void RedrawGameButtons(void)
16233 {
16234   RedrawGameButtonsExt(FALSE);
16235 }
16236
16237 void MapGameButtonsOnTape(void)
16238 {
16239   MapGameButtonsExt(TRUE);
16240 }
16241
16242 void UnmapGameButtonsOnTape(void)
16243 {
16244   UnmapGameButtonsExt(TRUE);
16245 }
16246
16247 void RedrawGameButtonsOnTape(void)
16248 {
16249   RedrawGameButtonsExt(TRUE);
16250 }
16251
16252 static void GameUndoRedoExt(void)
16253 {
16254   ClearPlayerAction();
16255
16256   tape.pausing = TRUE;
16257
16258   RedrawPlayfield();
16259   UpdateAndDisplayGameControlValues();
16260
16261   DrawCompleteVideoDisplay();
16262   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16263   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16264   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16265
16266   BackToFront();
16267 }
16268
16269 static void GameUndo(int steps)
16270 {
16271   if (!CheckEngineSnapshotList())
16272     return;
16273
16274   LoadEngineSnapshot_Undo(steps);
16275
16276   GameUndoRedoExt();
16277 }
16278
16279 static void GameRedo(int steps)
16280 {
16281   if (!CheckEngineSnapshotList())
16282     return;
16283
16284   LoadEngineSnapshot_Redo(steps);
16285
16286   GameUndoRedoExt();
16287 }
16288
16289 static void HandleGameButtonsExt(int id, int button)
16290 {
16291   static boolean game_undo_executed = FALSE;
16292   int steps = BUTTON_STEPSIZE(button);
16293   boolean handle_game_buttons =
16294     (game_status == GAME_MODE_PLAYING ||
16295      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16296
16297   if (!handle_game_buttons)
16298     return;
16299
16300   switch (id)
16301   {
16302     case GAME_CTRL_ID_STOP:
16303     case GAME_CTRL_ID_PANEL_STOP:
16304     case GAME_CTRL_ID_TOUCH_STOP:
16305       if (game_status == GAME_MODE_MAIN)
16306         break;
16307
16308       if (tape.playing)
16309         TapeStop();
16310       else
16311         RequestQuitGame(FALSE);
16312
16313       break;
16314
16315     case GAME_CTRL_ID_PAUSE:
16316     case GAME_CTRL_ID_PAUSE2:
16317     case GAME_CTRL_ID_PANEL_PAUSE:
16318     case GAME_CTRL_ID_TOUCH_PAUSE:
16319       if (network.enabled && game_status == GAME_MODE_PLAYING)
16320       {
16321         if (tape.pausing)
16322           SendToServer_ContinuePlaying();
16323         else
16324           SendToServer_PausePlaying();
16325       }
16326       else
16327         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16328
16329       game_undo_executed = FALSE;
16330
16331       break;
16332
16333     case GAME_CTRL_ID_PLAY:
16334     case GAME_CTRL_ID_PANEL_PLAY:
16335       if (game_status == GAME_MODE_MAIN)
16336       {
16337         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16338       }
16339       else if (tape.pausing)
16340       {
16341         if (network.enabled)
16342           SendToServer_ContinuePlaying();
16343         else
16344           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16345       }
16346       break;
16347
16348     case GAME_CTRL_ID_UNDO:
16349       // Important: When using "save snapshot when collecting an item" mode,
16350       // load last (current) snapshot for first "undo" after pressing "pause"
16351       // (else the last-but-one snapshot would be loaded, because the snapshot
16352       // pointer already points to the last snapshot when pressing "pause",
16353       // which is fine for "every step/move" mode, but not for "every collect")
16354       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16355           !game_undo_executed)
16356         steps--;
16357
16358       game_undo_executed = TRUE;
16359
16360       GameUndo(steps);
16361       break;
16362
16363     case GAME_CTRL_ID_REDO:
16364       GameRedo(steps);
16365       break;
16366
16367     case GAME_CTRL_ID_SAVE:
16368       TapeQuickSave();
16369       break;
16370
16371     case GAME_CTRL_ID_LOAD:
16372       TapeQuickLoad();
16373       break;
16374
16375     case SOUND_CTRL_ID_MUSIC:
16376     case SOUND_CTRL_ID_PANEL_MUSIC:
16377       if (setup.sound_music)
16378       { 
16379         setup.sound_music = FALSE;
16380
16381         FadeMusic();
16382       }
16383       else if (audio.music_available)
16384       { 
16385         setup.sound = setup.sound_music = TRUE;
16386
16387         SetAudioMode(setup.sound);
16388
16389         if (game_status == GAME_MODE_PLAYING)
16390           PlayLevelMusic();
16391       }
16392
16393       RedrawSoundButtonGadget(id);
16394
16395       break;
16396
16397     case SOUND_CTRL_ID_LOOPS:
16398     case SOUND_CTRL_ID_PANEL_LOOPS:
16399       if (setup.sound_loops)
16400         setup.sound_loops = FALSE;
16401       else if (audio.loops_available)
16402       {
16403         setup.sound = setup.sound_loops = TRUE;
16404
16405         SetAudioMode(setup.sound);
16406       }
16407
16408       RedrawSoundButtonGadget(id);
16409
16410       break;
16411
16412     case SOUND_CTRL_ID_SIMPLE:
16413     case SOUND_CTRL_ID_PANEL_SIMPLE:
16414       if (setup.sound_simple)
16415         setup.sound_simple = FALSE;
16416       else if (audio.sound_available)
16417       {
16418         setup.sound = setup.sound_simple = TRUE;
16419
16420         SetAudioMode(setup.sound);
16421       }
16422
16423       RedrawSoundButtonGadget(id);
16424
16425       break;
16426
16427     default:
16428       break;
16429   }
16430 }
16431
16432 static void HandleGameButtons(struct GadgetInfo *gi)
16433 {
16434   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16435 }
16436
16437 void HandleSoundButtonKeys(Key key)
16438 {
16439   if (key == setup.shortcut.sound_simple)
16440     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16441   else if (key == setup.shortcut.sound_loops)
16442     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16443   else if (key == setup.shortcut.sound_music)
16444     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16445 }