moved adding new score entry to separate 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 static int addScoreEntry(void)
5045 {
5046   int i, l;
5047   int position = -1;
5048   boolean one_score_entry_per_name = !program.many_scores_per_name;
5049
5050   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5051       game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
5052     return -1;
5053
5054   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5055   {
5056     struct ScoreEntry *entry = &scores.entry[i];
5057     boolean score_is_better = (game.score_final      >  entry->score);
5058     boolean score_is_equal  = (game.score_final      == entry->score);
5059     boolean time_is_better  = (game.score_time_final <  entry->time);
5060     boolean time_is_equal   = (game.score_time_final == entry->time);
5061     boolean better_by_score = (score_is_better ||
5062                                (score_is_equal && time_is_better));
5063     boolean better_by_time  = (time_is_better ||
5064                                (time_is_equal && score_is_better));
5065     boolean is_better = (level.rate_time_over_score ? better_by_time :
5066                          better_by_score);
5067     boolean entry_is_empty = (entry->score == 0 &&
5068                               entry->time == 0);
5069
5070     if (is_better || entry_is_empty)
5071     {
5072       // player has made it to the hall of fame
5073
5074       if (i < MAX_SCORE_ENTRIES - 1)
5075       {
5076         int m = MAX_SCORE_ENTRIES - 1;
5077
5078         if (one_score_entry_per_name)
5079         {
5080           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5081             if (strEqual(setup.player_name, scores.entry[l].name))
5082               m = l;
5083
5084           if (m == i)   // player's new highscore overwrites his old one
5085             goto put_into_list;
5086         }
5087
5088         for (l = m; l > i; l--)
5089         {
5090           strcpy(scores.entry[l].name, scores.entry[l - 1].name);
5091           scores.entry[l].score = scores.entry[l - 1].score;
5092           scores.entry[l].time  = scores.entry[l - 1].time;
5093         }
5094       }
5095
5096       put_into_list:
5097
5098       strcpy(entry->tape_basename, tape.score_tape_basename);
5099       strncpy(entry->name, setup.player_name, MAX_PLAYER_NAME_LEN);
5100       entry->name[MAX_PLAYER_NAME_LEN] = '\0';
5101       entry->score = game.score_final;
5102       entry->time = game.score_time_final;
5103       position = i;
5104
5105       break;
5106     }
5107     else if (one_score_entry_per_name &&
5108              !strncmp(setup.player_name, entry->name, MAX_PLAYER_NAME_LEN))
5109       break;    // player already there with a higher score
5110   }
5111
5112   return position;
5113 }
5114
5115 int NewHighScore(int level_nr)
5116 {
5117   LoadScore(level_nr);
5118
5119   int position = addScoreEntry();
5120
5121   if (position >= 0)
5122   {
5123     SaveScoreTape(level_nr);
5124     SaveScore(level_nr);
5125   }
5126
5127   return position;
5128 }
5129
5130 static int getElementMoveStepsizeExt(int x, int y, int direction)
5131 {
5132   int element = Tile[x][y];
5133   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5134   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5135   int horiz_move = (dx != 0);
5136   int sign = (horiz_move ? dx : dy);
5137   int step = sign * element_info[element].move_stepsize;
5138
5139   // special values for move stepsize for spring and things on conveyor belt
5140   if (horiz_move)
5141   {
5142     if (CAN_FALL(element) &&
5143         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5144       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5145     else if (element == EL_SPRING)
5146       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5147   }
5148
5149   return step;
5150 }
5151
5152 static int getElementMoveStepsize(int x, int y)
5153 {
5154   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5155 }
5156
5157 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5158 {
5159   if (player->GfxAction != action || player->GfxDir != dir)
5160   {
5161     player->GfxAction = action;
5162     player->GfxDir = dir;
5163     player->Frame = 0;
5164     player->StepFrame = 0;
5165   }
5166 }
5167
5168 static void ResetGfxFrame(int x, int y)
5169 {
5170   // profiling showed that "autotest" spends 10~20% of its time in this function
5171   if (DrawingDeactivatedField())
5172     return;
5173
5174   int element = Tile[x][y];
5175   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5176
5177   if (graphic_info[graphic].anim_global_sync)
5178     GfxFrame[x][y] = FrameCounter;
5179   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5180     GfxFrame[x][y] = CustomValue[x][y];
5181   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5182     GfxFrame[x][y] = element_info[element].collect_score;
5183   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5184     GfxFrame[x][y] = ChangeDelay[x][y];
5185 }
5186
5187 static void ResetGfxAnimation(int x, int y)
5188 {
5189   GfxAction[x][y] = ACTION_DEFAULT;
5190   GfxDir[x][y] = MovDir[x][y];
5191   GfxFrame[x][y] = 0;
5192
5193   ResetGfxFrame(x, y);
5194 }
5195
5196 static void ResetRandomAnimationValue(int x, int y)
5197 {
5198   GfxRandom[x][y] = INIT_GFX_RANDOM();
5199 }
5200
5201 static void InitMovingField(int x, int y, int direction)
5202 {
5203   int element = Tile[x][y];
5204   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5205   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5206   int newx = x + dx;
5207   int newy = y + dy;
5208   boolean is_moving_before, is_moving_after;
5209
5210   // check if element was/is moving or being moved before/after mode change
5211   is_moving_before = (WasJustMoving[x][y] != 0);
5212   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5213
5214   // reset animation only for moving elements which change direction of moving
5215   // or which just started or stopped moving
5216   // (else CEs with property "can move" / "not moving" are reset each frame)
5217   if (is_moving_before != is_moving_after ||
5218       direction != MovDir[x][y])
5219     ResetGfxAnimation(x, y);
5220
5221   MovDir[x][y] = direction;
5222   GfxDir[x][y] = direction;
5223
5224   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5225                      direction == MV_DOWN && CAN_FALL(element) ?
5226                      ACTION_FALLING : ACTION_MOVING);
5227
5228   // this is needed for CEs with property "can move" / "not moving"
5229
5230   if (is_moving_after)
5231   {
5232     if (Tile[newx][newy] == EL_EMPTY)
5233       Tile[newx][newy] = EL_BLOCKED;
5234
5235     MovDir[newx][newy] = MovDir[x][y];
5236
5237     CustomValue[newx][newy] = CustomValue[x][y];
5238
5239     GfxFrame[newx][newy] = GfxFrame[x][y];
5240     GfxRandom[newx][newy] = GfxRandom[x][y];
5241     GfxAction[newx][newy] = GfxAction[x][y];
5242     GfxDir[newx][newy] = GfxDir[x][y];
5243   }
5244 }
5245
5246 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5247 {
5248   int direction = MovDir[x][y];
5249   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5250   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5251
5252   *goes_to_x = newx;
5253   *goes_to_y = newy;
5254 }
5255
5256 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5257 {
5258   int oldx = x, oldy = y;
5259   int direction = MovDir[x][y];
5260
5261   if (direction == MV_LEFT)
5262     oldx++;
5263   else if (direction == MV_RIGHT)
5264     oldx--;
5265   else if (direction == MV_UP)
5266     oldy++;
5267   else if (direction == MV_DOWN)
5268     oldy--;
5269
5270   *comes_from_x = oldx;
5271   *comes_from_y = oldy;
5272 }
5273
5274 static int MovingOrBlocked2Element(int x, int y)
5275 {
5276   int element = Tile[x][y];
5277
5278   if (element == EL_BLOCKED)
5279   {
5280     int oldx, oldy;
5281
5282     Blocked2Moving(x, y, &oldx, &oldy);
5283     return Tile[oldx][oldy];
5284   }
5285   else
5286     return element;
5287 }
5288
5289 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5290 {
5291   // like MovingOrBlocked2Element(), but if element is moving
5292   // and (x,y) is the field the moving element is just leaving,
5293   // return EL_BLOCKED instead of the element value
5294   int element = Tile[x][y];
5295
5296   if (IS_MOVING(x, y))
5297   {
5298     if (element == EL_BLOCKED)
5299     {
5300       int oldx, oldy;
5301
5302       Blocked2Moving(x, y, &oldx, &oldy);
5303       return Tile[oldx][oldy];
5304     }
5305     else
5306       return EL_BLOCKED;
5307   }
5308   else
5309     return element;
5310 }
5311
5312 static void RemoveField(int x, int y)
5313 {
5314   Tile[x][y] = EL_EMPTY;
5315
5316   MovPos[x][y] = 0;
5317   MovDir[x][y] = 0;
5318   MovDelay[x][y] = 0;
5319
5320   CustomValue[x][y] = 0;
5321
5322   AmoebaNr[x][y] = 0;
5323   ChangeDelay[x][y] = 0;
5324   ChangePage[x][y] = -1;
5325   Pushed[x][y] = FALSE;
5326
5327   GfxElement[x][y] = EL_UNDEFINED;
5328   GfxAction[x][y] = ACTION_DEFAULT;
5329   GfxDir[x][y] = MV_NONE;
5330 }
5331
5332 static void RemoveMovingField(int x, int y)
5333 {
5334   int oldx = x, oldy = y, newx = x, newy = y;
5335   int element = Tile[x][y];
5336   int next_element = EL_UNDEFINED;
5337
5338   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5339     return;
5340
5341   if (IS_MOVING(x, y))
5342   {
5343     Moving2Blocked(x, y, &newx, &newy);
5344
5345     if (Tile[newx][newy] != EL_BLOCKED)
5346     {
5347       // element is moving, but target field is not free (blocked), but
5348       // already occupied by something different (example: acid pool);
5349       // in this case, only remove the moving field, but not the target
5350
5351       RemoveField(oldx, oldy);
5352
5353       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5354
5355       TEST_DrawLevelField(oldx, oldy);
5356
5357       return;
5358     }
5359   }
5360   else if (element == EL_BLOCKED)
5361   {
5362     Blocked2Moving(x, y, &oldx, &oldy);
5363     if (!IS_MOVING(oldx, oldy))
5364       return;
5365   }
5366
5367   if (element == EL_BLOCKED &&
5368       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5369        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5370        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5371        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5372        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5373        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5374     next_element = get_next_element(Tile[oldx][oldy]);
5375
5376   RemoveField(oldx, oldy);
5377   RemoveField(newx, newy);
5378
5379   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5380
5381   if (next_element != EL_UNDEFINED)
5382     Tile[oldx][oldy] = next_element;
5383
5384   TEST_DrawLevelField(oldx, oldy);
5385   TEST_DrawLevelField(newx, newy);
5386 }
5387
5388 void DrawDynamite(int x, int y)
5389 {
5390   int sx = SCREENX(x), sy = SCREENY(y);
5391   int graphic = el2img(Tile[x][y]);
5392   int frame;
5393
5394   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5395     return;
5396
5397   if (IS_WALKABLE_INSIDE(Back[x][y]))
5398     return;
5399
5400   if (Back[x][y])
5401     DrawLevelElement(x, y, Back[x][y]);
5402   else if (Store[x][y])
5403     DrawLevelElement(x, y, Store[x][y]);
5404   else if (game.use_masked_elements)
5405     DrawLevelElement(x, y, EL_EMPTY);
5406
5407   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5408
5409   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5410     DrawGraphicThruMask(sx, sy, graphic, frame);
5411   else
5412     DrawGraphic(sx, sy, graphic, frame);
5413 }
5414
5415 static void CheckDynamite(int x, int y)
5416 {
5417   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5418   {
5419     MovDelay[x][y]--;
5420
5421     if (MovDelay[x][y] != 0)
5422     {
5423       DrawDynamite(x, y);
5424       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5425
5426       return;
5427     }
5428   }
5429
5430   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5431
5432   Bang(x, y);
5433 }
5434
5435 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5436 {
5437   boolean num_checked_players = 0;
5438   int i;
5439
5440   for (i = 0; i < MAX_PLAYERS; i++)
5441   {
5442     if (stored_player[i].active)
5443     {
5444       int sx = stored_player[i].jx;
5445       int sy = stored_player[i].jy;
5446
5447       if (num_checked_players == 0)
5448       {
5449         *sx1 = *sx2 = sx;
5450         *sy1 = *sy2 = sy;
5451       }
5452       else
5453       {
5454         *sx1 = MIN(*sx1, sx);
5455         *sy1 = MIN(*sy1, sy);
5456         *sx2 = MAX(*sx2, sx);
5457         *sy2 = MAX(*sy2, sy);
5458       }
5459
5460       num_checked_players++;
5461     }
5462   }
5463 }
5464
5465 static boolean checkIfAllPlayersFitToScreen_RND(void)
5466 {
5467   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5468
5469   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5470
5471   return (sx2 - sx1 < SCR_FIELDX &&
5472           sy2 - sy1 < SCR_FIELDY);
5473 }
5474
5475 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5476 {
5477   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5478
5479   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5480
5481   *sx = (sx1 + sx2) / 2;
5482   *sy = (sy1 + sy2) / 2;
5483 }
5484
5485 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5486                                boolean center_screen, boolean quick_relocation)
5487 {
5488   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5489   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5490   boolean no_delay = (tape.warp_forward);
5491   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5492   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5493   int new_scroll_x, new_scroll_y;
5494
5495   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5496   {
5497     // case 1: quick relocation inside visible screen (without scrolling)
5498
5499     RedrawPlayfield();
5500
5501     return;
5502   }
5503
5504   if (!level.shifted_relocation || center_screen)
5505   {
5506     // relocation _with_ centering of screen
5507
5508     new_scroll_x = SCROLL_POSITION_X(x);
5509     new_scroll_y = SCROLL_POSITION_Y(y);
5510   }
5511   else
5512   {
5513     // relocation _without_ centering of screen
5514
5515     int center_scroll_x = SCROLL_POSITION_X(old_x);
5516     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5517     int offset_x = x + (scroll_x - center_scroll_x);
5518     int offset_y = y + (scroll_y - center_scroll_y);
5519
5520     // for new screen position, apply previous offset to center position
5521     new_scroll_x = SCROLL_POSITION_X(offset_x);
5522     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5523   }
5524
5525   if (quick_relocation)
5526   {
5527     // case 2: quick relocation (redraw without visible scrolling)
5528
5529     scroll_x = new_scroll_x;
5530     scroll_y = new_scroll_y;
5531
5532     RedrawPlayfield();
5533
5534     return;
5535   }
5536
5537   // case 3: visible relocation (with scrolling to new position)
5538
5539   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5540
5541   SetVideoFrameDelay(wait_delay_value);
5542
5543   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5544   {
5545     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5546     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5547
5548     if (dx == 0 && dy == 0)             // no scrolling needed at all
5549       break;
5550
5551     scroll_x -= dx;
5552     scroll_y -= dy;
5553
5554     // set values for horizontal/vertical screen scrolling (half tile size)
5555     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5556     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5557     int pos_x = dx * TILEX / 2;
5558     int pos_y = dy * TILEY / 2;
5559     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5560     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5561
5562     ScrollLevel(dx, dy);
5563     DrawAllPlayers();
5564
5565     // scroll in two steps of half tile size to make things smoother
5566     BlitScreenToBitmapExt_RND(window, fx, fy);
5567
5568     // scroll second step to align at full tile size
5569     BlitScreenToBitmap(window);
5570   }
5571
5572   DrawAllPlayers();
5573   BackToFront();
5574
5575   SetVideoFrameDelay(frame_delay_value_old);
5576 }
5577
5578 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5579 {
5580   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5581   int player_nr = GET_PLAYER_NR(el_player);
5582   struct PlayerInfo *player = &stored_player[player_nr];
5583   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5584   boolean no_delay = (tape.warp_forward);
5585   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5586   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5587   int old_jx = player->jx;
5588   int old_jy = player->jy;
5589   int old_element = Tile[old_jx][old_jy];
5590   int element = Tile[jx][jy];
5591   boolean player_relocated = (old_jx != jx || old_jy != jy);
5592
5593   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5594   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5595   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5596   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5597   int leave_side_horiz = move_dir_horiz;
5598   int leave_side_vert  = move_dir_vert;
5599   int enter_side = enter_side_horiz | enter_side_vert;
5600   int leave_side = leave_side_horiz | leave_side_vert;
5601
5602   if (player->buried)           // do not reanimate dead player
5603     return;
5604
5605   if (!player_relocated)        // no need to relocate the player
5606     return;
5607
5608   if (IS_PLAYER(jx, jy))        // player already placed at new position
5609   {
5610     RemoveField(jx, jy);        // temporarily remove newly placed player
5611     DrawLevelField(jx, jy);
5612   }
5613
5614   if (player->present)
5615   {
5616     while (player->MovPos)
5617     {
5618       ScrollPlayer(player, SCROLL_GO_ON);
5619       ScrollScreen(NULL, SCROLL_GO_ON);
5620
5621       AdvanceFrameAndPlayerCounters(player->index_nr);
5622
5623       DrawPlayer(player);
5624
5625       BackToFront_WithFrameDelay(wait_delay_value);
5626     }
5627
5628     DrawPlayer(player);         // needed here only to cleanup last field
5629     DrawLevelField(player->jx, player->jy);     // remove player graphic
5630
5631     player->is_moving = FALSE;
5632   }
5633
5634   if (IS_CUSTOM_ELEMENT(old_element))
5635     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5636                                CE_LEFT_BY_PLAYER,
5637                                player->index_bit, leave_side);
5638
5639   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5640                                       CE_PLAYER_LEAVES_X,
5641                                       player->index_bit, leave_side);
5642
5643   Tile[jx][jy] = el_player;
5644   InitPlayerField(jx, jy, el_player, TRUE);
5645
5646   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5647      possible that the relocation target field did not contain a player element,
5648      but a walkable element, to which the new player was relocated -- in this
5649      case, restore that (already initialized!) element on the player field */
5650   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5651   {
5652     Tile[jx][jy] = element;     // restore previously existing element
5653   }
5654
5655   // only visually relocate centered player
5656   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5657                      FALSE, level.instant_relocation);
5658
5659   TestIfPlayerTouchesBadThing(jx, jy);
5660   TestIfPlayerTouchesCustomElement(jx, jy);
5661
5662   if (IS_CUSTOM_ELEMENT(element))
5663     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5664                                player->index_bit, enter_side);
5665
5666   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5667                                       player->index_bit, enter_side);
5668
5669   if (player->is_switching)
5670   {
5671     /* ensure that relocation while still switching an element does not cause
5672        a new element to be treated as also switched directly after relocation
5673        (this is important for teleporter switches that teleport the player to
5674        a place where another teleporter switch is in the same direction, which
5675        would then incorrectly be treated as immediately switched before the
5676        direction key that caused the switch was released) */
5677
5678     player->switch_x += jx - old_jx;
5679     player->switch_y += jy - old_jy;
5680   }
5681 }
5682
5683 static void Explode(int ex, int ey, int phase, int mode)
5684 {
5685   int x, y;
5686   int last_phase;
5687   int border_element;
5688
5689   // !!! eliminate this variable !!!
5690   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5691
5692   if (game.explosions_delayed)
5693   {
5694     ExplodeField[ex][ey] = mode;
5695     return;
5696   }
5697
5698   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5699   {
5700     int center_element = Tile[ex][ey];
5701     int artwork_element, explosion_element;     // set these values later
5702
5703     // remove things displayed in background while burning dynamite
5704     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5705       Back[ex][ey] = 0;
5706
5707     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5708     {
5709       // put moving element to center field (and let it explode there)
5710       center_element = MovingOrBlocked2Element(ex, ey);
5711       RemoveMovingField(ex, ey);
5712       Tile[ex][ey] = center_element;
5713     }
5714
5715     // now "center_element" is finally determined -- set related values now
5716     artwork_element = center_element;           // for custom player artwork
5717     explosion_element = center_element;         // for custom player artwork
5718
5719     if (IS_PLAYER(ex, ey))
5720     {
5721       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5722
5723       artwork_element = stored_player[player_nr].artwork_element;
5724
5725       if (level.use_explosion_element[player_nr])
5726       {
5727         explosion_element = level.explosion_element[player_nr];
5728         artwork_element = explosion_element;
5729       }
5730     }
5731
5732     if (mode == EX_TYPE_NORMAL ||
5733         mode == EX_TYPE_CENTER ||
5734         mode == EX_TYPE_CROSS)
5735       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5736
5737     last_phase = element_info[explosion_element].explosion_delay + 1;
5738
5739     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5740     {
5741       int xx = x - ex + 1;
5742       int yy = y - ey + 1;
5743       int element;
5744
5745       if (!IN_LEV_FIELD(x, y) ||
5746           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5747           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5748         continue;
5749
5750       element = Tile[x][y];
5751
5752       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5753       {
5754         element = MovingOrBlocked2Element(x, y);
5755
5756         if (!IS_EXPLOSION_PROOF(element))
5757           RemoveMovingField(x, y);
5758       }
5759
5760       // indestructible elements can only explode in center (but not flames)
5761       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5762                                            mode == EX_TYPE_BORDER)) ||
5763           element == EL_FLAMES)
5764         continue;
5765
5766       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5767          behaviour, for example when touching a yamyam that explodes to rocks
5768          with active deadly shield, a rock is created under the player !!! */
5769       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5770 #if 0
5771       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5772           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5773            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5774 #else
5775       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5776 #endif
5777       {
5778         if (IS_ACTIVE_BOMB(element))
5779         {
5780           // re-activate things under the bomb like gate or penguin
5781           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5782           Back[x][y] = 0;
5783         }
5784
5785         continue;
5786       }
5787
5788       // save walkable background elements while explosion on same tile
5789       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5790           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5791         Back[x][y] = element;
5792
5793       // ignite explodable elements reached by other explosion
5794       if (element == EL_EXPLOSION)
5795         element = Store2[x][y];
5796
5797       if (AmoebaNr[x][y] &&
5798           (element == EL_AMOEBA_FULL ||
5799            element == EL_BD_AMOEBA ||
5800            element == EL_AMOEBA_GROWING))
5801       {
5802         AmoebaCnt[AmoebaNr[x][y]]--;
5803         AmoebaCnt2[AmoebaNr[x][y]]--;
5804       }
5805
5806       RemoveField(x, y);
5807
5808       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5809       {
5810         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5811
5812         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5813
5814         if (PLAYERINFO(ex, ey)->use_murphy)
5815           Store[x][y] = EL_EMPTY;
5816       }
5817
5818       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5819       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5820       else if (ELEM_IS_PLAYER(center_element))
5821         Store[x][y] = EL_EMPTY;
5822       else if (center_element == EL_YAMYAM)
5823         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5824       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5825         Store[x][y] = element_info[center_element].content.e[xx][yy];
5826 #if 1
5827       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5828       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5829       // otherwise) -- FIX THIS !!!
5830       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5831         Store[x][y] = element_info[element].content.e[1][1];
5832 #else
5833       else if (!CAN_EXPLODE(element))
5834         Store[x][y] = element_info[element].content.e[1][1];
5835 #endif
5836       else
5837         Store[x][y] = EL_EMPTY;
5838
5839       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5840           center_element == EL_AMOEBA_TO_DIAMOND)
5841         Store2[x][y] = element;
5842
5843       Tile[x][y] = EL_EXPLOSION;
5844       GfxElement[x][y] = artwork_element;
5845
5846       ExplodePhase[x][y] = 1;
5847       ExplodeDelay[x][y] = last_phase;
5848
5849       Stop[x][y] = TRUE;
5850     }
5851
5852     if (center_element == EL_YAMYAM)
5853       game.yamyam_content_nr =
5854         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5855
5856     return;
5857   }
5858
5859   if (Stop[ex][ey])
5860     return;
5861
5862   x = ex;
5863   y = ey;
5864
5865   if (phase == 1)
5866     GfxFrame[x][y] = 0;         // restart explosion animation
5867
5868   last_phase = ExplodeDelay[x][y];
5869
5870   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5871
5872   // this can happen if the player leaves an explosion just in time
5873   if (GfxElement[x][y] == EL_UNDEFINED)
5874     GfxElement[x][y] = EL_EMPTY;
5875
5876   border_element = Store2[x][y];
5877   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5878     border_element = StorePlayer[x][y];
5879
5880   if (phase == element_info[border_element].ignition_delay ||
5881       phase == last_phase)
5882   {
5883     boolean border_explosion = FALSE;
5884
5885     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5886         !PLAYER_EXPLOSION_PROTECTED(x, y))
5887     {
5888       KillPlayerUnlessExplosionProtected(x, y);
5889       border_explosion = TRUE;
5890     }
5891     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5892     {
5893       Tile[x][y] = Store2[x][y];
5894       Store2[x][y] = 0;
5895       Bang(x, y);
5896       border_explosion = TRUE;
5897     }
5898     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5899     {
5900       AmoebaToDiamond(x, y);
5901       Store2[x][y] = 0;
5902       border_explosion = TRUE;
5903     }
5904
5905     // if an element just explodes due to another explosion (chain-reaction),
5906     // do not immediately end the new explosion when it was the last frame of
5907     // the explosion (as it would be done in the following "if"-statement!)
5908     if (border_explosion && phase == last_phase)
5909       return;
5910   }
5911
5912   if (phase == last_phase)
5913   {
5914     int element;
5915
5916     element = Tile[x][y] = Store[x][y];
5917     Store[x][y] = Store2[x][y] = 0;
5918     GfxElement[x][y] = EL_UNDEFINED;
5919
5920     // player can escape from explosions and might therefore be still alive
5921     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5922         element <= EL_PLAYER_IS_EXPLODING_4)
5923     {
5924       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5925       int explosion_element = EL_PLAYER_1 + player_nr;
5926       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5927       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5928
5929       if (level.use_explosion_element[player_nr])
5930         explosion_element = level.explosion_element[player_nr];
5931
5932       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5933                     element_info[explosion_element].content.e[xx][yy]);
5934     }
5935
5936     // restore probably existing indestructible background element
5937     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5938       element = Tile[x][y] = Back[x][y];
5939     Back[x][y] = 0;
5940
5941     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5942     GfxDir[x][y] = MV_NONE;
5943     ChangeDelay[x][y] = 0;
5944     ChangePage[x][y] = -1;
5945
5946     CustomValue[x][y] = 0;
5947
5948     InitField_WithBug2(x, y, FALSE);
5949
5950     TEST_DrawLevelField(x, y);
5951
5952     TestIfElementTouchesCustomElement(x, y);
5953
5954     if (GFX_CRUMBLED(element))
5955       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5956
5957     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5958       StorePlayer[x][y] = 0;
5959
5960     if (ELEM_IS_PLAYER(element))
5961       RelocatePlayer(x, y, element);
5962   }
5963   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5964   {
5965     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5966     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5967
5968     if (phase == delay)
5969       TEST_DrawLevelFieldCrumbled(x, y);
5970
5971     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5972     {
5973       DrawLevelElement(x, y, Back[x][y]);
5974       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5975     }
5976     else if (IS_WALKABLE_UNDER(Back[x][y]))
5977     {
5978       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5979       DrawLevelElementThruMask(x, y, Back[x][y]);
5980     }
5981     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5982       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5983   }
5984 }
5985
5986 static void DynaExplode(int ex, int ey)
5987 {
5988   int i, j;
5989   int dynabomb_element = Tile[ex][ey];
5990   int dynabomb_size = 1;
5991   boolean dynabomb_xl = FALSE;
5992   struct PlayerInfo *player;
5993   static int xy[4][2] =
5994   {
5995     { 0, -1 },
5996     { -1, 0 },
5997     { +1, 0 },
5998     { 0, +1 }
5999   };
6000
6001   if (IS_ACTIVE_BOMB(dynabomb_element))
6002   {
6003     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6004     dynabomb_size = player->dynabomb_size;
6005     dynabomb_xl = player->dynabomb_xl;
6006     player->dynabombs_left++;
6007   }
6008
6009   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6010
6011   for (i = 0; i < NUM_DIRECTIONS; i++)
6012   {
6013     for (j = 1; j <= dynabomb_size; j++)
6014     {
6015       int x = ex + j * xy[i][0];
6016       int y = ey + j * xy[i][1];
6017       int element;
6018
6019       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6020         break;
6021
6022       element = Tile[x][y];
6023
6024       // do not restart explosions of fields with active bombs
6025       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6026         continue;
6027
6028       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6029
6030       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6031           !IS_DIGGABLE(element) && !dynabomb_xl)
6032         break;
6033     }
6034   }
6035 }
6036
6037 void Bang(int x, int y)
6038 {
6039   int element = MovingOrBlocked2Element(x, y);
6040   int explosion_type = EX_TYPE_NORMAL;
6041
6042   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6043   {
6044     struct PlayerInfo *player = PLAYERINFO(x, y);
6045
6046     element = Tile[x][y] = player->initial_element;
6047
6048     if (level.use_explosion_element[player->index_nr])
6049     {
6050       int explosion_element = level.explosion_element[player->index_nr];
6051
6052       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6053         explosion_type = EX_TYPE_CROSS;
6054       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6055         explosion_type = EX_TYPE_CENTER;
6056     }
6057   }
6058
6059   switch (element)
6060   {
6061     case EL_BUG:
6062     case EL_SPACESHIP:
6063     case EL_BD_BUTTERFLY:
6064     case EL_BD_FIREFLY:
6065     case EL_YAMYAM:
6066     case EL_DARK_YAMYAM:
6067     case EL_ROBOT:
6068     case EL_PACMAN:
6069     case EL_MOLE:
6070       RaiseScoreElement(element);
6071       break;
6072
6073     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6074     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6075     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6076     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6077     case EL_DYNABOMB_INCREASE_NUMBER:
6078     case EL_DYNABOMB_INCREASE_SIZE:
6079     case EL_DYNABOMB_INCREASE_POWER:
6080       explosion_type = EX_TYPE_DYNA;
6081       break;
6082
6083     case EL_DC_LANDMINE:
6084       explosion_type = EX_TYPE_CENTER;
6085       break;
6086
6087     case EL_PENGUIN:
6088     case EL_LAMP:
6089     case EL_LAMP_ACTIVE:
6090     case EL_AMOEBA_TO_DIAMOND:
6091       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6092         explosion_type = EX_TYPE_CENTER;
6093       break;
6094
6095     default:
6096       if (element_info[element].explosion_type == EXPLODES_CROSS)
6097         explosion_type = EX_TYPE_CROSS;
6098       else if (element_info[element].explosion_type == EXPLODES_1X1)
6099         explosion_type = EX_TYPE_CENTER;
6100       break;
6101   }
6102
6103   if (explosion_type == EX_TYPE_DYNA)
6104     DynaExplode(x, y);
6105   else
6106     Explode(x, y, EX_PHASE_START, explosion_type);
6107
6108   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6109 }
6110
6111 static void SplashAcid(int x, int y)
6112 {
6113   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6114       (!IN_LEV_FIELD(x - 1, y - 2) ||
6115        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6116     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6117
6118   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6119       (!IN_LEV_FIELD(x + 1, y - 2) ||
6120        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6121     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6122
6123   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6124 }
6125
6126 static void InitBeltMovement(void)
6127 {
6128   static int belt_base_element[4] =
6129   {
6130     EL_CONVEYOR_BELT_1_LEFT,
6131     EL_CONVEYOR_BELT_2_LEFT,
6132     EL_CONVEYOR_BELT_3_LEFT,
6133     EL_CONVEYOR_BELT_4_LEFT
6134   };
6135   static int belt_base_active_element[4] =
6136   {
6137     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6138     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6139     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6140     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6141   };
6142
6143   int x, y, i, j;
6144
6145   // set frame order for belt animation graphic according to belt direction
6146   for (i = 0; i < NUM_BELTS; i++)
6147   {
6148     int belt_nr = i;
6149
6150     for (j = 0; j < NUM_BELT_PARTS; j++)
6151     {
6152       int element = belt_base_active_element[belt_nr] + j;
6153       int graphic_1 = el2img(element);
6154       int graphic_2 = el2panelimg(element);
6155
6156       if (game.belt_dir[i] == MV_LEFT)
6157       {
6158         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6159         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6160       }
6161       else
6162       {
6163         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6164         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6165       }
6166     }
6167   }
6168
6169   SCAN_PLAYFIELD(x, y)
6170   {
6171     int element = Tile[x][y];
6172
6173     for (i = 0; i < NUM_BELTS; i++)
6174     {
6175       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6176       {
6177         int e_belt_nr = getBeltNrFromBeltElement(element);
6178         int belt_nr = i;
6179
6180         if (e_belt_nr == belt_nr)
6181         {
6182           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6183
6184           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6185         }
6186       }
6187     }
6188   }
6189 }
6190
6191 static void ToggleBeltSwitch(int x, int y)
6192 {
6193   static int belt_base_element[4] =
6194   {
6195     EL_CONVEYOR_BELT_1_LEFT,
6196     EL_CONVEYOR_BELT_2_LEFT,
6197     EL_CONVEYOR_BELT_3_LEFT,
6198     EL_CONVEYOR_BELT_4_LEFT
6199   };
6200   static int belt_base_active_element[4] =
6201   {
6202     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6203     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6204     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6205     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6206   };
6207   static int belt_base_switch_element[4] =
6208   {
6209     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6210     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6211     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6212     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6213   };
6214   static int belt_move_dir[4] =
6215   {
6216     MV_LEFT,
6217     MV_NONE,
6218     MV_RIGHT,
6219     MV_NONE,
6220   };
6221
6222   int element = Tile[x][y];
6223   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6224   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6225   int belt_dir = belt_move_dir[belt_dir_nr];
6226   int xx, yy, i;
6227
6228   if (!IS_BELT_SWITCH(element))
6229     return;
6230
6231   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6232   game.belt_dir[belt_nr] = belt_dir;
6233
6234   if (belt_dir_nr == 3)
6235     belt_dir_nr = 1;
6236
6237   // set frame order for belt animation graphic according to belt direction
6238   for (i = 0; i < NUM_BELT_PARTS; i++)
6239   {
6240     int element = belt_base_active_element[belt_nr] + i;
6241     int graphic_1 = el2img(element);
6242     int graphic_2 = el2panelimg(element);
6243
6244     if (belt_dir == MV_LEFT)
6245     {
6246       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6247       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6248     }
6249     else
6250     {
6251       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6252       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6253     }
6254   }
6255
6256   SCAN_PLAYFIELD(xx, yy)
6257   {
6258     int element = Tile[xx][yy];
6259
6260     if (IS_BELT_SWITCH(element))
6261     {
6262       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6263
6264       if (e_belt_nr == belt_nr)
6265       {
6266         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6267         TEST_DrawLevelField(xx, yy);
6268       }
6269     }
6270     else if (IS_BELT(element) && belt_dir != MV_NONE)
6271     {
6272       int e_belt_nr = getBeltNrFromBeltElement(element);
6273
6274       if (e_belt_nr == belt_nr)
6275       {
6276         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6277
6278         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6279         TEST_DrawLevelField(xx, yy);
6280       }
6281     }
6282     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6283     {
6284       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6285
6286       if (e_belt_nr == belt_nr)
6287       {
6288         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6289
6290         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6291         TEST_DrawLevelField(xx, yy);
6292       }
6293     }
6294   }
6295 }
6296
6297 static void ToggleSwitchgateSwitch(int x, int y)
6298 {
6299   int xx, yy;
6300
6301   game.switchgate_pos = !game.switchgate_pos;
6302
6303   SCAN_PLAYFIELD(xx, yy)
6304   {
6305     int element = Tile[xx][yy];
6306
6307     if (element == EL_SWITCHGATE_SWITCH_UP)
6308     {
6309       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6310       TEST_DrawLevelField(xx, yy);
6311     }
6312     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6313     {
6314       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6315       TEST_DrawLevelField(xx, yy);
6316     }
6317     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6318     {
6319       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6320       TEST_DrawLevelField(xx, yy);
6321     }
6322     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6323     {
6324       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6325       TEST_DrawLevelField(xx, yy);
6326     }
6327     else if (element == EL_SWITCHGATE_OPEN ||
6328              element == EL_SWITCHGATE_OPENING)
6329     {
6330       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6331
6332       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6333     }
6334     else if (element == EL_SWITCHGATE_CLOSED ||
6335              element == EL_SWITCHGATE_CLOSING)
6336     {
6337       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6338
6339       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6340     }
6341   }
6342 }
6343
6344 static int getInvisibleActiveFromInvisibleElement(int element)
6345 {
6346   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6347           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6348           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6349           element);
6350 }
6351
6352 static int getInvisibleFromInvisibleActiveElement(int element)
6353 {
6354   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6355           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6356           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6357           element);
6358 }
6359
6360 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6361 {
6362   int x, y;
6363
6364   SCAN_PLAYFIELD(x, y)
6365   {
6366     int element = Tile[x][y];
6367
6368     if (element == EL_LIGHT_SWITCH &&
6369         game.light_time_left > 0)
6370     {
6371       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6372       TEST_DrawLevelField(x, y);
6373     }
6374     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6375              game.light_time_left == 0)
6376     {
6377       Tile[x][y] = EL_LIGHT_SWITCH;
6378       TEST_DrawLevelField(x, y);
6379     }
6380     else if (element == EL_EMC_DRIPPER &&
6381              game.light_time_left > 0)
6382     {
6383       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6384       TEST_DrawLevelField(x, y);
6385     }
6386     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6387              game.light_time_left == 0)
6388     {
6389       Tile[x][y] = EL_EMC_DRIPPER;
6390       TEST_DrawLevelField(x, y);
6391     }
6392     else if (element == EL_INVISIBLE_STEELWALL ||
6393              element == EL_INVISIBLE_WALL ||
6394              element == EL_INVISIBLE_SAND)
6395     {
6396       if (game.light_time_left > 0)
6397         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6398
6399       TEST_DrawLevelField(x, y);
6400
6401       // uncrumble neighbour fields, if needed
6402       if (element == EL_INVISIBLE_SAND)
6403         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6404     }
6405     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6406              element == EL_INVISIBLE_WALL_ACTIVE ||
6407              element == EL_INVISIBLE_SAND_ACTIVE)
6408     {
6409       if (game.light_time_left == 0)
6410         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6411
6412       TEST_DrawLevelField(x, y);
6413
6414       // re-crumble neighbour fields, if needed
6415       if (element == EL_INVISIBLE_SAND)
6416         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6417     }
6418   }
6419 }
6420
6421 static void RedrawAllInvisibleElementsForLenses(void)
6422 {
6423   int x, y;
6424
6425   SCAN_PLAYFIELD(x, y)
6426   {
6427     int element = Tile[x][y];
6428
6429     if (element == EL_EMC_DRIPPER &&
6430         game.lenses_time_left > 0)
6431     {
6432       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6433       TEST_DrawLevelField(x, y);
6434     }
6435     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6436              game.lenses_time_left == 0)
6437     {
6438       Tile[x][y] = EL_EMC_DRIPPER;
6439       TEST_DrawLevelField(x, y);
6440     }
6441     else if (element == EL_INVISIBLE_STEELWALL ||
6442              element == EL_INVISIBLE_WALL ||
6443              element == EL_INVISIBLE_SAND)
6444     {
6445       if (game.lenses_time_left > 0)
6446         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6447
6448       TEST_DrawLevelField(x, y);
6449
6450       // uncrumble neighbour fields, if needed
6451       if (element == EL_INVISIBLE_SAND)
6452         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6453     }
6454     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6455              element == EL_INVISIBLE_WALL_ACTIVE ||
6456              element == EL_INVISIBLE_SAND_ACTIVE)
6457     {
6458       if (game.lenses_time_left == 0)
6459         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6460
6461       TEST_DrawLevelField(x, y);
6462
6463       // re-crumble neighbour fields, if needed
6464       if (element == EL_INVISIBLE_SAND)
6465         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6466     }
6467   }
6468 }
6469
6470 static void RedrawAllInvisibleElementsForMagnifier(void)
6471 {
6472   int x, y;
6473
6474   SCAN_PLAYFIELD(x, y)
6475   {
6476     int element = Tile[x][y];
6477
6478     if (element == EL_EMC_FAKE_GRASS &&
6479         game.magnify_time_left > 0)
6480     {
6481       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6482       TEST_DrawLevelField(x, y);
6483     }
6484     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6485              game.magnify_time_left == 0)
6486     {
6487       Tile[x][y] = EL_EMC_FAKE_GRASS;
6488       TEST_DrawLevelField(x, y);
6489     }
6490     else if (IS_GATE_GRAY(element) &&
6491              game.magnify_time_left > 0)
6492     {
6493       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6494                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6495                     IS_EM_GATE_GRAY(element) ?
6496                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6497                     IS_EMC_GATE_GRAY(element) ?
6498                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6499                     IS_DC_GATE_GRAY(element) ?
6500                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6501                     element);
6502       TEST_DrawLevelField(x, y);
6503     }
6504     else if (IS_GATE_GRAY_ACTIVE(element) &&
6505              game.magnify_time_left == 0)
6506     {
6507       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6508                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6509                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6510                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6511                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6512                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6513                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6514                     EL_DC_GATE_WHITE_GRAY :
6515                     element);
6516       TEST_DrawLevelField(x, y);
6517     }
6518   }
6519 }
6520
6521 static void ToggleLightSwitch(int x, int y)
6522 {
6523   int element = Tile[x][y];
6524
6525   game.light_time_left =
6526     (element == EL_LIGHT_SWITCH ?
6527      level.time_light * FRAMES_PER_SECOND : 0);
6528
6529   RedrawAllLightSwitchesAndInvisibleElements();
6530 }
6531
6532 static void ActivateTimegateSwitch(int x, int y)
6533 {
6534   int xx, yy;
6535
6536   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6537
6538   SCAN_PLAYFIELD(xx, yy)
6539   {
6540     int element = Tile[xx][yy];
6541
6542     if (element == EL_TIMEGATE_CLOSED ||
6543         element == EL_TIMEGATE_CLOSING)
6544     {
6545       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6546       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6547     }
6548
6549     /*
6550     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6551     {
6552       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6553       TEST_DrawLevelField(xx, yy);
6554     }
6555     */
6556
6557   }
6558
6559   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6560                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6561 }
6562
6563 static void Impact(int x, int y)
6564 {
6565   boolean last_line = (y == lev_fieldy - 1);
6566   boolean object_hit = FALSE;
6567   boolean impact = (last_line || object_hit);
6568   int element = Tile[x][y];
6569   int smashed = EL_STEELWALL;
6570
6571   if (!last_line)       // check if element below was hit
6572   {
6573     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6574       return;
6575
6576     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6577                                          MovDir[x][y + 1] != MV_DOWN ||
6578                                          MovPos[x][y + 1] <= TILEY / 2));
6579
6580     // do not smash moving elements that left the smashed field in time
6581     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6582         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6583       object_hit = FALSE;
6584
6585 #if USE_QUICKSAND_IMPACT_BUGFIX
6586     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6587     {
6588       RemoveMovingField(x, y + 1);
6589       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6590       Tile[x][y + 2] = EL_ROCK;
6591       TEST_DrawLevelField(x, y + 2);
6592
6593       object_hit = TRUE;
6594     }
6595
6596     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6597     {
6598       RemoveMovingField(x, y + 1);
6599       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6600       Tile[x][y + 2] = EL_ROCK;
6601       TEST_DrawLevelField(x, y + 2);
6602
6603       object_hit = TRUE;
6604     }
6605 #endif
6606
6607     if (object_hit)
6608       smashed = MovingOrBlocked2Element(x, y + 1);
6609
6610     impact = (last_line || object_hit);
6611   }
6612
6613   if (!last_line && smashed == EL_ACID) // element falls into acid
6614   {
6615     SplashAcid(x, y + 1);
6616     return;
6617   }
6618
6619   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6620   // only reset graphic animation if graphic really changes after impact
6621   if (impact &&
6622       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6623   {
6624     ResetGfxAnimation(x, y);
6625     TEST_DrawLevelField(x, y);
6626   }
6627
6628   if (impact && CAN_EXPLODE_IMPACT(element))
6629   {
6630     Bang(x, y);
6631     return;
6632   }
6633   else if (impact && element == EL_PEARL &&
6634            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6635   {
6636     ResetGfxAnimation(x, y);
6637
6638     Tile[x][y] = EL_PEARL_BREAKING;
6639     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6640     return;
6641   }
6642   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6643   {
6644     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6645
6646     return;
6647   }
6648
6649   if (impact && element == EL_AMOEBA_DROP)
6650   {
6651     if (object_hit && IS_PLAYER(x, y + 1))
6652       KillPlayerUnlessEnemyProtected(x, y + 1);
6653     else if (object_hit && smashed == EL_PENGUIN)
6654       Bang(x, y + 1);
6655     else
6656     {
6657       Tile[x][y] = EL_AMOEBA_GROWING;
6658       Store[x][y] = EL_AMOEBA_WET;
6659
6660       ResetRandomAnimationValue(x, y);
6661     }
6662     return;
6663   }
6664
6665   if (object_hit)               // check which object was hit
6666   {
6667     if ((CAN_PASS_MAGIC_WALL(element) && 
6668          (smashed == EL_MAGIC_WALL ||
6669           smashed == EL_BD_MAGIC_WALL)) ||
6670         (CAN_PASS_DC_MAGIC_WALL(element) &&
6671          smashed == EL_DC_MAGIC_WALL))
6672     {
6673       int xx, yy;
6674       int activated_magic_wall =
6675         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6676          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6677          EL_DC_MAGIC_WALL_ACTIVE);
6678
6679       // activate magic wall / mill
6680       SCAN_PLAYFIELD(xx, yy)
6681       {
6682         if (Tile[xx][yy] == smashed)
6683           Tile[xx][yy] = activated_magic_wall;
6684       }
6685
6686       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6687       game.magic_wall_active = TRUE;
6688
6689       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6690                             SND_MAGIC_WALL_ACTIVATING :
6691                             smashed == EL_BD_MAGIC_WALL ?
6692                             SND_BD_MAGIC_WALL_ACTIVATING :
6693                             SND_DC_MAGIC_WALL_ACTIVATING));
6694     }
6695
6696     if (IS_PLAYER(x, y + 1))
6697     {
6698       if (CAN_SMASH_PLAYER(element))
6699       {
6700         KillPlayerUnlessEnemyProtected(x, y + 1);
6701         return;
6702       }
6703     }
6704     else if (smashed == EL_PENGUIN)
6705     {
6706       if (CAN_SMASH_PLAYER(element))
6707       {
6708         Bang(x, y + 1);
6709         return;
6710       }
6711     }
6712     else if (element == EL_BD_DIAMOND)
6713     {
6714       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6715       {
6716         Bang(x, y + 1);
6717         return;
6718       }
6719     }
6720     else if (((element == EL_SP_INFOTRON ||
6721                element == EL_SP_ZONK) &&
6722               (smashed == EL_SP_SNIKSNAK ||
6723                smashed == EL_SP_ELECTRON ||
6724                smashed == EL_SP_DISK_ORANGE)) ||
6725              (element == EL_SP_INFOTRON &&
6726               smashed == EL_SP_DISK_YELLOW))
6727     {
6728       Bang(x, y + 1);
6729       return;
6730     }
6731     else if (CAN_SMASH_EVERYTHING(element))
6732     {
6733       if (IS_CLASSIC_ENEMY(smashed) ||
6734           CAN_EXPLODE_SMASHED(smashed))
6735       {
6736         Bang(x, y + 1);
6737         return;
6738       }
6739       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6740       {
6741         if (smashed == EL_LAMP ||
6742             smashed == EL_LAMP_ACTIVE)
6743         {
6744           Bang(x, y + 1);
6745           return;
6746         }
6747         else if (smashed == EL_NUT)
6748         {
6749           Tile[x][y + 1] = EL_NUT_BREAKING;
6750           PlayLevelSound(x, y, SND_NUT_BREAKING);
6751           RaiseScoreElement(EL_NUT);
6752           return;
6753         }
6754         else if (smashed == EL_PEARL)
6755         {
6756           ResetGfxAnimation(x, y);
6757
6758           Tile[x][y + 1] = EL_PEARL_BREAKING;
6759           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6760           return;
6761         }
6762         else if (smashed == EL_DIAMOND)
6763         {
6764           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6765           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6766           return;
6767         }
6768         else if (IS_BELT_SWITCH(smashed))
6769         {
6770           ToggleBeltSwitch(x, y + 1);
6771         }
6772         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6773                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6774                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6775                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6776         {
6777           ToggleSwitchgateSwitch(x, y + 1);
6778         }
6779         else if (smashed == EL_LIGHT_SWITCH ||
6780                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6781         {
6782           ToggleLightSwitch(x, y + 1);
6783         }
6784         else
6785         {
6786           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6787
6788           CheckElementChangeBySide(x, y + 1, smashed, element,
6789                                    CE_SWITCHED, CH_SIDE_TOP);
6790           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6791                                             CH_SIDE_TOP);
6792         }
6793       }
6794       else
6795       {
6796         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6797       }
6798     }
6799   }
6800
6801   // play sound of magic wall / mill
6802   if (!last_line &&
6803       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6804        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6805        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6806   {
6807     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6808       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6809     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6810       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6811     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6812       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6813
6814     return;
6815   }
6816
6817   // play sound of object that hits the ground
6818   if (last_line || object_hit)
6819     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6820 }
6821
6822 static void TurnRoundExt(int x, int y)
6823 {
6824   static struct
6825   {
6826     int dx, dy;
6827   } move_xy[] =
6828   {
6829     {  0,  0 },
6830     { -1,  0 },
6831     { +1,  0 },
6832     {  0,  0 },
6833     {  0, -1 },
6834     {  0,  0 }, { 0, 0 }, { 0, 0 },
6835     {  0, +1 }
6836   };
6837   static struct
6838   {
6839     int left, right, back;
6840   } turn[] =
6841   {
6842     { 0,        0,              0        },
6843     { MV_DOWN,  MV_UP,          MV_RIGHT },
6844     { MV_UP,    MV_DOWN,        MV_LEFT  },
6845     { 0,        0,              0        },
6846     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6847     { 0,        0,              0        },
6848     { 0,        0,              0        },
6849     { 0,        0,              0        },
6850     { MV_RIGHT, MV_LEFT,        MV_UP    }
6851   };
6852
6853   int element = Tile[x][y];
6854   int move_pattern = element_info[element].move_pattern;
6855
6856   int old_move_dir = MovDir[x][y];
6857   int left_dir  = turn[old_move_dir].left;
6858   int right_dir = turn[old_move_dir].right;
6859   int back_dir  = turn[old_move_dir].back;
6860
6861   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6862   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6863   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6864   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6865
6866   int left_x  = x + left_dx,  left_y  = y + left_dy;
6867   int right_x = x + right_dx, right_y = y + right_dy;
6868   int move_x  = x + move_dx,  move_y  = y + move_dy;
6869
6870   int xx, yy;
6871
6872   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6873   {
6874     TestIfBadThingTouchesOtherBadThing(x, y);
6875
6876     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6877       MovDir[x][y] = right_dir;
6878     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6879       MovDir[x][y] = left_dir;
6880
6881     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6882       MovDelay[x][y] = 9;
6883     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6884       MovDelay[x][y] = 1;
6885   }
6886   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6887   {
6888     TestIfBadThingTouchesOtherBadThing(x, y);
6889
6890     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6891       MovDir[x][y] = left_dir;
6892     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6893       MovDir[x][y] = right_dir;
6894
6895     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6896       MovDelay[x][y] = 9;
6897     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6898       MovDelay[x][y] = 1;
6899   }
6900   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6901   {
6902     TestIfBadThingTouchesOtherBadThing(x, y);
6903
6904     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6905       MovDir[x][y] = left_dir;
6906     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6907       MovDir[x][y] = right_dir;
6908
6909     if (MovDir[x][y] != old_move_dir)
6910       MovDelay[x][y] = 9;
6911   }
6912   else if (element == EL_YAMYAM)
6913   {
6914     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6915     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6916
6917     if (can_turn_left && can_turn_right)
6918       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6919     else if (can_turn_left)
6920       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6921     else if (can_turn_right)
6922       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6923     else
6924       MovDir[x][y] = back_dir;
6925
6926     MovDelay[x][y] = 16 + 16 * RND(3);
6927   }
6928   else if (element == EL_DARK_YAMYAM)
6929   {
6930     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6931                                                          left_x, left_y);
6932     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6933                                                          right_x, right_y);
6934
6935     if (can_turn_left && can_turn_right)
6936       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6937     else if (can_turn_left)
6938       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6939     else if (can_turn_right)
6940       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6941     else
6942       MovDir[x][y] = back_dir;
6943
6944     MovDelay[x][y] = 16 + 16 * RND(3);
6945   }
6946   else if (element == EL_PACMAN)
6947   {
6948     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6949     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6950
6951     if (can_turn_left && can_turn_right)
6952       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6953     else if (can_turn_left)
6954       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6955     else if (can_turn_right)
6956       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6957     else
6958       MovDir[x][y] = back_dir;
6959
6960     MovDelay[x][y] = 6 + RND(40);
6961   }
6962   else if (element == EL_PIG)
6963   {
6964     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6965     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6966     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6967     boolean should_turn_left, should_turn_right, should_move_on;
6968     int rnd_value = 24;
6969     int rnd = RND(rnd_value);
6970
6971     should_turn_left = (can_turn_left &&
6972                         (!can_move_on ||
6973                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6974                                                    y + back_dy + left_dy)));
6975     should_turn_right = (can_turn_right &&
6976                          (!can_move_on ||
6977                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6978                                                     y + back_dy + right_dy)));
6979     should_move_on = (can_move_on &&
6980                       (!can_turn_left ||
6981                        !can_turn_right ||
6982                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6983                                                  y + move_dy + left_dy) ||
6984                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6985                                                  y + move_dy + right_dy)));
6986
6987     if (should_turn_left || should_turn_right || should_move_on)
6988     {
6989       if (should_turn_left && should_turn_right && should_move_on)
6990         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6991                         rnd < 2 * rnd_value / 3 ? right_dir :
6992                         old_move_dir);
6993       else if (should_turn_left && should_turn_right)
6994         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6995       else if (should_turn_left && should_move_on)
6996         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6997       else if (should_turn_right && should_move_on)
6998         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6999       else if (should_turn_left)
7000         MovDir[x][y] = left_dir;
7001       else if (should_turn_right)
7002         MovDir[x][y] = right_dir;
7003       else if (should_move_on)
7004         MovDir[x][y] = old_move_dir;
7005     }
7006     else if (can_move_on && rnd > rnd_value / 8)
7007       MovDir[x][y] = old_move_dir;
7008     else if (can_turn_left && can_turn_right)
7009       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7010     else if (can_turn_left && rnd > rnd_value / 8)
7011       MovDir[x][y] = left_dir;
7012     else if (can_turn_right && rnd > rnd_value/8)
7013       MovDir[x][y] = right_dir;
7014     else
7015       MovDir[x][y] = back_dir;
7016
7017     xx = x + move_xy[MovDir[x][y]].dx;
7018     yy = y + move_xy[MovDir[x][y]].dy;
7019
7020     if (!IN_LEV_FIELD(xx, yy) ||
7021         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7022       MovDir[x][y] = old_move_dir;
7023
7024     MovDelay[x][y] = 0;
7025   }
7026   else if (element == EL_DRAGON)
7027   {
7028     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7029     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7030     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7031     int rnd_value = 24;
7032     int rnd = RND(rnd_value);
7033
7034     if (can_move_on && rnd > rnd_value / 8)
7035       MovDir[x][y] = old_move_dir;
7036     else if (can_turn_left && can_turn_right)
7037       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7038     else if (can_turn_left && rnd > rnd_value / 8)
7039       MovDir[x][y] = left_dir;
7040     else if (can_turn_right && rnd > rnd_value / 8)
7041       MovDir[x][y] = right_dir;
7042     else
7043       MovDir[x][y] = back_dir;
7044
7045     xx = x + move_xy[MovDir[x][y]].dx;
7046     yy = y + move_xy[MovDir[x][y]].dy;
7047
7048     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7049       MovDir[x][y] = old_move_dir;
7050
7051     MovDelay[x][y] = 0;
7052   }
7053   else if (element == EL_MOLE)
7054   {
7055     boolean can_move_on =
7056       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7057                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7058                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7059     if (!can_move_on)
7060     {
7061       boolean can_turn_left =
7062         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7063                               IS_AMOEBOID(Tile[left_x][left_y])));
7064
7065       boolean can_turn_right =
7066         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7067                               IS_AMOEBOID(Tile[right_x][right_y])));
7068
7069       if (can_turn_left && can_turn_right)
7070         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7071       else if (can_turn_left)
7072         MovDir[x][y] = left_dir;
7073       else
7074         MovDir[x][y] = right_dir;
7075     }
7076
7077     if (MovDir[x][y] != old_move_dir)
7078       MovDelay[x][y] = 9;
7079   }
7080   else if (element == EL_BALLOON)
7081   {
7082     MovDir[x][y] = game.wind_direction;
7083     MovDelay[x][y] = 0;
7084   }
7085   else if (element == EL_SPRING)
7086   {
7087     if (MovDir[x][y] & MV_HORIZONTAL)
7088     {
7089       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7090           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7091       {
7092         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7093         ResetGfxAnimation(move_x, move_y);
7094         TEST_DrawLevelField(move_x, move_y);
7095
7096         MovDir[x][y] = back_dir;
7097       }
7098       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7099                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7100         MovDir[x][y] = MV_NONE;
7101     }
7102
7103     MovDelay[x][y] = 0;
7104   }
7105   else if (element == EL_ROBOT ||
7106            element == EL_SATELLITE ||
7107            element == EL_PENGUIN ||
7108            element == EL_EMC_ANDROID)
7109   {
7110     int attr_x = -1, attr_y = -1;
7111
7112     if (game.all_players_gone)
7113     {
7114       attr_x = game.exit_x;
7115       attr_y = game.exit_y;
7116     }
7117     else
7118     {
7119       int i;
7120
7121       for (i = 0; i < MAX_PLAYERS; i++)
7122       {
7123         struct PlayerInfo *player = &stored_player[i];
7124         int jx = player->jx, jy = player->jy;
7125
7126         if (!player->active)
7127           continue;
7128
7129         if (attr_x == -1 ||
7130             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7131         {
7132           attr_x = jx;
7133           attr_y = jy;
7134         }
7135       }
7136     }
7137
7138     if (element == EL_ROBOT &&
7139         game.robot_wheel_x >= 0 &&
7140         game.robot_wheel_y >= 0 &&
7141         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7142          game.engine_version < VERSION_IDENT(3,1,0,0)))
7143     {
7144       attr_x = game.robot_wheel_x;
7145       attr_y = game.robot_wheel_y;
7146     }
7147
7148     if (element == EL_PENGUIN)
7149     {
7150       int i;
7151       static int xy[4][2] =
7152       {
7153         { 0, -1 },
7154         { -1, 0 },
7155         { +1, 0 },
7156         { 0, +1 }
7157       };
7158
7159       for (i = 0; i < NUM_DIRECTIONS; i++)
7160       {
7161         int ex = x + xy[i][0];
7162         int ey = y + xy[i][1];
7163
7164         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7165                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7166                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7167                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7168         {
7169           attr_x = ex;
7170           attr_y = ey;
7171           break;
7172         }
7173       }
7174     }
7175
7176     MovDir[x][y] = MV_NONE;
7177     if (attr_x < x)
7178       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7179     else if (attr_x > x)
7180       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7181     if (attr_y < y)
7182       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7183     else if (attr_y > y)
7184       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7185
7186     if (element == EL_ROBOT)
7187     {
7188       int newx, newy;
7189
7190       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7191         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7192       Moving2Blocked(x, y, &newx, &newy);
7193
7194       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7195         MovDelay[x][y] = 8 + 8 * !RND(3);
7196       else
7197         MovDelay[x][y] = 16;
7198     }
7199     else if (element == EL_PENGUIN)
7200     {
7201       int newx, newy;
7202
7203       MovDelay[x][y] = 1;
7204
7205       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7206       {
7207         boolean first_horiz = RND(2);
7208         int new_move_dir = MovDir[x][y];
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] =
7218           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7219         Moving2Blocked(x, y, &newx, &newy);
7220
7221         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7222           return;
7223
7224         MovDir[x][y] = old_move_dir;
7225         return;
7226       }
7227     }
7228     else if (element == EL_SATELLITE)
7229     {
7230       int newx, newy;
7231
7232       MovDelay[x][y] = 1;
7233
7234       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7235       {
7236         boolean first_horiz = RND(2);
7237         int new_move_dir = MovDir[x][y];
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] =
7247           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7248         Moving2Blocked(x, y, &newx, &newy);
7249
7250         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7251           return;
7252
7253         MovDir[x][y] = old_move_dir;
7254         return;
7255       }
7256     }
7257     else if (element == EL_EMC_ANDROID)
7258     {
7259       static int check_pos[16] =
7260       {
7261         -1,             //  0 => (invalid)
7262         7,              //  1 => MV_LEFT
7263         3,              //  2 => MV_RIGHT
7264         -1,             //  3 => (invalid)
7265         1,              //  4 =>            MV_UP
7266         0,              //  5 => MV_LEFT  | MV_UP
7267         2,              //  6 => MV_RIGHT | MV_UP
7268         -1,             //  7 => (invalid)
7269         5,              //  8 =>            MV_DOWN
7270         6,              //  9 => MV_LEFT  | MV_DOWN
7271         4,              // 10 => MV_RIGHT | MV_DOWN
7272         -1,             // 11 => (invalid)
7273         -1,             // 12 => (invalid)
7274         -1,             // 13 => (invalid)
7275         -1,             // 14 => (invalid)
7276         -1,             // 15 => (invalid)
7277       };
7278       static struct
7279       {
7280         int dx, dy;
7281         int dir;
7282       } check_xy[8] =
7283       {
7284         { -1, -1,       MV_LEFT  | MV_UP   },
7285         {  0, -1,                  MV_UP   },
7286         { +1, -1,       MV_RIGHT | MV_UP   },
7287         { +1,  0,       MV_RIGHT           },
7288         { +1, +1,       MV_RIGHT | MV_DOWN },
7289         {  0, +1,                  MV_DOWN },
7290         { -1, +1,       MV_LEFT  | MV_DOWN },
7291         { -1,  0,       MV_LEFT            },
7292       };
7293       int start_pos, check_order;
7294       boolean can_clone = FALSE;
7295       int i;
7296
7297       // check if there is any free field around current position
7298       for (i = 0; i < 8; i++)
7299       {
7300         int newx = x + check_xy[i].dx;
7301         int newy = y + check_xy[i].dy;
7302
7303         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7304         {
7305           can_clone = TRUE;
7306
7307           break;
7308         }
7309       }
7310
7311       if (can_clone)            // randomly find an element to clone
7312       {
7313         can_clone = FALSE;
7314
7315         start_pos = check_pos[RND(8)];
7316         check_order = (RND(2) ? -1 : +1);
7317
7318         for (i = 0; i < 8; i++)
7319         {
7320           int pos_raw = start_pos + i * check_order;
7321           int pos = (pos_raw + 8) % 8;
7322           int newx = x + check_xy[pos].dx;
7323           int newy = y + check_xy[pos].dy;
7324
7325           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7326           {
7327             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7328             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7329
7330             Store[x][y] = Tile[newx][newy];
7331
7332             can_clone = TRUE;
7333
7334             break;
7335           }
7336         }
7337       }
7338
7339       if (can_clone)            // randomly find a direction to move
7340       {
7341         can_clone = FALSE;
7342
7343         start_pos = check_pos[RND(8)];
7344         check_order = (RND(2) ? -1 : +1);
7345
7346         for (i = 0; i < 8; i++)
7347         {
7348           int pos_raw = start_pos + i * check_order;
7349           int pos = (pos_raw + 8) % 8;
7350           int newx = x + check_xy[pos].dx;
7351           int newy = y + check_xy[pos].dy;
7352           int new_move_dir = check_xy[pos].dir;
7353
7354           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7355           {
7356             MovDir[x][y] = new_move_dir;
7357             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7358
7359             can_clone = TRUE;
7360
7361             break;
7362           }
7363         }
7364       }
7365
7366       if (can_clone)            // cloning and moving successful
7367         return;
7368
7369       // cannot clone -- try to move towards player
7370
7371       start_pos = check_pos[MovDir[x][y] & 0x0f];
7372       check_order = (RND(2) ? -1 : +1);
7373
7374       for (i = 0; i < 3; i++)
7375       {
7376         // first check start_pos, then previous/next or (next/previous) pos
7377         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7378         int pos = (pos_raw + 8) % 8;
7379         int newx = x + check_xy[pos].dx;
7380         int newy = y + check_xy[pos].dy;
7381         int new_move_dir = check_xy[pos].dir;
7382
7383         if (IS_PLAYER(newx, newy))
7384           break;
7385
7386         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7387         {
7388           MovDir[x][y] = new_move_dir;
7389           MovDelay[x][y] = level.android_move_time * 8 + 1;
7390
7391           break;
7392         }
7393       }
7394     }
7395   }
7396   else if (move_pattern == MV_TURNING_LEFT ||
7397            move_pattern == MV_TURNING_RIGHT ||
7398            move_pattern == MV_TURNING_LEFT_RIGHT ||
7399            move_pattern == MV_TURNING_RIGHT_LEFT ||
7400            move_pattern == MV_TURNING_RANDOM ||
7401            move_pattern == MV_ALL_DIRECTIONS)
7402   {
7403     boolean can_turn_left =
7404       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7405     boolean can_turn_right =
7406       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7407
7408     if (element_info[element].move_stepsize == 0)       // "not moving"
7409       return;
7410
7411     if (move_pattern == MV_TURNING_LEFT)
7412       MovDir[x][y] = left_dir;
7413     else if (move_pattern == MV_TURNING_RIGHT)
7414       MovDir[x][y] = right_dir;
7415     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7416       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7417     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7418       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7419     else if (move_pattern == MV_TURNING_RANDOM)
7420       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7421                       can_turn_right && !can_turn_left ? right_dir :
7422                       RND(2) ? left_dir : right_dir);
7423     else if (can_turn_left && can_turn_right)
7424       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7425     else if (can_turn_left)
7426       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7427     else if (can_turn_right)
7428       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7429     else
7430       MovDir[x][y] = back_dir;
7431
7432     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7433   }
7434   else if (move_pattern == MV_HORIZONTAL ||
7435            move_pattern == MV_VERTICAL)
7436   {
7437     if (move_pattern & old_move_dir)
7438       MovDir[x][y] = back_dir;
7439     else if (move_pattern == MV_HORIZONTAL)
7440       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7441     else if (move_pattern == MV_VERTICAL)
7442       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7443
7444     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7445   }
7446   else if (move_pattern & MV_ANY_DIRECTION)
7447   {
7448     MovDir[x][y] = move_pattern;
7449     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7450   }
7451   else if (move_pattern & MV_WIND_DIRECTION)
7452   {
7453     MovDir[x][y] = game.wind_direction;
7454     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7455   }
7456   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7457   {
7458     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7459       MovDir[x][y] = left_dir;
7460     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7461       MovDir[x][y] = right_dir;
7462
7463     if (MovDir[x][y] != old_move_dir)
7464       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7465   }
7466   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7467   {
7468     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7469       MovDir[x][y] = right_dir;
7470     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7471       MovDir[x][y] = left_dir;
7472
7473     if (MovDir[x][y] != old_move_dir)
7474       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7475   }
7476   else if (move_pattern == MV_TOWARDS_PLAYER ||
7477            move_pattern == MV_AWAY_FROM_PLAYER)
7478   {
7479     int attr_x = -1, attr_y = -1;
7480     int newx, newy;
7481     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7482
7483     if (game.all_players_gone)
7484     {
7485       attr_x = game.exit_x;
7486       attr_y = game.exit_y;
7487     }
7488     else
7489     {
7490       int i;
7491
7492       for (i = 0; i < MAX_PLAYERS; i++)
7493       {
7494         struct PlayerInfo *player = &stored_player[i];
7495         int jx = player->jx, jy = player->jy;
7496
7497         if (!player->active)
7498           continue;
7499
7500         if (attr_x == -1 ||
7501             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7502         {
7503           attr_x = jx;
7504           attr_y = jy;
7505         }
7506       }
7507     }
7508
7509     MovDir[x][y] = MV_NONE;
7510     if (attr_x < x)
7511       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7512     else if (attr_x > x)
7513       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7514     if (attr_y < y)
7515       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7516     else if (attr_y > y)
7517       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7518
7519     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7520
7521     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7522     {
7523       boolean first_horiz = RND(2);
7524       int new_move_dir = MovDir[x][y];
7525
7526       if (element_info[element].move_stepsize == 0)     // "not moving"
7527       {
7528         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7529         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7530
7531         return;
7532       }
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] =
7542         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7543       Moving2Blocked(x, y, &newx, &newy);
7544
7545       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7546         return;
7547
7548       MovDir[x][y] = old_move_dir;
7549     }
7550   }
7551   else if (move_pattern == MV_WHEN_PUSHED ||
7552            move_pattern == MV_WHEN_DROPPED)
7553   {
7554     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7555       MovDir[x][y] = MV_NONE;
7556
7557     MovDelay[x][y] = 0;
7558   }
7559   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7560   {
7561     static int test_xy[7][2] =
7562     {
7563       { 0, -1 },
7564       { -1, 0 },
7565       { +1, 0 },
7566       { 0, +1 },
7567       { 0, -1 },
7568       { -1, 0 },
7569       { +1, 0 },
7570     };
7571     static int test_dir[7] =
7572     {
7573       MV_UP,
7574       MV_LEFT,
7575       MV_RIGHT,
7576       MV_DOWN,
7577       MV_UP,
7578       MV_LEFT,
7579       MV_RIGHT,
7580     };
7581     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7582     int move_preference = -1000000;     // start with very low preference
7583     int new_move_dir = MV_NONE;
7584     int start_test = RND(4);
7585     int i;
7586
7587     for (i = 0; i < NUM_DIRECTIONS; i++)
7588     {
7589       int move_dir = test_dir[start_test + i];
7590       int move_dir_preference;
7591
7592       xx = x + test_xy[start_test + i][0];
7593       yy = y + test_xy[start_test + i][1];
7594
7595       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7596           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7597       {
7598         new_move_dir = move_dir;
7599
7600         break;
7601       }
7602
7603       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7604         continue;
7605
7606       move_dir_preference = -1 * RunnerVisit[xx][yy];
7607       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7608         move_dir_preference = PlayerVisit[xx][yy];
7609
7610       if (move_dir_preference > move_preference)
7611       {
7612         // prefer field that has not been visited for the longest time
7613         move_preference = move_dir_preference;
7614         new_move_dir = move_dir;
7615       }
7616       else if (move_dir_preference == move_preference &&
7617                move_dir == old_move_dir)
7618       {
7619         // prefer last direction when all directions are preferred equally
7620         move_preference = move_dir_preference;
7621         new_move_dir = move_dir;
7622       }
7623     }
7624
7625     MovDir[x][y] = new_move_dir;
7626     if (old_move_dir != new_move_dir)
7627       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7628   }
7629 }
7630
7631 static void TurnRound(int x, int y)
7632 {
7633   int direction = MovDir[x][y];
7634
7635   TurnRoundExt(x, y);
7636
7637   GfxDir[x][y] = MovDir[x][y];
7638
7639   if (direction != MovDir[x][y])
7640     GfxFrame[x][y] = 0;
7641
7642   if (MovDelay[x][y])
7643     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7644
7645   ResetGfxFrame(x, y);
7646 }
7647
7648 static boolean JustBeingPushed(int x, int y)
7649 {
7650   int i;
7651
7652   for (i = 0; i < MAX_PLAYERS; i++)
7653   {
7654     struct PlayerInfo *player = &stored_player[i];
7655
7656     if (player->active && player->is_pushing && player->MovPos)
7657     {
7658       int next_jx = player->jx + (player->jx - player->last_jx);
7659       int next_jy = player->jy + (player->jy - player->last_jy);
7660
7661       if (x == next_jx && y == next_jy)
7662         return TRUE;
7663     }
7664   }
7665
7666   return FALSE;
7667 }
7668
7669 static void StartMoving(int x, int y)
7670 {
7671   boolean started_moving = FALSE;       // some elements can fall _and_ move
7672   int element = Tile[x][y];
7673
7674   if (Stop[x][y])
7675     return;
7676
7677   if (MovDelay[x][y] == 0)
7678     GfxAction[x][y] = ACTION_DEFAULT;
7679
7680   if (CAN_FALL(element) && y < lev_fieldy - 1)
7681   {
7682     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7683         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7684       if (JustBeingPushed(x, y))
7685         return;
7686
7687     if (element == EL_QUICKSAND_FULL)
7688     {
7689       if (IS_FREE(x, y + 1))
7690       {
7691         InitMovingField(x, y, MV_DOWN);
7692         started_moving = TRUE;
7693
7694         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7695 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7696         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7697           Store[x][y] = EL_ROCK;
7698 #else
7699         Store[x][y] = EL_ROCK;
7700 #endif
7701
7702         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7703       }
7704       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7705       {
7706         if (!MovDelay[x][y])
7707         {
7708           MovDelay[x][y] = TILEY + 1;
7709
7710           ResetGfxAnimation(x, y);
7711           ResetGfxAnimation(x, y + 1);
7712         }
7713
7714         if (MovDelay[x][y])
7715         {
7716           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7717           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7718
7719           MovDelay[x][y]--;
7720           if (MovDelay[x][y])
7721             return;
7722         }
7723
7724         Tile[x][y] = EL_QUICKSAND_EMPTY;
7725         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7726         Store[x][y + 1] = Store[x][y];
7727         Store[x][y] = 0;
7728
7729         PlayLevelSoundAction(x, y, ACTION_FILLING);
7730       }
7731       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7732       {
7733         if (!MovDelay[x][y])
7734         {
7735           MovDelay[x][y] = TILEY + 1;
7736
7737           ResetGfxAnimation(x, y);
7738           ResetGfxAnimation(x, y + 1);
7739         }
7740
7741         if (MovDelay[x][y])
7742         {
7743           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7744           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7745
7746           MovDelay[x][y]--;
7747           if (MovDelay[x][y])
7748             return;
7749         }
7750
7751         Tile[x][y] = EL_QUICKSAND_EMPTY;
7752         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7753         Store[x][y + 1] = Store[x][y];
7754         Store[x][y] = 0;
7755
7756         PlayLevelSoundAction(x, y, ACTION_FILLING);
7757       }
7758     }
7759     else if (element == EL_QUICKSAND_FAST_FULL)
7760     {
7761       if (IS_FREE(x, y + 1))
7762       {
7763         InitMovingField(x, y, MV_DOWN);
7764         started_moving = TRUE;
7765
7766         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7767 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7768         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7769           Store[x][y] = EL_ROCK;
7770 #else
7771         Store[x][y] = EL_ROCK;
7772 #endif
7773
7774         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7775       }
7776       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7777       {
7778         if (!MovDelay[x][y])
7779         {
7780           MovDelay[x][y] = TILEY + 1;
7781
7782           ResetGfxAnimation(x, y);
7783           ResetGfxAnimation(x, y + 1);
7784         }
7785
7786         if (MovDelay[x][y])
7787         {
7788           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7789           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7790
7791           MovDelay[x][y]--;
7792           if (MovDelay[x][y])
7793             return;
7794         }
7795
7796         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7797         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7798         Store[x][y + 1] = Store[x][y];
7799         Store[x][y] = 0;
7800
7801         PlayLevelSoundAction(x, y, ACTION_FILLING);
7802       }
7803       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7804       {
7805         if (!MovDelay[x][y])
7806         {
7807           MovDelay[x][y] = TILEY + 1;
7808
7809           ResetGfxAnimation(x, y);
7810           ResetGfxAnimation(x, y + 1);
7811         }
7812
7813         if (MovDelay[x][y])
7814         {
7815           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7816           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7817
7818           MovDelay[x][y]--;
7819           if (MovDelay[x][y])
7820             return;
7821         }
7822
7823         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7824         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7825         Store[x][y + 1] = Store[x][y];
7826         Store[x][y] = 0;
7827
7828         PlayLevelSoundAction(x, y, ACTION_FILLING);
7829       }
7830     }
7831     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7832              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7833     {
7834       InitMovingField(x, y, MV_DOWN);
7835       started_moving = TRUE;
7836
7837       Tile[x][y] = EL_QUICKSAND_FILLING;
7838       Store[x][y] = element;
7839
7840       PlayLevelSoundAction(x, y, ACTION_FILLING);
7841     }
7842     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7843              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7844     {
7845       InitMovingField(x, y, MV_DOWN);
7846       started_moving = TRUE;
7847
7848       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7849       Store[x][y] = element;
7850
7851       PlayLevelSoundAction(x, y, ACTION_FILLING);
7852     }
7853     else if (element == EL_MAGIC_WALL_FULL)
7854     {
7855       if (IS_FREE(x, y + 1))
7856       {
7857         InitMovingField(x, y, MV_DOWN);
7858         started_moving = TRUE;
7859
7860         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7861         Store[x][y] = EL_CHANGED(Store[x][y]);
7862       }
7863       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7864       {
7865         if (!MovDelay[x][y])
7866           MovDelay[x][y] = TILEY / 4 + 1;
7867
7868         if (MovDelay[x][y])
7869         {
7870           MovDelay[x][y]--;
7871           if (MovDelay[x][y])
7872             return;
7873         }
7874
7875         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7876         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7877         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7878         Store[x][y] = 0;
7879       }
7880     }
7881     else if (element == EL_BD_MAGIC_WALL_FULL)
7882     {
7883       if (IS_FREE(x, y + 1))
7884       {
7885         InitMovingField(x, y, MV_DOWN);
7886         started_moving = TRUE;
7887
7888         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7889         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7890       }
7891       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7892       {
7893         if (!MovDelay[x][y])
7894           MovDelay[x][y] = TILEY / 4 + 1;
7895
7896         if (MovDelay[x][y])
7897         {
7898           MovDelay[x][y]--;
7899           if (MovDelay[x][y])
7900             return;
7901         }
7902
7903         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7904         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7905         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7906         Store[x][y] = 0;
7907       }
7908     }
7909     else if (element == EL_DC_MAGIC_WALL_FULL)
7910     {
7911       if (IS_FREE(x, y + 1))
7912       {
7913         InitMovingField(x, y, MV_DOWN);
7914         started_moving = TRUE;
7915
7916         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7917         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7918       }
7919       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7920       {
7921         if (!MovDelay[x][y])
7922           MovDelay[x][y] = TILEY / 4 + 1;
7923
7924         if (MovDelay[x][y])
7925         {
7926           MovDelay[x][y]--;
7927           if (MovDelay[x][y])
7928             return;
7929         }
7930
7931         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7932         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7933         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7934         Store[x][y] = 0;
7935       }
7936     }
7937     else if ((CAN_PASS_MAGIC_WALL(element) &&
7938               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7939                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7940              (CAN_PASS_DC_MAGIC_WALL(element) &&
7941               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7942
7943     {
7944       InitMovingField(x, y, MV_DOWN);
7945       started_moving = TRUE;
7946
7947       Tile[x][y] =
7948         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7949          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7950          EL_DC_MAGIC_WALL_FILLING);
7951       Store[x][y] = element;
7952     }
7953     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7954     {
7955       SplashAcid(x, y + 1);
7956
7957       InitMovingField(x, y, MV_DOWN);
7958       started_moving = TRUE;
7959
7960       Store[x][y] = EL_ACID;
7961     }
7962     else if (
7963              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7964               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7965              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7966               CAN_FALL(element) && WasJustFalling[x][y] &&
7967               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7968
7969              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7970               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7971               (Tile[x][y + 1] == EL_BLOCKED)))
7972     {
7973       /* this is needed for a special case not covered by calling "Impact()"
7974          from "ContinueMoving()": if an element moves to a tile directly below
7975          another element which was just falling on that tile (which was empty
7976          in the previous frame), the falling element above would just stop
7977          instead of smashing the element below (in previous version, the above
7978          element was just checked for "moving" instead of "falling", resulting
7979          in incorrect smashes caused by horizontal movement of the above
7980          element; also, the case of the player being the element to smash was
7981          simply not covered here... :-/ ) */
7982
7983       CheckCollision[x][y] = 0;
7984       CheckImpact[x][y] = 0;
7985
7986       Impact(x, y);
7987     }
7988     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7989     {
7990       if (MovDir[x][y] == MV_NONE)
7991       {
7992         InitMovingField(x, y, MV_DOWN);
7993         started_moving = TRUE;
7994       }
7995     }
7996     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7997     {
7998       if (WasJustFalling[x][y]) // prevent animation from being restarted
7999         MovDir[x][y] = MV_DOWN;
8000
8001       InitMovingField(x, y, MV_DOWN);
8002       started_moving = TRUE;
8003     }
8004     else if (element == EL_AMOEBA_DROP)
8005     {
8006       Tile[x][y] = EL_AMOEBA_GROWING;
8007       Store[x][y] = EL_AMOEBA_WET;
8008     }
8009     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8010               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8011              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8012              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8013     {
8014       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8015                                 (IS_FREE(x - 1, y + 1) ||
8016                                  Tile[x - 1][y + 1] == EL_ACID));
8017       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8018                                 (IS_FREE(x + 1, y + 1) ||
8019                                  Tile[x + 1][y + 1] == EL_ACID));
8020       boolean can_fall_any  = (can_fall_left || can_fall_right);
8021       boolean can_fall_both = (can_fall_left && can_fall_right);
8022       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8023
8024       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8025       {
8026         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8027           can_fall_right = FALSE;
8028         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8029           can_fall_left = FALSE;
8030         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8031           can_fall_right = FALSE;
8032         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8033           can_fall_left = FALSE;
8034
8035         can_fall_any  = (can_fall_left || can_fall_right);
8036         can_fall_both = FALSE;
8037       }
8038
8039       if (can_fall_both)
8040       {
8041         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8042           can_fall_right = FALSE;       // slip down on left side
8043         else
8044           can_fall_left = !(can_fall_right = RND(2));
8045
8046         can_fall_both = FALSE;
8047       }
8048
8049       if (can_fall_any)
8050       {
8051         // if not determined otherwise, prefer left side for slipping down
8052         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8053         started_moving = TRUE;
8054       }
8055     }
8056     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8057     {
8058       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8059       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8060       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8061       int belt_dir = game.belt_dir[belt_nr];
8062
8063       if ((belt_dir == MV_LEFT  && left_is_free) ||
8064           (belt_dir == MV_RIGHT && right_is_free))
8065       {
8066         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8067
8068         InitMovingField(x, y, belt_dir);
8069         started_moving = TRUE;
8070
8071         Pushed[x][y] = TRUE;
8072         Pushed[nextx][y] = TRUE;
8073
8074         GfxAction[x][y] = ACTION_DEFAULT;
8075       }
8076       else
8077       {
8078         MovDir[x][y] = 0;       // if element was moving, stop it
8079       }
8080     }
8081   }
8082
8083   // not "else if" because of elements that can fall and move (EL_SPRING)
8084   if (CAN_MOVE(element) && !started_moving)
8085   {
8086     int move_pattern = element_info[element].move_pattern;
8087     int newx, newy;
8088
8089     Moving2Blocked(x, y, &newx, &newy);
8090
8091     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8092       return;
8093
8094     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8095         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8096     {
8097       WasJustMoving[x][y] = 0;
8098       CheckCollision[x][y] = 0;
8099
8100       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8101
8102       if (Tile[x][y] != element)        // element has changed
8103         return;
8104     }
8105
8106     if (!MovDelay[x][y])        // start new movement phase
8107     {
8108       // all objects that can change their move direction after each step
8109       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8110
8111       if (element != EL_YAMYAM &&
8112           element != EL_DARK_YAMYAM &&
8113           element != EL_PACMAN &&
8114           !(move_pattern & MV_ANY_DIRECTION) &&
8115           move_pattern != MV_TURNING_LEFT &&
8116           move_pattern != MV_TURNING_RIGHT &&
8117           move_pattern != MV_TURNING_LEFT_RIGHT &&
8118           move_pattern != MV_TURNING_RIGHT_LEFT &&
8119           move_pattern != MV_TURNING_RANDOM)
8120       {
8121         TurnRound(x, y);
8122
8123         if (MovDelay[x][y] && (element == EL_BUG ||
8124                                element == EL_SPACESHIP ||
8125                                element == EL_SP_SNIKSNAK ||
8126                                element == EL_SP_ELECTRON ||
8127                                element == EL_MOLE))
8128           TEST_DrawLevelField(x, y);
8129       }
8130     }
8131
8132     if (MovDelay[x][y])         // wait some time before next movement
8133     {
8134       MovDelay[x][y]--;
8135
8136       if (element == EL_ROBOT ||
8137           element == EL_YAMYAM ||
8138           element == EL_DARK_YAMYAM)
8139       {
8140         DrawLevelElementAnimationIfNeeded(x, y, element);
8141         PlayLevelSoundAction(x, y, ACTION_WAITING);
8142       }
8143       else if (element == EL_SP_ELECTRON)
8144         DrawLevelElementAnimationIfNeeded(x, y, element);
8145       else if (element == EL_DRAGON)
8146       {
8147         int i;
8148         int dir = MovDir[x][y];
8149         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8150         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8151         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8152                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8153                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8154                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8155         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8156
8157         GfxAction[x][y] = ACTION_ATTACKING;
8158
8159         if (IS_PLAYER(x, y))
8160           DrawPlayerField(x, y);
8161         else
8162           TEST_DrawLevelField(x, y);
8163
8164         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8165
8166         for (i = 1; i <= 3; i++)
8167         {
8168           int xx = x + i * dx;
8169           int yy = y + i * dy;
8170           int sx = SCREENX(xx);
8171           int sy = SCREENY(yy);
8172           int flame_graphic = graphic + (i - 1);
8173
8174           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8175             break;
8176
8177           if (MovDelay[x][y])
8178           {
8179             int flamed = MovingOrBlocked2Element(xx, yy);
8180
8181             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8182               Bang(xx, yy);
8183             else
8184               RemoveMovingField(xx, yy);
8185
8186             ChangeDelay[xx][yy] = 0;
8187
8188             Tile[xx][yy] = EL_FLAMES;
8189
8190             if (IN_SCR_FIELD(sx, sy))
8191             {
8192               TEST_DrawLevelFieldCrumbled(xx, yy);
8193               DrawGraphic(sx, sy, flame_graphic, frame);
8194             }
8195           }
8196           else
8197           {
8198             if (Tile[xx][yy] == EL_FLAMES)
8199               Tile[xx][yy] = EL_EMPTY;
8200             TEST_DrawLevelField(xx, yy);
8201           }
8202         }
8203       }
8204
8205       if (MovDelay[x][y])       // element still has to wait some time
8206       {
8207         PlayLevelSoundAction(x, y, ACTION_WAITING);
8208
8209         return;
8210       }
8211     }
8212
8213     // now make next step
8214
8215     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8216
8217     if (DONT_COLLIDE_WITH(element) &&
8218         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8219         !PLAYER_ENEMY_PROTECTED(newx, newy))
8220     {
8221       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8222
8223       return;
8224     }
8225
8226     else if (CAN_MOVE_INTO_ACID(element) &&
8227              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8228              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8229              (MovDir[x][y] == MV_DOWN ||
8230               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8231     {
8232       SplashAcid(newx, newy);
8233       Store[x][y] = EL_ACID;
8234     }
8235     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8236     {
8237       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8238           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8239           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8240           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8241       {
8242         RemoveField(x, y);
8243         TEST_DrawLevelField(x, y);
8244
8245         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8246         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8247           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8248
8249         game.friends_still_needed--;
8250         if (!game.friends_still_needed &&
8251             !game.GameOver &&
8252             game.all_players_gone)
8253           LevelSolved();
8254
8255         return;
8256       }
8257       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8258       {
8259         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8260           TEST_DrawLevelField(newx, newy);
8261         else
8262           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8263       }
8264       else if (!IS_FREE(newx, newy))
8265       {
8266         GfxAction[x][y] = ACTION_WAITING;
8267
8268         if (IS_PLAYER(x, y))
8269           DrawPlayerField(x, y);
8270         else
8271           TEST_DrawLevelField(x, y);
8272
8273         return;
8274       }
8275     }
8276     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8277     {
8278       if (IS_FOOD_PIG(Tile[newx][newy]))
8279       {
8280         if (IS_MOVING(newx, newy))
8281           RemoveMovingField(newx, newy);
8282         else
8283         {
8284           Tile[newx][newy] = EL_EMPTY;
8285           TEST_DrawLevelField(newx, newy);
8286         }
8287
8288         PlayLevelSound(x, y, SND_PIG_DIGGING);
8289       }
8290       else if (!IS_FREE(newx, newy))
8291       {
8292         if (IS_PLAYER(x, y))
8293           DrawPlayerField(x, y);
8294         else
8295           TEST_DrawLevelField(x, y);
8296
8297         return;
8298       }
8299     }
8300     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8301     {
8302       if (Store[x][y] != EL_EMPTY)
8303       {
8304         boolean can_clone = FALSE;
8305         int xx, yy;
8306
8307         // check if element to clone is still there
8308         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8309         {
8310           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8311           {
8312             can_clone = TRUE;
8313
8314             break;
8315           }
8316         }
8317
8318         // cannot clone or target field not free anymore -- do not clone
8319         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8320           Store[x][y] = EL_EMPTY;
8321       }
8322
8323       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8324       {
8325         if (IS_MV_DIAGONAL(MovDir[x][y]))
8326         {
8327           int diagonal_move_dir = MovDir[x][y];
8328           int stored = Store[x][y];
8329           int change_delay = 8;
8330           int graphic;
8331
8332           // android is moving diagonally
8333
8334           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8335
8336           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8337           GfxElement[x][y] = EL_EMC_ANDROID;
8338           GfxAction[x][y] = ACTION_SHRINKING;
8339           GfxDir[x][y] = diagonal_move_dir;
8340           ChangeDelay[x][y] = change_delay;
8341
8342           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8343                                    GfxDir[x][y]);
8344
8345           DrawLevelGraphicAnimation(x, y, graphic);
8346           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8347
8348           if (Tile[newx][newy] == EL_ACID)
8349           {
8350             SplashAcid(newx, newy);
8351
8352             return;
8353           }
8354
8355           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8356
8357           Store[newx][newy] = EL_EMC_ANDROID;
8358           GfxElement[newx][newy] = EL_EMC_ANDROID;
8359           GfxAction[newx][newy] = ACTION_GROWING;
8360           GfxDir[newx][newy] = diagonal_move_dir;
8361           ChangeDelay[newx][newy] = change_delay;
8362
8363           graphic = el_act_dir2img(GfxElement[newx][newy],
8364                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8365
8366           DrawLevelGraphicAnimation(newx, newy, graphic);
8367           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8368
8369           return;
8370         }
8371         else
8372         {
8373           Tile[newx][newy] = EL_EMPTY;
8374           TEST_DrawLevelField(newx, newy);
8375
8376           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8377         }
8378       }
8379       else if (!IS_FREE(newx, newy))
8380       {
8381         return;
8382       }
8383     }
8384     else if (IS_CUSTOM_ELEMENT(element) &&
8385              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8386     {
8387       if (!DigFieldByCE(newx, newy, element))
8388         return;
8389
8390       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8391       {
8392         RunnerVisit[x][y] = FrameCounter;
8393         PlayerVisit[x][y] /= 8;         // expire player visit path
8394       }
8395     }
8396     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8397     {
8398       if (!IS_FREE(newx, newy))
8399       {
8400         if (IS_PLAYER(x, y))
8401           DrawPlayerField(x, y);
8402         else
8403           TEST_DrawLevelField(x, y);
8404
8405         return;
8406       }
8407       else
8408       {
8409         boolean wanna_flame = !RND(10);
8410         int dx = newx - x, dy = newy - y;
8411         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8412         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8413         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8414                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8415         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8416                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8417
8418         if ((wanna_flame ||
8419              IS_CLASSIC_ENEMY(element1) ||
8420              IS_CLASSIC_ENEMY(element2)) &&
8421             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8422             element1 != EL_FLAMES && element2 != EL_FLAMES)
8423         {
8424           ResetGfxAnimation(x, y);
8425           GfxAction[x][y] = ACTION_ATTACKING;
8426
8427           if (IS_PLAYER(x, y))
8428             DrawPlayerField(x, y);
8429           else
8430             TEST_DrawLevelField(x, y);
8431
8432           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8433
8434           MovDelay[x][y] = 50;
8435
8436           Tile[newx][newy] = EL_FLAMES;
8437           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8438             Tile[newx1][newy1] = EL_FLAMES;
8439           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8440             Tile[newx2][newy2] = EL_FLAMES;
8441
8442           return;
8443         }
8444       }
8445     }
8446     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8447              Tile[newx][newy] == EL_DIAMOND)
8448     {
8449       if (IS_MOVING(newx, newy))
8450         RemoveMovingField(newx, newy);
8451       else
8452       {
8453         Tile[newx][newy] = EL_EMPTY;
8454         TEST_DrawLevelField(newx, newy);
8455       }
8456
8457       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8458     }
8459     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8460              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8461     {
8462       if (AmoebaNr[newx][newy])
8463       {
8464         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8465         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8466             Tile[newx][newy] == EL_BD_AMOEBA)
8467           AmoebaCnt[AmoebaNr[newx][newy]]--;
8468       }
8469
8470       if (IS_MOVING(newx, newy))
8471       {
8472         RemoveMovingField(newx, newy);
8473       }
8474       else
8475       {
8476         Tile[newx][newy] = EL_EMPTY;
8477         TEST_DrawLevelField(newx, newy);
8478       }
8479
8480       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8481     }
8482     else if ((element == EL_PACMAN || element == EL_MOLE)
8483              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8484     {
8485       if (AmoebaNr[newx][newy])
8486       {
8487         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8488         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8489             Tile[newx][newy] == EL_BD_AMOEBA)
8490           AmoebaCnt[AmoebaNr[newx][newy]]--;
8491       }
8492
8493       if (element == EL_MOLE)
8494       {
8495         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8496         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8497
8498         ResetGfxAnimation(x, y);
8499         GfxAction[x][y] = ACTION_DIGGING;
8500         TEST_DrawLevelField(x, y);
8501
8502         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8503
8504         return;                         // wait for shrinking amoeba
8505       }
8506       else      // element == EL_PACMAN
8507       {
8508         Tile[newx][newy] = EL_EMPTY;
8509         TEST_DrawLevelField(newx, newy);
8510         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8511       }
8512     }
8513     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8514              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8515               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8516     {
8517       // wait for shrinking amoeba to completely disappear
8518       return;
8519     }
8520     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8521     {
8522       // object was running against a wall
8523
8524       TurnRound(x, y);
8525
8526       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8527         DrawLevelElementAnimation(x, y, element);
8528
8529       if (DONT_TOUCH(element))
8530         TestIfBadThingTouchesPlayer(x, y);
8531
8532       return;
8533     }
8534
8535     InitMovingField(x, y, MovDir[x][y]);
8536
8537     PlayLevelSoundAction(x, y, ACTION_MOVING);
8538   }
8539
8540   if (MovDir[x][y])
8541     ContinueMoving(x, y);
8542 }
8543
8544 void ContinueMoving(int x, int y)
8545 {
8546   int element = Tile[x][y];
8547   struct ElementInfo *ei = &element_info[element];
8548   int direction = MovDir[x][y];
8549   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8550   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8551   int newx = x + dx, newy = y + dy;
8552   int stored = Store[x][y];
8553   int stored_new = Store[newx][newy];
8554   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8555   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8556   boolean last_line = (newy == lev_fieldy - 1);
8557   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8558
8559   if (pushed_by_player)         // special case: moving object pushed by player
8560   {
8561     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8562   }
8563   else if (use_step_delay)      // special case: moving object has step delay
8564   {
8565     if (!MovDelay[x][y])
8566       MovPos[x][y] += getElementMoveStepsize(x, y);
8567
8568     if (MovDelay[x][y])
8569       MovDelay[x][y]--;
8570     else
8571       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8572
8573     if (MovDelay[x][y])
8574     {
8575       TEST_DrawLevelField(x, y);
8576
8577       return;   // element is still waiting
8578     }
8579   }
8580   else                          // normal case: generically moving object
8581   {
8582     MovPos[x][y] += getElementMoveStepsize(x, y);
8583   }
8584
8585   if (ABS(MovPos[x][y]) < TILEX)
8586   {
8587     TEST_DrawLevelField(x, y);
8588
8589     return;     // element is still moving
8590   }
8591
8592   // element reached destination field
8593
8594   Tile[x][y] = EL_EMPTY;
8595   Tile[newx][newy] = element;
8596   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8597
8598   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8599   {
8600     element = Tile[newx][newy] = EL_ACID;
8601   }
8602   else if (element == EL_MOLE)
8603   {
8604     Tile[x][y] = EL_SAND;
8605
8606     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8607   }
8608   else if (element == EL_QUICKSAND_FILLING)
8609   {
8610     element = Tile[newx][newy] = get_next_element(element);
8611     Store[newx][newy] = Store[x][y];
8612   }
8613   else if (element == EL_QUICKSAND_EMPTYING)
8614   {
8615     Tile[x][y] = get_next_element(element);
8616     element = Tile[newx][newy] = Store[x][y];
8617   }
8618   else if (element == EL_QUICKSAND_FAST_FILLING)
8619   {
8620     element = Tile[newx][newy] = get_next_element(element);
8621     Store[newx][newy] = Store[x][y];
8622   }
8623   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8624   {
8625     Tile[x][y] = get_next_element(element);
8626     element = Tile[newx][newy] = Store[x][y];
8627   }
8628   else if (element == EL_MAGIC_WALL_FILLING)
8629   {
8630     element = Tile[newx][newy] = get_next_element(element);
8631     if (!game.magic_wall_active)
8632       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8633     Store[newx][newy] = Store[x][y];
8634   }
8635   else if (element == EL_MAGIC_WALL_EMPTYING)
8636   {
8637     Tile[x][y] = get_next_element(element);
8638     if (!game.magic_wall_active)
8639       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8640     element = Tile[newx][newy] = Store[x][y];
8641
8642     InitField(newx, newy, FALSE);
8643   }
8644   else if (element == EL_BD_MAGIC_WALL_FILLING)
8645   {
8646     element = Tile[newx][newy] = get_next_element(element);
8647     if (!game.magic_wall_active)
8648       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8649     Store[newx][newy] = Store[x][y];
8650   }
8651   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8652   {
8653     Tile[x][y] = get_next_element(element);
8654     if (!game.magic_wall_active)
8655       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8656     element = Tile[newx][newy] = Store[x][y];
8657
8658     InitField(newx, newy, FALSE);
8659   }
8660   else if (element == EL_DC_MAGIC_WALL_FILLING)
8661   {
8662     element = Tile[newx][newy] = get_next_element(element);
8663     if (!game.magic_wall_active)
8664       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8665     Store[newx][newy] = Store[x][y];
8666   }
8667   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8668   {
8669     Tile[x][y] = get_next_element(element);
8670     if (!game.magic_wall_active)
8671       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8672     element = Tile[newx][newy] = Store[x][y];
8673
8674     InitField(newx, newy, FALSE);
8675   }
8676   else if (element == EL_AMOEBA_DROPPING)
8677   {
8678     Tile[x][y] = get_next_element(element);
8679     element = Tile[newx][newy] = Store[x][y];
8680   }
8681   else if (element == EL_SOKOBAN_OBJECT)
8682   {
8683     if (Back[x][y])
8684       Tile[x][y] = Back[x][y];
8685
8686     if (Back[newx][newy])
8687       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8688
8689     Back[x][y] = Back[newx][newy] = 0;
8690   }
8691
8692   Store[x][y] = EL_EMPTY;
8693   MovPos[x][y] = 0;
8694   MovDir[x][y] = 0;
8695   MovDelay[x][y] = 0;
8696
8697   MovDelay[newx][newy] = 0;
8698
8699   if (CAN_CHANGE_OR_HAS_ACTION(element))
8700   {
8701     // copy element change control values to new field
8702     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8703     ChangePage[newx][newy]  = ChangePage[x][y];
8704     ChangeCount[newx][newy] = ChangeCount[x][y];
8705     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8706   }
8707
8708   CustomValue[newx][newy] = CustomValue[x][y];
8709
8710   ChangeDelay[x][y] = 0;
8711   ChangePage[x][y] = -1;
8712   ChangeCount[x][y] = 0;
8713   ChangeEvent[x][y] = -1;
8714
8715   CustomValue[x][y] = 0;
8716
8717   // copy animation control values to new field
8718   GfxFrame[newx][newy]  = GfxFrame[x][y];
8719   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8720   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8721   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8722
8723   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8724
8725   // some elements can leave other elements behind after moving
8726   if (ei->move_leave_element != EL_EMPTY &&
8727       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8728       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8729   {
8730     int move_leave_element = ei->move_leave_element;
8731
8732     // this makes it possible to leave the removed element again
8733     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8734       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8735
8736     Tile[x][y] = move_leave_element;
8737
8738     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8739       MovDir[x][y] = direction;
8740
8741     InitField(x, y, FALSE);
8742
8743     if (GFX_CRUMBLED(Tile[x][y]))
8744       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8745
8746     if (ELEM_IS_PLAYER(move_leave_element))
8747       RelocatePlayer(x, y, move_leave_element);
8748   }
8749
8750   // do this after checking for left-behind element
8751   ResetGfxAnimation(x, y);      // reset animation values for old field
8752
8753   if (!CAN_MOVE(element) ||
8754       (CAN_FALL(element) && direction == MV_DOWN &&
8755        (element == EL_SPRING ||
8756         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8757         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8758     GfxDir[x][y] = MovDir[newx][newy] = 0;
8759
8760   TEST_DrawLevelField(x, y);
8761   TEST_DrawLevelField(newx, newy);
8762
8763   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8764
8765   // prevent pushed element from moving on in pushed direction
8766   if (pushed_by_player && CAN_MOVE(element) &&
8767       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8768       !(element_info[element].move_pattern & direction))
8769     TurnRound(newx, newy);
8770
8771   // prevent elements on conveyor belt from moving on in last direction
8772   if (pushed_by_conveyor && CAN_FALL(element) &&
8773       direction & MV_HORIZONTAL)
8774     MovDir[newx][newy] = 0;
8775
8776   if (!pushed_by_player)
8777   {
8778     int nextx = newx + dx, nexty = newy + dy;
8779     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8780
8781     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8782
8783     if (CAN_FALL(element) && direction == MV_DOWN)
8784       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8785
8786     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8787       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8788
8789     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8790       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8791   }
8792
8793   if (DONT_TOUCH(element))      // object may be nasty to player or others
8794   {
8795     TestIfBadThingTouchesPlayer(newx, newy);
8796     TestIfBadThingTouchesFriend(newx, newy);
8797
8798     if (!IS_CUSTOM_ELEMENT(element))
8799       TestIfBadThingTouchesOtherBadThing(newx, newy);
8800   }
8801   else if (element == EL_PENGUIN)
8802     TestIfFriendTouchesBadThing(newx, newy);
8803
8804   if (DONT_GET_HIT_BY(element))
8805   {
8806     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8807   }
8808
8809   // give the player one last chance (one more frame) to move away
8810   if (CAN_FALL(element) && direction == MV_DOWN &&
8811       (last_line || (!IS_FREE(x, newy + 1) &&
8812                      (!IS_PLAYER(x, newy + 1) ||
8813                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8814     Impact(x, newy);
8815
8816   if (pushed_by_player && !game.use_change_when_pushing_bug)
8817   {
8818     int push_side = MV_DIR_OPPOSITE(direction);
8819     struct PlayerInfo *player = PLAYERINFO(x, y);
8820
8821     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8822                                player->index_bit, push_side);
8823     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8824                                         player->index_bit, push_side);
8825   }
8826
8827   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8828     MovDelay[newx][newy] = 1;
8829
8830   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8831
8832   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8833   TestIfElementHitsCustomElement(newx, newy, direction);
8834   TestIfPlayerTouchesCustomElement(newx, newy);
8835   TestIfElementTouchesCustomElement(newx, newy);
8836
8837   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8838       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8839     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8840                              MV_DIR_OPPOSITE(direction));
8841 }
8842
8843 int AmoebaNeighbourNr(int ax, int ay)
8844 {
8845   int i;
8846   int element = Tile[ax][ay];
8847   int group_nr = 0;
8848   static int xy[4][2] =
8849   {
8850     { 0, -1 },
8851     { -1, 0 },
8852     { +1, 0 },
8853     { 0, +1 }
8854   };
8855
8856   for (i = 0; i < NUM_DIRECTIONS; i++)
8857   {
8858     int x = ax + xy[i][0];
8859     int y = ay + xy[i][1];
8860
8861     if (!IN_LEV_FIELD(x, y))
8862       continue;
8863
8864     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8865       group_nr = AmoebaNr[x][y];
8866   }
8867
8868   return group_nr;
8869 }
8870
8871 static void AmoebaMerge(int ax, int ay)
8872 {
8873   int i, x, y, xx, yy;
8874   int new_group_nr = AmoebaNr[ax][ay];
8875   static int xy[4][2] =
8876   {
8877     { 0, -1 },
8878     { -1, 0 },
8879     { +1, 0 },
8880     { 0, +1 }
8881   };
8882
8883   if (new_group_nr == 0)
8884     return;
8885
8886   for (i = 0; i < NUM_DIRECTIONS; i++)
8887   {
8888     x = ax + xy[i][0];
8889     y = ay + xy[i][1];
8890
8891     if (!IN_LEV_FIELD(x, y))
8892       continue;
8893
8894     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8895          Tile[x][y] == EL_BD_AMOEBA ||
8896          Tile[x][y] == EL_AMOEBA_DEAD) &&
8897         AmoebaNr[x][y] != new_group_nr)
8898     {
8899       int old_group_nr = AmoebaNr[x][y];
8900
8901       if (old_group_nr == 0)
8902         return;
8903
8904       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8905       AmoebaCnt[old_group_nr] = 0;
8906       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8907       AmoebaCnt2[old_group_nr] = 0;
8908
8909       SCAN_PLAYFIELD(xx, yy)
8910       {
8911         if (AmoebaNr[xx][yy] == old_group_nr)
8912           AmoebaNr[xx][yy] = new_group_nr;
8913       }
8914     }
8915   }
8916 }
8917
8918 void AmoebaToDiamond(int ax, int ay)
8919 {
8920   int i, x, y;
8921
8922   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8923   {
8924     int group_nr = AmoebaNr[ax][ay];
8925
8926 #ifdef DEBUG
8927     if (group_nr == 0)
8928     {
8929       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8930       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8931
8932       return;
8933     }
8934 #endif
8935
8936     SCAN_PLAYFIELD(x, y)
8937     {
8938       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8939       {
8940         AmoebaNr[x][y] = 0;
8941         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8942       }
8943     }
8944
8945     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8946                             SND_AMOEBA_TURNING_TO_GEM :
8947                             SND_AMOEBA_TURNING_TO_ROCK));
8948     Bang(ax, ay);
8949   }
8950   else
8951   {
8952     static int xy[4][2] =
8953     {
8954       { 0, -1 },
8955       { -1, 0 },
8956       { +1, 0 },
8957       { 0, +1 }
8958     };
8959
8960     for (i = 0; i < NUM_DIRECTIONS; i++)
8961     {
8962       x = ax + xy[i][0];
8963       y = ay + xy[i][1];
8964
8965       if (!IN_LEV_FIELD(x, y))
8966         continue;
8967
8968       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8969       {
8970         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8971                               SND_AMOEBA_TURNING_TO_GEM :
8972                               SND_AMOEBA_TURNING_TO_ROCK));
8973         Bang(x, y);
8974       }
8975     }
8976   }
8977 }
8978
8979 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8980 {
8981   int x, y;
8982   int group_nr = AmoebaNr[ax][ay];
8983   boolean done = FALSE;
8984
8985 #ifdef DEBUG
8986   if (group_nr == 0)
8987   {
8988     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8989     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8990
8991     return;
8992   }
8993 #endif
8994
8995   SCAN_PLAYFIELD(x, y)
8996   {
8997     if (AmoebaNr[x][y] == group_nr &&
8998         (Tile[x][y] == EL_AMOEBA_DEAD ||
8999          Tile[x][y] == EL_BD_AMOEBA ||
9000          Tile[x][y] == EL_AMOEBA_GROWING))
9001     {
9002       AmoebaNr[x][y] = 0;
9003       Tile[x][y] = new_element;
9004       InitField(x, y, FALSE);
9005       TEST_DrawLevelField(x, y);
9006       done = TRUE;
9007     }
9008   }
9009
9010   if (done)
9011     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9012                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9013                             SND_BD_AMOEBA_TURNING_TO_GEM));
9014 }
9015
9016 static void AmoebaGrowing(int x, int y)
9017 {
9018   static unsigned int sound_delay = 0;
9019   static unsigned int sound_delay_value = 0;
9020
9021   if (!MovDelay[x][y])          // start new growing cycle
9022   {
9023     MovDelay[x][y] = 7;
9024
9025     if (DelayReached(&sound_delay, sound_delay_value))
9026     {
9027       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9028       sound_delay_value = 30;
9029     }
9030   }
9031
9032   if (MovDelay[x][y])           // wait some time before growing bigger
9033   {
9034     MovDelay[x][y]--;
9035     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9036     {
9037       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9038                                            6 - MovDelay[x][y]);
9039
9040       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9041     }
9042
9043     if (!MovDelay[x][y])
9044     {
9045       Tile[x][y] = Store[x][y];
9046       Store[x][y] = 0;
9047       TEST_DrawLevelField(x, y);
9048     }
9049   }
9050 }
9051
9052 static void AmoebaShrinking(int x, int y)
9053 {
9054   static unsigned int sound_delay = 0;
9055   static unsigned int sound_delay_value = 0;
9056
9057   if (!MovDelay[x][y])          // start new shrinking cycle
9058   {
9059     MovDelay[x][y] = 7;
9060
9061     if (DelayReached(&sound_delay, sound_delay_value))
9062       sound_delay_value = 30;
9063   }
9064
9065   if (MovDelay[x][y])           // wait some time before shrinking
9066   {
9067     MovDelay[x][y]--;
9068     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9069     {
9070       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9071                                            6 - MovDelay[x][y]);
9072
9073       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9074     }
9075
9076     if (!MovDelay[x][y])
9077     {
9078       Tile[x][y] = EL_EMPTY;
9079       TEST_DrawLevelField(x, y);
9080
9081       // don't let mole enter this field in this cycle;
9082       // (give priority to objects falling to this field from above)
9083       Stop[x][y] = TRUE;
9084     }
9085   }
9086 }
9087
9088 static void AmoebaReproduce(int ax, int ay)
9089 {
9090   int i;
9091   int element = Tile[ax][ay];
9092   int graphic = el2img(element);
9093   int newax = ax, neway = ay;
9094   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9095   static int xy[4][2] =
9096   {
9097     { 0, -1 },
9098     { -1, 0 },
9099     { +1, 0 },
9100     { 0, +1 }
9101   };
9102
9103   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9104   {
9105     Tile[ax][ay] = EL_AMOEBA_DEAD;
9106     TEST_DrawLevelField(ax, ay);
9107     return;
9108   }
9109
9110   if (IS_ANIMATED(graphic))
9111     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9112
9113   if (!MovDelay[ax][ay])        // start making new amoeba field
9114     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9115
9116   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9117   {
9118     MovDelay[ax][ay]--;
9119     if (MovDelay[ax][ay])
9120       return;
9121   }
9122
9123   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9124   {
9125     int start = RND(4);
9126     int x = ax + xy[start][0];
9127     int y = ay + xy[start][1];
9128
9129     if (!IN_LEV_FIELD(x, y))
9130       return;
9131
9132     if (IS_FREE(x, y) ||
9133         CAN_GROW_INTO(Tile[x][y]) ||
9134         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9135         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9136     {
9137       newax = x;
9138       neway = y;
9139     }
9140
9141     if (newax == ax && neway == ay)
9142       return;
9143   }
9144   else                          // normal or "filled" (BD style) amoeba
9145   {
9146     int start = RND(4);
9147     boolean waiting_for_player = FALSE;
9148
9149     for (i = 0; i < NUM_DIRECTIONS; i++)
9150     {
9151       int j = (start + i) % 4;
9152       int x = ax + xy[j][0];
9153       int y = ay + xy[j][1];
9154
9155       if (!IN_LEV_FIELD(x, y))
9156         continue;
9157
9158       if (IS_FREE(x, y) ||
9159           CAN_GROW_INTO(Tile[x][y]) ||
9160           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9161           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9162       {
9163         newax = x;
9164         neway = y;
9165         break;
9166       }
9167       else if (IS_PLAYER(x, y))
9168         waiting_for_player = TRUE;
9169     }
9170
9171     if (newax == ax && neway == ay)             // amoeba cannot grow
9172     {
9173       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9174       {
9175         Tile[ax][ay] = EL_AMOEBA_DEAD;
9176         TEST_DrawLevelField(ax, ay);
9177         AmoebaCnt[AmoebaNr[ax][ay]]--;
9178
9179         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9180         {
9181           if (element == EL_AMOEBA_FULL)
9182             AmoebaToDiamond(ax, ay);
9183           else if (element == EL_BD_AMOEBA)
9184             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9185         }
9186       }
9187       return;
9188     }
9189     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9190     {
9191       // amoeba gets larger by growing in some direction
9192
9193       int new_group_nr = AmoebaNr[ax][ay];
9194
9195 #ifdef DEBUG
9196   if (new_group_nr == 0)
9197   {
9198     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9199           newax, neway);
9200     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9201
9202     return;
9203   }
9204 #endif
9205
9206       AmoebaNr[newax][neway] = new_group_nr;
9207       AmoebaCnt[new_group_nr]++;
9208       AmoebaCnt2[new_group_nr]++;
9209
9210       // if amoeba touches other amoeba(s) after growing, unify them
9211       AmoebaMerge(newax, neway);
9212
9213       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9214       {
9215         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9216         return;
9217       }
9218     }
9219   }
9220
9221   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9222       (neway == lev_fieldy - 1 && newax != ax))
9223   {
9224     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9225     Store[newax][neway] = element;
9226   }
9227   else if (neway == ay || element == EL_EMC_DRIPPER)
9228   {
9229     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9230
9231     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9232   }
9233   else
9234   {
9235     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9236     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9237     Store[ax][ay] = EL_AMOEBA_DROP;
9238     ContinueMoving(ax, ay);
9239     return;
9240   }
9241
9242   TEST_DrawLevelField(newax, neway);
9243 }
9244
9245 static void Life(int ax, int ay)
9246 {
9247   int x1, y1, x2, y2;
9248   int life_time = 40;
9249   int element = Tile[ax][ay];
9250   int graphic = el2img(element);
9251   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9252                          level.biomaze);
9253   boolean changed = FALSE;
9254
9255   if (IS_ANIMATED(graphic))
9256     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9257
9258   if (Stop[ax][ay])
9259     return;
9260
9261   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9262     MovDelay[ax][ay] = life_time;
9263
9264   if (MovDelay[ax][ay])         // wait some time before next cycle
9265   {
9266     MovDelay[ax][ay]--;
9267     if (MovDelay[ax][ay])
9268       return;
9269   }
9270
9271   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9272   {
9273     int xx = ax+x1, yy = ay+y1;
9274     int old_element = Tile[xx][yy];
9275     int num_neighbours = 0;
9276
9277     if (!IN_LEV_FIELD(xx, yy))
9278       continue;
9279
9280     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9281     {
9282       int x = xx+x2, y = yy+y2;
9283
9284       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9285         continue;
9286
9287       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9288       boolean is_neighbour = FALSE;
9289
9290       if (level.use_life_bugs)
9291         is_neighbour =
9292           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9293            (IS_FREE(x, y)                             &&  Stop[x][y]));
9294       else
9295         is_neighbour =
9296           (Last[x][y] == element || is_player_cell);
9297
9298       if (is_neighbour)
9299         num_neighbours++;
9300     }
9301
9302     boolean is_free = FALSE;
9303
9304     if (level.use_life_bugs)
9305       is_free = (IS_FREE(xx, yy));
9306     else
9307       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9308
9309     if (xx == ax && yy == ay)           // field in the middle
9310     {
9311       if (num_neighbours < life_parameter[0] ||
9312           num_neighbours > life_parameter[1])
9313       {
9314         Tile[xx][yy] = EL_EMPTY;
9315         if (Tile[xx][yy] != old_element)
9316           TEST_DrawLevelField(xx, yy);
9317         Stop[xx][yy] = TRUE;
9318         changed = TRUE;
9319       }
9320     }
9321     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9322     {                                   // free border field
9323       if (num_neighbours >= life_parameter[2] &&
9324           num_neighbours <= life_parameter[3])
9325       {
9326         Tile[xx][yy] = element;
9327         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9328         if (Tile[xx][yy] != old_element)
9329           TEST_DrawLevelField(xx, yy);
9330         Stop[xx][yy] = TRUE;
9331         changed = TRUE;
9332       }
9333     }
9334   }
9335
9336   if (changed)
9337     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9338                    SND_GAME_OF_LIFE_GROWING);
9339 }
9340
9341 static void InitRobotWheel(int x, int y)
9342 {
9343   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9344 }
9345
9346 static void RunRobotWheel(int x, int y)
9347 {
9348   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9349 }
9350
9351 static void StopRobotWheel(int x, int y)
9352 {
9353   if (game.robot_wheel_x == x &&
9354       game.robot_wheel_y == y)
9355   {
9356     game.robot_wheel_x = -1;
9357     game.robot_wheel_y = -1;
9358     game.robot_wheel_active = FALSE;
9359   }
9360 }
9361
9362 static void InitTimegateWheel(int x, int y)
9363 {
9364   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9365 }
9366
9367 static void RunTimegateWheel(int x, int y)
9368 {
9369   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9370 }
9371
9372 static void InitMagicBallDelay(int x, int y)
9373 {
9374   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9375 }
9376
9377 static void ActivateMagicBall(int bx, int by)
9378 {
9379   int x, y;
9380
9381   if (level.ball_random)
9382   {
9383     int pos_border = RND(8);    // select one of the eight border elements
9384     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9385     int xx = pos_content % 3;
9386     int yy = pos_content / 3;
9387
9388     x = bx - 1 + xx;
9389     y = by - 1 + yy;
9390
9391     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9392       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9393   }
9394   else
9395   {
9396     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9397     {
9398       int xx = x - bx + 1;
9399       int yy = y - by + 1;
9400
9401       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9402         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9403     }
9404   }
9405
9406   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9407 }
9408
9409 static void CheckExit(int x, int y)
9410 {
9411   if (game.gems_still_needed > 0 ||
9412       game.sokoban_fields_still_needed > 0 ||
9413       game.sokoban_objects_still_needed > 0 ||
9414       game.lights_still_needed > 0)
9415   {
9416     int element = Tile[x][y];
9417     int graphic = el2img(element);
9418
9419     if (IS_ANIMATED(graphic))
9420       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9421
9422     return;
9423   }
9424
9425   // do not re-open exit door closed after last player
9426   if (game.all_players_gone)
9427     return;
9428
9429   Tile[x][y] = EL_EXIT_OPENING;
9430
9431   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9432 }
9433
9434 static void CheckExitEM(int x, int y)
9435 {
9436   if (game.gems_still_needed > 0 ||
9437       game.sokoban_fields_still_needed > 0 ||
9438       game.sokoban_objects_still_needed > 0 ||
9439       game.lights_still_needed > 0)
9440   {
9441     int element = Tile[x][y];
9442     int graphic = el2img(element);
9443
9444     if (IS_ANIMATED(graphic))
9445       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9446
9447     return;
9448   }
9449
9450   // do not re-open exit door closed after last player
9451   if (game.all_players_gone)
9452     return;
9453
9454   Tile[x][y] = EL_EM_EXIT_OPENING;
9455
9456   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9457 }
9458
9459 static void CheckExitSteel(int x, int y)
9460 {
9461   if (game.gems_still_needed > 0 ||
9462       game.sokoban_fields_still_needed > 0 ||
9463       game.sokoban_objects_still_needed > 0 ||
9464       game.lights_still_needed > 0)
9465   {
9466     int element = Tile[x][y];
9467     int graphic = el2img(element);
9468
9469     if (IS_ANIMATED(graphic))
9470       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9471
9472     return;
9473   }
9474
9475   // do not re-open exit door closed after last player
9476   if (game.all_players_gone)
9477     return;
9478
9479   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9480
9481   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9482 }
9483
9484 static void CheckExitSteelEM(int x, int y)
9485 {
9486   if (game.gems_still_needed > 0 ||
9487       game.sokoban_fields_still_needed > 0 ||
9488       game.sokoban_objects_still_needed > 0 ||
9489       game.lights_still_needed > 0)
9490   {
9491     int element = Tile[x][y];
9492     int graphic = el2img(element);
9493
9494     if (IS_ANIMATED(graphic))
9495       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9496
9497     return;
9498   }
9499
9500   // do not re-open exit door closed after last player
9501   if (game.all_players_gone)
9502     return;
9503
9504   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9505
9506   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9507 }
9508
9509 static void CheckExitSP(int x, int y)
9510 {
9511   if (game.gems_still_needed > 0)
9512   {
9513     int element = Tile[x][y];
9514     int graphic = el2img(element);
9515
9516     if (IS_ANIMATED(graphic))
9517       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9518
9519     return;
9520   }
9521
9522   // do not re-open exit door closed after last player
9523   if (game.all_players_gone)
9524     return;
9525
9526   Tile[x][y] = EL_SP_EXIT_OPENING;
9527
9528   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9529 }
9530
9531 static void CloseAllOpenTimegates(void)
9532 {
9533   int x, y;
9534
9535   SCAN_PLAYFIELD(x, y)
9536   {
9537     int element = Tile[x][y];
9538
9539     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9540     {
9541       Tile[x][y] = EL_TIMEGATE_CLOSING;
9542
9543       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9544     }
9545   }
9546 }
9547
9548 static void DrawTwinkleOnField(int x, int y)
9549 {
9550   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9551     return;
9552
9553   if (Tile[x][y] == EL_BD_DIAMOND)
9554     return;
9555
9556   if (MovDelay[x][y] == 0)      // next animation frame
9557     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9558
9559   if (MovDelay[x][y] != 0)      // wait some time before next frame
9560   {
9561     MovDelay[x][y]--;
9562
9563     DrawLevelElementAnimation(x, y, Tile[x][y]);
9564
9565     if (MovDelay[x][y] != 0)
9566     {
9567       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9568                                            10 - MovDelay[x][y]);
9569
9570       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9571     }
9572   }
9573 }
9574
9575 static void MauerWaechst(int x, int y)
9576 {
9577   int delay = 6;
9578
9579   if (!MovDelay[x][y])          // next animation frame
9580     MovDelay[x][y] = 3 * delay;
9581
9582   if (MovDelay[x][y])           // wait some time before next frame
9583   {
9584     MovDelay[x][y]--;
9585
9586     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9587     {
9588       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9589       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9590
9591       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9592     }
9593
9594     if (!MovDelay[x][y])
9595     {
9596       if (MovDir[x][y] == MV_LEFT)
9597       {
9598         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9599           TEST_DrawLevelField(x - 1, y);
9600       }
9601       else if (MovDir[x][y] == MV_RIGHT)
9602       {
9603         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9604           TEST_DrawLevelField(x + 1, y);
9605       }
9606       else if (MovDir[x][y] == MV_UP)
9607       {
9608         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9609           TEST_DrawLevelField(x, y - 1);
9610       }
9611       else
9612       {
9613         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9614           TEST_DrawLevelField(x, y + 1);
9615       }
9616
9617       Tile[x][y] = Store[x][y];
9618       Store[x][y] = 0;
9619       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9620       TEST_DrawLevelField(x, y);
9621     }
9622   }
9623 }
9624
9625 static void MauerAbleger(int ax, int ay)
9626 {
9627   int element = Tile[ax][ay];
9628   int graphic = el2img(element);
9629   boolean oben_frei = FALSE, unten_frei = FALSE;
9630   boolean links_frei = FALSE, rechts_frei = FALSE;
9631   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9632   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9633   boolean new_wall = FALSE;
9634
9635   if (IS_ANIMATED(graphic))
9636     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9637
9638   if (!MovDelay[ax][ay])        // start building new wall
9639     MovDelay[ax][ay] = 6;
9640
9641   if (MovDelay[ax][ay])         // wait some time before building new wall
9642   {
9643     MovDelay[ax][ay]--;
9644     if (MovDelay[ax][ay])
9645       return;
9646   }
9647
9648   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9649     oben_frei = TRUE;
9650   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9651     unten_frei = TRUE;
9652   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9653     links_frei = TRUE;
9654   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9655     rechts_frei = TRUE;
9656
9657   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9658       element == EL_EXPANDABLE_WALL_ANY)
9659   {
9660     if (oben_frei)
9661     {
9662       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9663       Store[ax][ay-1] = element;
9664       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9665       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9666         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9667                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9668       new_wall = TRUE;
9669     }
9670     if (unten_frei)
9671     {
9672       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9673       Store[ax][ay+1] = element;
9674       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9675       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9676         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9677                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9678       new_wall = TRUE;
9679     }
9680   }
9681
9682   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9683       element == EL_EXPANDABLE_WALL_ANY ||
9684       element == EL_EXPANDABLE_WALL ||
9685       element == EL_BD_EXPANDABLE_WALL)
9686   {
9687     if (links_frei)
9688     {
9689       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9690       Store[ax-1][ay] = element;
9691       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9692       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9693         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9694                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9695       new_wall = TRUE;
9696     }
9697
9698     if (rechts_frei)
9699     {
9700       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9701       Store[ax+1][ay] = element;
9702       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9703       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9704         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9705                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9706       new_wall = TRUE;
9707     }
9708   }
9709
9710   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9711     TEST_DrawLevelField(ax, ay);
9712
9713   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9714     oben_massiv = TRUE;
9715   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9716     unten_massiv = TRUE;
9717   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9718     links_massiv = TRUE;
9719   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9720     rechts_massiv = TRUE;
9721
9722   if (((oben_massiv && unten_massiv) ||
9723        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9724        element == EL_EXPANDABLE_WALL) &&
9725       ((links_massiv && rechts_massiv) ||
9726        element == EL_EXPANDABLE_WALL_VERTICAL))
9727     Tile[ax][ay] = EL_WALL;
9728
9729   if (new_wall)
9730     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9731 }
9732
9733 static void MauerAblegerStahl(int ax, int ay)
9734 {
9735   int element = Tile[ax][ay];
9736   int graphic = el2img(element);
9737   boolean oben_frei = FALSE, unten_frei = FALSE;
9738   boolean links_frei = FALSE, rechts_frei = FALSE;
9739   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9740   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9741   boolean new_wall = FALSE;
9742
9743   if (IS_ANIMATED(graphic))
9744     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9745
9746   if (!MovDelay[ax][ay])        // start building new wall
9747     MovDelay[ax][ay] = 6;
9748
9749   if (MovDelay[ax][ay])         // wait some time before building new wall
9750   {
9751     MovDelay[ax][ay]--;
9752     if (MovDelay[ax][ay])
9753       return;
9754   }
9755
9756   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9757     oben_frei = TRUE;
9758   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9759     unten_frei = TRUE;
9760   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9761     links_frei = TRUE;
9762   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9763     rechts_frei = TRUE;
9764
9765   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9766       element == EL_EXPANDABLE_STEELWALL_ANY)
9767   {
9768     if (oben_frei)
9769     {
9770       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9771       Store[ax][ay-1] = element;
9772       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9773       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9774         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9775                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9776       new_wall = TRUE;
9777     }
9778     if (unten_frei)
9779     {
9780       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9781       Store[ax][ay+1] = element;
9782       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9783       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9784         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9785                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9786       new_wall = TRUE;
9787     }
9788   }
9789
9790   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9791       element == EL_EXPANDABLE_STEELWALL_ANY)
9792   {
9793     if (links_frei)
9794     {
9795       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9796       Store[ax-1][ay] = element;
9797       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9798       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9799         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9800                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9801       new_wall = TRUE;
9802     }
9803
9804     if (rechts_frei)
9805     {
9806       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9807       Store[ax+1][ay] = element;
9808       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9809       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9810         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9811                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9812       new_wall = TRUE;
9813     }
9814   }
9815
9816   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9817     oben_massiv = TRUE;
9818   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9819     unten_massiv = TRUE;
9820   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9821     links_massiv = TRUE;
9822   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9823     rechts_massiv = TRUE;
9824
9825   if (((oben_massiv && unten_massiv) ||
9826        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9827       ((links_massiv && rechts_massiv) ||
9828        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9829     Tile[ax][ay] = EL_STEELWALL;
9830
9831   if (new_wall)
9832     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9833 }
9834
9835 static void CheckForDragon(int x, int y)
9836 {
9837   int i, j;
9838   boolean dragon_found = FALSE;
9839   static int xy[4][2] =
9840   {
9841     { 0, -1 },
9842     { -1, 0 },
9843     { +1, 0 },
9844     { 0, +1 }
9845   };
9846
9847   for (i = 0; i < NUM_DIRECTIONS; i++)
9848   {
9849     for (j = 0; j < 4; j++)
9850     {
9851       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9852
9853       if (IN_LEV_FIELD(xx, yy) &&
9854           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9855       {
9856         if (Tile[xx][yy] == EL_DRAGON)
9857           dragon_found = TRUE;
9858       }
9859       else
9860         break;
9861     }
9862   }
9863
9864   if (!dragon_found)
9865   {
9866     for (i = 0; i < NUM_DIRECTIONS; i++)
9867     {
9868       for (j = 0; j < 3; j++)
9869       {
9870         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9871   
9872         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9873         {
9874           Tile[xx][yy] = EL_EMPTY;
9875           TEST_DrawLevelField(xx, yy);
9876         }
9877         else
9878           break;
9879       }
9880     }
9881   }
9882 }
9883
9884 static void InitBuggyBase(int x, int y)
9885 {
9886   int element = Tile[x][y];
9887   int activating_delay = FRAMES_PER_SECOND / 4;
9888
9889   ChangeDelay[x][y] =
9890     (element == EL_SP_BUGGY_BASE ?
9891      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9892      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9893      activating_delay :
9894      element == EL_SP_BUGGY_BASE_ACTIVE ?
9895      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9896 }
9897
9898 static void WarnBuggyBase(int x, int y)
9899 {
9900   int i;
9901   static int xy[4][2] =
9902   {
9903     { 0, -1 },
9904     { -1, 0 },
9905     { +1, 0 },
9906     { 0, +1 }
9907   };
9908
9909   for (i = 0; i < NUM_DIRECTIONS; i++)
9910   {
9911     int xx = x + xy[i][0];
9912     int yy = y + xy[i][1];
9913
9914     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9915     {
9916       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9917
9918       break;
9919     }
9920   }
9921 }
9922
9923 static void InitTrap(int x, int y)
9924 {
9925   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9926 }
9927
9928 static void ActivateTrap(int x, int y)
9929 {
9930   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9931 }
9932
9933 static void ChangeActiveTrap(int x, int y)
9934 {
9935   int graphic = IMG_TRAP_ACTIVE;
9936
9937   // if new animation frame was drawn, correct crumbled sand border
9938   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9939     TEST_DrawLevelFieldCrumbled(x, y);
9940 }
9941
9942 static int getSpecialActionElement(int element, int number, int base_element)
9943 {
9944   return (element != EL_EMPTY ? element :
9945           number != -1 ? base_element + number - 1 :
9946           EL_EMPTY);
9947 }
9948
9949 static int getModifiedActionNumber(int value_old, int operator, int operand,
9950                                    int value_min, int value_max)
9951 {
9952   int value_new = (operator == CA_MODE_SET      ? operand :
9953                    operator == CA_MODE_ADD      ? value_old + operand :
9954                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9955                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9956                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9957                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9958                    value_old);
9959
9960   return (value_new < value_min ? value_min :
9961           value_new > value_max ? value_max :
9962           value_new);
9963 }
9964
9965 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9966 {
9967   struct ElementInfo *ei = &element_info[element];
9968   struct ElementChangeInfo *change = &ei->change_page[page];
9969   int target_element = change->target_element;
9970   int action_type = change->action_type;
9971   int action_mode = change->action_mode;
9972   int action_arg = change->action_arg;
9973   int action_element = change->action_element;
9974   int i;
9975
9976   if (!change->has_action)
9977     return;
9978
9979   // ---------- determine action paramater values -----------------------------
9980
9981   int level_time_value =
9982     (level.time > 0 ? TimeLeft :
9983      TimePlayed);
9984
9985   int action_arg_element_raw =
9986     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9987      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9988      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9989      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9990      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9991      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9992      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9993      EL_EMPTY);
9994   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9995
9996   int action_arg_direction =
9997     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9998      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9999      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10000      change->actual_trigger_side :
10001      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10002      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10003      MV_NONE);
10004
10005   int action_arg_number_min =
10006     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10007      CA_ARG_MIN);
10008
10009   int action_arg_number_max =
10010     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10011      action_type == CA_SET_LEVEL_GEMS ? 999 :
10012      action_type == CA_SET_LEVEL_TIME ? 9999 :
10013      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10014      action_type == CA_SET_CE_VALUE ? 9999 :
10015      action_type == CA_SET_CE_SCORE ? 9999 :
10016      CA_ARG_MAX);
10017
10018   int action_arg_number_reset =
10019     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10020      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10021      action_type == CA_SET_LEVEL_TIME ? level.time :
10022      action_type == CA_SET_LEVEL_SCORE ? 0 :
10023      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10024      action_type == CA_SET_CE_SCORE ? 0 :
10025      0);
10026
10027   int action_arg_number =
10028     (action_arg <= CA_ARG_MAX ? action_arg :
10029      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10030      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10031      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10032      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10033      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10034      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10035      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10036      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10037      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10038      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10039      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10040      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10041      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10042      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10043      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10044      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10045      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10046      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10047      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10048      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10049      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10050      -1);
10051
10052   int action_arg_number_old =
10053     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10054      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10055      action_type == CA_SET_LEVEL_SCORE ? game.score :
10056      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10057      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10058      0);
10059
10060   int action_arg_number_new =
10061     getModifiedActionNumber(action_arg_number_old,
10062                             action_mode, action_arg_number,
10063                             action_arg_number_min, action_arg_number_max);
10064
10065   int trigger_player_bits =
10066     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10067      change->actual_trigger_player_bits : change->trigger_player);
10068
10069   int action_arg_player_bits =
10070     (action_arg >= CA_ARG_PLAYER_1 &&
10071      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10072      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10073      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10074      PLAYER_BITS_ANY);
10075
10076   // ---------- execute action  -----------------------------------------------
10077
10078   switch (action_type)
10079   {
10080     case CA_NO_ACTION:
10081     {
10082       return;
10083     }
10084
10085     // ---------- level actions  ----------------------------------------------
10086
10087     case CA_RESTART_LEVEL:
10088     {
10089       game.restart_level = TRUE;
10090
10091       break;
10092     }
10093
10094     case CA_SHOW_ENVELOPE:
10095     {
10096       int element = getSpecialActionElement(action_arg_element,
10097                                             action_arg_number, EL_ENVELOPE_1);
10098
10099       if (IS_ENVELOPE(element))
10100         local_player->show_envelope = element;
10101
10102       break;
10103     }
10104
10105     case CA_SET_LEVEL_TIME:
10106     {
10107       if (level.time > 0)       // only modify limited time value
10108       {
10109         TimeLeft = action_arg_number_new;
10110
10111         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10112
10113         DisplayGameControlValues();
10114
10115         if (!TimeLeft && setup.time_limit)
10116           for (i = 0; i < MAX_PLAYERS; i++)
10117             KillPlayer(&stored_player[i]);
10118       }
10119
10120       break;
10121     }
10122
10123     case CA_SET_LEVEL_SCORE:
10124     {
10125       game.score = action_arg_number_new;
10126
10127       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10128
10129       DisplayGameControlValues();
10130
10131       break;
10132     }
10133
10134     case CA_SET_LEVEL_GEMS:
10135     {
10136       game.gems_still_needed = action_arg_number_new;
10137
10138       game.snapshot.collected_item = TRUE;
10139
10140       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10141
10142       DisplayGameControlValues();
10143
10144       break;
10145     }
10146
10147     case CA_SET_LEVEL_WIND:
10148     {
10149       game.wind_direction = action_arg_direction;
10150
10151       break;
10152     }
10153
10154     case CA_SET_LEVEL_RANDOM_SEED:
10155     {
10156       // ensure that setting a new random seed while playing is predictable
10157       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10158
10159       break;
10160     }
10161
10162     // ---------- player actions  ---------------------------------------------
10163
10164     case CA_MOVE_PLAYER:
10165     case CA_MOVE_PLAYER_NEW:
10166     {
10167       // automatically move to the next field in specified direction
10168       for (i = 0; i < MAX_PLAYERS; i++)
10169         if (trigger_player_bits & (1 << i))
10170           if (action_type == CA_MOVE_PLAYER ||
10171               stored_player[i].MovPos == 0)
10172             stored_player[i].programmed_action = action_arg_direction;
10173
10174       break;
10175     }
10176
10177     case CA_EXIT_PLAYER:
10178     {
10179       for (i = 0; i < MAX_PLAYERS; i++)
10180         if (action_arg_player_bits & (1 << i))
10181           ExitPlayer(&stored_player[i]);
10182
10183       if (game.players_still_needed == 0)
10184         LevelSolved();
10185
10186       break;
10187     }
10188
10189     case CA_KILL_PLAYER:
10190     {
10191       for (i = 0; i < MAX_PLAYERS; i++)
10192         if (action_arg_player_bits & (1 << i))
10193           KillPlayer(&stored_player[i]);
10194
10195       break;
10196     }
10197
10198     case CA_SET_PLAYER_KEYS:
10199     {
10200       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10201       int element = getSpecialActionElement(action_arg_element,
10202                                             action_arg_number, EL_KEY_1);
10203
10204       if (IS_KEY(element))
10205       {
10206         for (i = 0; i < MAX_PLAYERS; i++)
10207         {
10208           if (trigger_player_bits & (1 << i))
10209           {
10210             stored_player[i].key[KEY_NR(element)] = key_state;
10211
10212             DrawGameDoorValues();
10213           }
10214         }
10215       }
10216
10217       break;
10218     }
10219
10220     case CA_SET_PLAYER_SPEED:
10221     {
10222       for (i = 0; i < MAX_PLAYERS; i++)
10223       {
10224         if (trigger_player_bits & (1 << i))
10225         {
10226           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10227
10228           if (action_arg == CA_ARG_SPEED_FASTER &&
10229               stored_player[i].cannot_move)
10230           {
10231             action_arg_number = STEPSIZE_VERY_SLOW;
10232           }
10233           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10234                    action_arg == CA_ARG_SPEED_FASTER)
10235           {
10236             action_arg_number = 2;
10237             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10238                            CA_MODE_MULTIPLY);
10239           }
10240           else if (action_arg == CA_ARG_NUMBER_RESET)
10241           {
10242             action_arg_number = level.initial_player_stepsize[i];
10243           }
10244
10245           move_stepsize =
10246             getModifiedActionNumber(move_stepsize,
10247                                     action_mode,
10248                                     action_arg_number,
10249                                     action_arg_number_min,
10250                                     action_arg_number_max);
10251
10252           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10253         }
10254       }
10255
10256       break;
10257     }
10258
10259     case CA_SET_PLAYER_SHIELD:
10260     {
10261       for (i = 0; i < MAX_PLAYERS; i++)
10262       {
10263         if (trigger_player_bits & (1 << i))
10264         {
10265           if (action_arg == CA_ARG_SHIELD_OFF)
10266           {
10267             stored_player[i].shield_normal_time_left = 0;
10268             stored_player[i].shield_deadly_time_left = 0;
10269           }
10270           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10271           {
10272             stored_player[i].shield_normal_time_left = 999999;
10273           }
10274           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10275           {
10276             stored_player[i].shield_normal_time_left = 999999;
10277             stored_player[i].shield_deadly_time_left = 999999;
10278           }
10279         }
10280       }
10281
10282       break;
10283     }
10284
10285     case CA_SET_PLAYER_GRAVITY:
10286     {
10287       for (i = 0; i < MAX_PLAYERS; i++)
10288       {
10289         if (trigger_player_bits & (1 << i))
10290         {
10291           stored_player[i].gravity =
10292             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10293              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10294              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10295              stored_player[i].gravity);
10296         }
10297       }
10298
10299       break;
10300     }
10301
10302     case CA_SET_PLAYER_ARTWORK:
10303     {
10304       for (i = 0; i < MAX_PLAYERS; i++)
10305       {
10306         if (trigger_player_bits & (1 << i))
10307         {
10308           int artwork_element = action_arg_element;
10309
10310           if (action_arg == CA_ARG_ELEMENT_RESET)
10311             artwork_element =
10312               (level.use_artwork_element[i] ? level.artwork_element[i] :
10313                stored_player[i].element_nr);
10314
10315           if (stored_player[i].artwork_element != artwork_element)
10316             stored_player[i].Frame = 0;
10317
10318           stored_player[i].artwork_element = artwork_element;
10319
10320           SetPlayerWaiting(&stored_player[i], FALSE);
10321
10322           // set number of special actions for bored and sleeping animation
10323           stored_player[i].num_special_action_bored =
10324             get_num_special_action(artwork_element,
10325                                    ACTION_BORING_1, ACTION_BORING_LAST);
10326           stored_player[i].num_special_action_sleeping =
10327             get_num_special_action(artwork_element,
10328                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10329         }
10330       }
10331
10332       break;
10333     }
10334
10335     case CA_SET_PLAYER_INVENTORY:
10336     {
10337       for (i = 0; i < MAX_PLAYERS; i++)
10338       {
10339         struct PlayerInfo *player = &stored_player[i];
10340         int j, k;
10341
10342         if (trigger_player_bits & (1 << i))
10343         {
10344           int inventory_element = action_arg_element;
10345
10346           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10347               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10348               action_arg == CA_ARG_ELEMENT_ACTION)
10349           {
10350             int element = inventory_element;
10351             int collect_count = element_info[element].collect_count_initial;
10352
10353             if (!IS_CUSTOM_ELEMENT(element))
10354               collect_count = 1;
10355
10356             if (collect_count == 0)
10357               player->inventory_infinite_element = element;
10358             else
10359               for (k = 0; k < collect_count; k++)
10360                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10361                   player->inventory_element[player->inventory_size++] =
10362                     element;
10363           }
10364           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10365                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10366                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10367           {
10368             if (player->inventory_infinite_element != EL_UNDEFINED &&
10369                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10370                                      action_arg_element_raw))
10371               player->inventory_infinite_element = EL_UNDEFINED;
10372
10373             for (k = 0, j = 0; j < player->inventory_size; j++)
10374             {
10375               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10376                                         action_arg_element_raw))
10377                 player->inventory_element[k++] = player->inventory_element[j];
10378             }
10379
10380             player->inventory_size = k;
10381           }
10382           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10383           {
10384             if (player->inventory_size > 0)
10385             {
10386               for (j = 0; j < player->inventory_size - 1; j++)
10387                 player->inventory_element[j] = player->inventory_element[j + 1];
10388
10389               player->inventory_size--;
10390             }
10391           }
10392           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10393           {
10394             if (player->inventory_size > 0)
10395               player->inventory_size--;
10396           }
10397           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10398           {
10399             player->inventory_infinite_element = EL_UNDEFINED;
10400             player->inventory_size = 0;
10401           }
10402           else if (action_arg == CA_ARG_INVENTORY_RESET)
10403           {
10404             player->inventory_infinite_element = EL_UNDEFINED;
10405             player->inventory_size = 0;
10406
10407             if (level.use_initial_inventory[i])
10408             {
10409               for (j = 0; j < level.initial_inventory_size[i]; j++)
10410               {
10411                 int element = level.initial_inventory_content[i][j];
10412                 int collect_count = element_info[element].collect_count_initial;
10413
10414                 if (!IS_CUSTOM_ELEMENT(element))
10415                   collect_count = 1;
10416
10417                 if (collect_count == 0)
10418                   player->inventory_infinite_element = element;
10419                 else
10420                   for (k = 0; k < collect_count; k++)
10421                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10422                       player->inventory_element[player->inventory_size++] =
10423                         element;
10424               }
10425             }
10426           }
10427         }
10428       }
10429
10430       break;
10431     }
10432
10433     // ---------- CE actions  -------------------------------------------------
10434
10435     case CA_SET_CE_VALUE:
10436     {
10437       int last_ce_value = CustomValue[x][y];
10438
10439       CustomValue[x][y] = action_arg_number_new;
10440
10441       if (CustomValue[x][y] != last_ce_value)
10442       {
10443         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10444         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10445
10446         if (CustomValue[x][y] == 0)
10447         {
10448           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10449           ChangeCount[x][y] = 0;        // allow at least one more change
10450
10451           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10452           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10453         }
10454       }
10455
10456       break;
10457     }
10458
10459     case CA_SET_CE_SCORE:
10460     {
10461       int last_ce_score = ei->collect_score;
10462
10463       ei->collect_score = action_arg_number_new;
10464
10465       if (ei->collect_score != last_ce_score)
10466       {
10467         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10468         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10469
10470         if (ei->collect_score == 0)
10471         {
10472           int xx, yy;
10473
10474           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10475           ChangeCount[x][y] = 0;        // allow at least one more change
10476
10477           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10478           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10479
10480           /*
10481             This is a very special case that seems to be a mixture between
10482             CheckElementChange() and CheckTriggeredElementChange(): while
10483             the first one only affects single elements that are triggered
10484             directly, the second one affects multiple elements in the playfield
10485             that are triggered indirectly by another element. This is a third
10486             case: Changing the CE score always affects multiple identical CEs,
10487             so every affected CE must be checked, not only the single CE for
10488             which the CE score was changed in the first place (as every instance
10489             of that CE shares the same CE score, and therefore also can change)!
10490           */
10491           SCAN_PLAYFIELD(xx, yy)
10492           {
10493             if (Tile[xx][yy] == element)
10494               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10495                                  CE_SCORE_GETS_ZERO);
10496           }
10497         }
10498       }
10499
10500       break;
10501     }
10502
10503     case CA_SET_CE_ARTWORK:
10504     {
10505       int artwork_element = action_arg_element;
10506       boolean reset_frame = FALSE;
10507       int xx, yy;
10508
10509       if (action_arg == CA_ARG_ELEMENT_RESET)
10510         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10511                            element);
10512
10513       if (ei->gfx_element != artwork_element)
10514         reset_frame = TRUE;
10515
10516       ei->gfx_element = artwork_element;
10517
10518       SCAN_PLAYFIELD(xx, yy)
10519       {
10520         if (Tile[xx][yy] == element)
10521         {
10522           if (reset_frame)
10523           {
10524             ResetGfxAnimation(xx, yy);
10525             ResetRandomAnimationValue(xx, yy);
10526           }
10527
10528           TEST_DrawLevelField(xx, yy);
10529         }
10530       }
10531
10532       break;
10533     }
10534
10535     // ---------- engine actions  ---------------------------------------------
10536
10537     case CA_SET_ENGINE_SCAN_MODE:
10538     {
10539       InitPlayfieldScanMode(action_arg);
10540
10541       break;
10542     }
10543
10544     default:
10545       break;
10546   }
10547 }
10548
10549 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10550 {
10551   int old_element = Tile[x][y];
10552   int new_element = GetElementFromGroupElement(element);
10553   int previous_move_direction = MovDir[x][y];
10554   int last_ce_value = CustomValue[x][y];
10555   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10556   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10557   boolean add_player_onto_element = (new_element_is_player &&
10558                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10559                                      IS_WALKABLE(old_element));
10560
10561   if (!add_player_onto_element)
10562   {
10563     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10564       RemoveMovingField(x, y);
10565     else
10566       RemoveField(x, y);
10567
10568     Tile[x][y] = new_element;
10569
10570     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10571       MovDir[x][y] = previous_move_direction;
10572
10573     if (element_info[new_element].use_last_ce_value)
10574       CustomValue[x][y] = last_ce_value;
10575
10576     InitField_WithBug1(x, y, FALSE);
10577
10578     new_element = Tile[x][y];   // element may have changed
10579
10580     ResetGfxAnimation(x, y);
10581     ResetRandomAnimationValue(x, y);
10582
10583     TEST_DrawLevelField(x, y);
10584
10585     if (GFX_CRUMBLED(new_element))
10586       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10587   }
10588
10589   // check if element under the player changes from accessible to unaccessible
10590   // (needed for special case of dropping element which then changes)
10591   // (must be checked after creating new element for walkable group elements)
10592   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10593       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10594   {
10595     Bang(x, y);
10596
10597     return;
10598   }
10599
10600   // "ChangeCount" not set yet to allow "entered by player" change one time
10601   if (new_element_is_player)
10602     RelocatePlayer(x, y, new_element);
10603
10604   if (is_change)
10605     ChangeCount[x][y]++;        // count number of changes in the same frame
10606
10607   TestIfBadThingTouchesPlayer(x, y);
10608   TestIfPlayerTouchesCustomElement(x, y);
10609   TestIfElementTouchesCustomElement(x, y);
10610 }
10611
10612 static void CreateField(int x, int y, int element)
10613 {
10614   CreateFieldExt(x, y, element, FALSE);
10615 }
10616
10617 static void CreateElementFromChange(int x, int y, int element)
10618 {
10619   element = GET_VALID_RUNTIME_ELEMENT(element);
10620
10621   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10622   {
10623     int old_element = Tile[x][y];
10624
10625     // prevent changed element from moving in same engine frame
10626     // unless both old and new element can either fall or move
10627     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10628         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10629       Stop[x][y] = TRUE;
10630   }
10631
10632   CreateFieldExt(x, y, element, TRUE);
10633 }
10634
10635 static boolean ChangeElement(int x, int y, int element, int page)
10636 {
10637   struct ElementInfo *ei = &element_info[element];
10638   struct ElementChangeInfo *change = &ei->change_page[page];
10639   int ce_value = CustomValue[x][y];
10640   int ce_score = ei->collect_score;
10641   int target_element;
10642   int old_element = Tile[x][y];
10643
10644   // always use default change event to prevent running into a loop
10645   if (ChangeEvent[x][y] == -1)
10646     ChangeEvent[x][y] = CE_DELAY;
10647
10648   if (ChangeEvent[x][y] == CE_DELAY)
10649   {
10650     // reset actual trigger element, trigger player and action element
10651     change->actual_trigger_element = EL_EMPTY;
10652     change->actual_trigger_player = EL_EMPTY;
10653     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10654     change->actual_trigger_side = CH_SIDE_NONE;
10655     change->actual_trigger_ce_value = 0;
10656     change->actual_trigger_ce_score = 0;
10657   }
10658
10659   // do not change elements more than a specified maximum number of changes
10660   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10661     return FALSE;
10662
10663   ChangeCount[x][y]++;          // count number of changes in the same frame
10664
10665   if (change->explode)
10666   {
10667     Bang(x, y);
10668
10669     return TRUE;
10670   }
10671
10672   if (change->use_target_content)
10673   {
10674     boolean complete_replace = TRUE;
10675     boolean can_replace[3][3];
10676     int xx, yy;
10677
10678     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10679     {
10680       boolean is_empty;
10681       boolean is_walkable;
10682       boolean is_diggable;
10683       boolean is_collectible;
10684       boolean is_removable;
10685       boolean is_destructible;
10686       int ex = x + xx - 1;
10687       int ey = y + yy - 1;
10688       int content_element = change->target_content.e[xx][yy];
10689       int e;
10690
10691       can_replace[xx][yy] = TRUE;
10692
10693       if (ex == x && ey == y)   // do not check changing element itself
10694         continue;
10695
10696       if (content_element == EL_EMPTY_SPACE)
10697       {
10698         can_replace[xx][yy] = FALSE;    // do not replace border with space
10699
10700         continue;
10701       }
10702
10703       if (!IN_LEV_FIELD(ex, ey))
10704       {
10705         can_replace[xx][yy] = FALSE;
10706         complete_replace = FALSE;
10707
10708         continue;
10709       }
10710
10711       e = Tile[ex][ey];
10712
10713       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10714         e = MovingOrBlocked2Element(ex, ey);
10715
10716       is_empty = (IS_FREE(ex, ey) ||
10717                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10718
10719       is_walkable     = (is_empty || IS_WALKABLE(e));
10720       is_diggable     = (is_empty || IS_DIGGABLE(e));
10721       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10722       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10723       is_removable    = (is_diggable || is_collectible);
10724
10725       can_replace[xx][yy] =
10726         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10727           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10728           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10729           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10730           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10731           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10732          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10733
10734       if (!can_replace[xx][yy])
10735         complete_replace = FALSE;
10736     }
10737
10738     if (!change->only_if_complete || complete_replace)
10739     {
10740       boolean something_has_changed = FALSE;
10741
10742       if (change->only_if_complete && change->use_random_replace &&
10743           RND(100) < change->random_percentage)
10744         return FALSE;
10745
10746       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10747       {
10748         int ex = x + xx - 1;
10749         int ey = y + yy - 1;
10750         int content_element;
10751
10752         if (can_replace[xx][yy] && (!change->use_random_replace ||
10753                                     RND(100) < change->random_percentage))
10754         {
10755           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10756             RemoveMovingField(ex, ey);
10757
10758           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10759
10760           content_element = change->target_content.e[xx][yy];
10761           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10762                                               ce_value, ce_score);
10763
10764           CreateElementFromChange(ex, ey, target_element);
10765
10766           something_has_changed = TRUE;
10767
10768           // for symmetry reasons, freeze newly created border elements
10769           if (ex != x || ey != y)
10770             Stop[ex][ey] = TRUE;        // no more moving in this frame
10771         }
10772       }
10773
10774       if (something_has_changed)
10775       {
10776         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10777         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10778       }
10779     }
10780   }
10781   else
10782   {
10783     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10784                                         ce_value, ce_score);
10785
10786     if (element == EL_DIAGONAL_GROWING ||
10787         element == EL_DIAGONAL_SHRINKING)
10788     {
10789       target_element = Store[x][y];
10790
10791       Store[x][y] = EL_EMPTY;
10792     }
10793
10794     CreateElementFromChange(x, y, target_element);
10795
10796     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10797     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10798   }
10799
10800   // this uses direct change before indirect change
10801   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10802
10803   return TRUE;
10804 }
10805
10806 static void HandleElementChange(int x, int y, int page)
10807 {
10808   int element = MovingOrBlocked2Element(x, y);
10809   struct ElementInfo *ei = &element_info[element];
10810   struct ElementChangeInfo *change = &ei->change_page[page];
10811   boolean handle_action_before_change = FALSE;
10812
10813 #ifdef DEBUG
10814   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10815       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10816   {
10817     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10818           x, y, element, element_info[element].token_name);
10819     Debug("game:playing:HandleElementChange", "This should never happen!");
10820   }
10821 #endif
10822
10823   // this can happen with classic bombs on walkable, changing elements
10824   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10825   {
10826     return;
10827   }
10828
10829   if (ChangeDelay[x][y] == 0)           // initialize element change
10830   {
10831     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10832
10833     if (change->can_change)
10834     {
10835       // !!! not clear why graphic animation should be reset at all here !!!
10836       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10837       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10838
10839       /*
10840         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10841
10842         When using an animation frame delay of 1 (this only happens with
10843         "sp_zonk.moving.left/right" in the classic graphics), the default
10844         (non-moving) animation shows wrong animation frames (while the
10845         moving animation, like "sp_zonk.moving.left/right", is correct,
10846         so this graphical bug never shows up with the classic graphics).
10847         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10848         be drawn instead of the correct frames 0,1,2,3. This is caused by
10849         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10850         an element change: First when the change delay ("ChangeDelay[][]")
10851         counter has reached zero after decrementing, then a second time in
10852         the next frame (after "GfxFrame[][]" was already incremented) when
10853         "ChangeDelay[][]" is reset to the initial delay value again.
10854
10855         This causes frame 0 to be drawn twice, while the last frame won't
10856         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10857
10858         As some animations may already be cleverly designed around this bug
10859         (at least the "Snake Bite" snake tail animation does this), it cannot
10860         simply be fixed here without breaking such existing animations.
10861         Unfortunately, it cannot easily be detected if a graphics set was
10862         designed "before" or "after" the bug was fixed. As a workaround,
10863         a new graphics set option "game.graphics_engine_version" was added
10864         to be able to specify the game's major release version for which the
10865         graphics set was designed, which can then be used to decide if the
10866         bugfix should be used (version 4 and above) or not (version 3 or
10867         below, or if no version was specified at all, as with old sets).
10868
10869         (The wrong/fixed animation frames can be tested with the test level set
10870         "test_gfxframe" and level "000", which contains a specially prepared
10871         custom element at level position (x/y) == (11/9) which uses the zonk
10872         animation mentioned above. Using "game.graphics_engine_version: 4"
10873         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10874         This can also be seen from the debug output for this test element.)
10875       */
10876
10877       // when a custom element is about to change (for example by change delay),
10878       // do not reset graphic animation when the custom element is moving
10879       if (game.graphics_engine_version < 4 &&
10880           !IS_MOVING(x, y))
10881       {
10882         ResetGfxAnimation(x, y);
10883         ResetRandomAnimationValue(x, y);
10884       }
10885
10886       if (change->pre_change_function)
10887         change->pre_change_function(x, y);
10888     }
10889   }
10890
10891   ChangeDelay[x][y]--;
10892
10893   if (ChangeDelay[x][y] != 0)           // continue element change
10894   {
10895     if (change->can_change)
10896     {
10897       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10898
10899       if (IS_ANIMATED(graphic))
10900         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10901
10902       if (change->change_function)
10903         change->change_function(x, y);
10904     }
10905   }
10906   else                                  // finish element change
10907   {
10908     if (ChangePage[x][y] != -1)         // remember page from delayed change
10909     {
10910       page = ChangePage[x][y];
10911       ChangePage[x][y] = -1;
10912
10913       change = &ei->change_page[page];
10914     }
10915
10916     if (IS_MOVING(x, y))                // never change a running system ;-)
10917     {
10918       ChangeDelay[x][y] = 1;            // try change after next move step
10919       ChangePage[x][y] = page;          // remember page to use for change
10920
10921       return;
10922     }
10923
10924     // special case: set new level random seed before changing element
10925     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10926       handle_action_before_change = TRUE;
10927
10928     if (change->has_action && handle_action_before_change)
10929       ExecuteCustomElementAction(x, y, element, page);
10930
10931     if (change->can_change)
10932     {
10933       if (ChangeElement(x, y, element, page))
10934       {
10935         if (change->post_change_function)
10936           change->post_change_function(x, y);
10937       }
10938     }
10939
10940     if (change->has_action && !handle_action_before_change)
10941       ExecuteCustomElementAction(x, y, element, page);
10942   }
10943 }
10944
10945 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10946                                               int trigger_element,
10947                                               int trigger_event,
10948                                               int trigger_player,
10949                                               int trigger_side,
10950                                               int trigger_page)
10951 {
10952   boolean change_done_any = FALSE;
10953   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10954   int i;
10955
10956   if (!(trigger_events[trigger_element][trigger_event]))
10957     return FALSE;
10958
10959   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10960
10961   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10962   {
10963     int element = EL_CUSTOM_START + i;
10964     boolean change_done = FALSE;
10965     int p;
10966
10967     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10968         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10969       continue;
10970
10971     for (p = 0; p < element_info[element].num_change_pages; p++)
10972     {
10973       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10974
10975       if (change->can_change_or_has_action &&
10976           change->has_event[trigger_event] &&
10977           change->trigger_side & trigger_side &&
10978           change->trigger_player & trigger_player &&
10979           change->trigger_page & trigger_page_bits &&
10980           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10981       {
10982         change->actual_trigger_element = trigger_element;
10983         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10984         change->actual_trigger_player_bits = trigger_player;
10985         change->actual_trigger_side = trigger_side;
10986         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10987         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10988
10989         if ((change->can_change && !change_done) || change->has_action)
10990         {
10991           int x, y;
10992
10993           SCAN_PLAYFIELD(x, y)
10994           {
10995             if (Tile[x][y] == element)
10996             {
10997               if (change->can_change && !change_done)
10998               {
10999                 // if element already changed in this frame, not only prevent
11000                 // another element change (checked in ChangeElement()), but
11001                 // also prevent additional element actions for this element
11002
11003                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11004                     !level.use_action_after_change_bug)
11005                   continue;
11006
11007                 ChangeDelay[x][y] = 1;
11008                 ChangeEvent[x][y] = trigger_event;
11009
11010                 HandleElementChange(x, y, p);
11011               }
11012               else if (change->has_action)
11013               {
11014                 // if element already changed in this frame, not only prevent
11015                 // another element change (checked in ChangeElement()), but
11016                 // also prevent additional element actions for this element
11017
11018                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11019                     !level.use_action_after_change_bug)
11020                   continue;
11021
11022                 ExecuteCustomElementAction(x, y, element, p);
11023                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11024               }
11025             }
11026           }
11027
11028           if (change->can_change)
11029           {
11030             change_done = TRUE;
11031             change_done_any = TRUE;
11032           }
11033         }
11034       }
11035     }
11036   }
11037
11038   RECURSION_LOOP_DETECTION_END();
11039
11040   return change_done_any;
11041 }
11042
11043 static boolean CheckElementChangeExt(int x, int y,
11044                                      int element,
11045                                      int trigger_element,
11046                                      int trigger_event,
11047                                      int trigger_player,
11048                                      int trigger_side)
11049 {
11050   boolean change_done = FALSE;
11051   int p;
11052
11053   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11054       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11055     return FALSE;
11056
11057   if (Tile[x][y] == EL_BLOCKED)
11058   {
11059     Blocked2Moving(x, y, &x, &y);
11060     element = Tile[x][y];
11061   }
11062
11063   // check if element has already changed or is about to change after moving
11064   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11065        Tile[x][y] != element) ||
11066
11067       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11068        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11069         ChangePage[x][y] != -1)))
11070     return FALSE;
11071
11072   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11073
11074   for (p = 0; p < element_info[element].num_change_pages; p++)
11075   {
11076     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11077
11078     /* check trigger element for all events where the element that is checked
11079        for changing interacts with a directly adjacent element -- this is
11080        different to element changes that affect other elements to change on the
11081        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11082     boolean check_trigger_element =
11083       (trigger_event == CE_TOUCHING_X ||
11084        trigger_event == CE_HITTING_X ||
11085        trigger_event == CE_HIT_BY_X ||
11086        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11087
11088     if (change->can_change_or_has_action &&
11089         change->has_event[trigger_event] &&
11090         change->trigger_side & trigger_side &&
11091         change->trigger_player & trigger_player &&
11092         (!check_trigger_element ||
11093          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11094     {
11095       change->actual_trigger_element = trigger_element;
11096       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11097       change->actual_trigger_player_bits = trigger_player;
11098       change->actual_trigger_side = trigger_side;
11099       change->actual_trigger_ce_value = CustomValue[x][y];
11100       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11101
11102       // special case: trigger element not at (x,y) position for some events
11103       if (check_trigger_element)
11104       {
11105         static struct
11106         {
11107           int dx, dy;
11108         } move_xy[] =
11109           {
11110             {  0,  0 },
11111             { -1,  0 },
11112             { +1,  0 },
11113             {  0,  0 },
11114             {  0, -1 },
11115             {  0,  0 }, { 0, 0 }, { 0, 0 },
11116             {  0, +1 }
11117           };
11118
11119         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11120         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11121
11122         change->actual_trigger_ce_value = CustomValue[xx][yy];
11123         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11124       }
11125
11126       if (change->can_change && !change_done)
11127       {
11128         ChangeDelay[x][y] = 1;
11129         ChangeEvent[x][y] = trigger_event;
11130
11131         HandleElementChange(x, y, p);
11132
11133         change_done = TRUE;
11134       }
11135       else if (change->has_action)
11136       {
11137         ExecuteCustomElementAction(x, y, element, p);
11138         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11139       }
11140     }
11141   }
11142
11143   RECURSION_LOOP_DETECTION_END();
11144
11145   return change_done;
11146 }
11147
11148 static void PlayPlayerSound(struct PlayerInfo *player)
11149 {
11150   int jx = player->jx, jy = player->jy;
11151   int sound_element = player->artwork_element;
11152   int last_action = player->last_action_waiting;
11153   int action = player->action_waiting;
11154
11155   if (player->is_waiting)
11156   {
11157     if (action != last_action)
11158       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11159     else
11160       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11161   }
11162   else
11163   {
11164     if (action != last_action)
11165       StopSound(element_info[sound_element].sound[last_action]);
11166
11167     if (last_action == ACTION_SLEEPING)
11168       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11169   }
11170 }
11171
11172 static void PlayAllPlayersSound(void)
11173 {
11174   int i;
11175
11176   for (i = 0; i < MAX_PLAYERS; i++)
11177     if (stored_player[i].active)
11178       PlayPlayerSound(&stored_player[i]);
11179 }
11180
11181 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11182 {
11183   boolean last_waiting = player->is_waiting;
11184   int move_dir = player->MovDir;
11185
11186   player->dir_waiting = move_dir;
11187   player->last_action_waiting = player->action_waiting;
11188
11189   if (is_waiting)
11190   {
11191     if (!last_waiting)          // not waiting -> waiting
11192     {
11193       player->is_waiting = TRUE;
11194
11195       player->frame_counter_bored =
11196         FrameCounter +
11197         game.player_boring_delay_fixed +
11198         GetSimpleRandom(game.player_boring_delay_random);
11199       player->frame_counter_sleeping =
11200         FrameCounter +
11201         game.player_sleeping_delay_fixed +
11202         GetSimpleRandom(game.player_sleeping_delay_random);
11203
11204       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11205     }
11206
11207     if (game.player_sleeping_delay_fixed +
11208         game.player_sleeping_delay_random > 0 &&
11209         player->anim_delay_counter == 0 &&
11210         player->post_delay_counter == 0 &&
11211         FrameCounter >= player->frame_counter_sleeping)
11212       player->is_sleeping = TRUE;
11213     else if (game.player_boring_delay_fixed +
11214              game.player_boring_delay_random > 0 &&
11215              FrameCounter >= player->frame_counter_bored)
11216       player->is_bored = TRUE;
11217
11218     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11219                               player->is_bored ? ACTION_BORING :
11220                               ACTION_WAITING);
11221
11222     if (player->is_sleeping && player->use_murphy)
11223     {
11224       // special case for sleeping Murphy when leaning against non-free tile
11225
11226       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11227           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11228            !IS_MOVING(player->jx - 1, player->jy)))
11229         move_dir = MV_LEFT;
11230       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11231                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11232                 !IS_MOVING(player->jx + 1, player->jy)))
11233         move_dir = MV_RIGHT;
11234       else
11235         player->is_sleeping = FALSE;
11236
11237       player->dir_waiting = move_dir;
11238     }
11239
11240     if (player->is_sleeping)
11241     {
11242       if (player->num_special_action_sleeping > 0)
11243       {
11244         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11245         {
11246           int last_special_action = player->special_action_sleeping;
11247           int num_special_action = player->num_special_action_sleeping;
11248           int special_action =
11249             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11250              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11251              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11252              last_special_action + 1 : ACTION_SLEEPING);
11253           int special_graphic =
11254             el_act_dir2img(player->artwork_element, special_action, move_dir);
11255
11256           player->anim_delay_counter =
11257             graphic_info[special_graphic].anim_delay_fixed +
11258             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11259           player->post_delay_counter =
11260             graphic_info[special_graphic].post_delay_fixed +
11261             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11262
11263           player->special_action_sleeping = special_action;
11264         }
11265
11266         if (player->anim_delay_counter > 0)
11267         {
11268           player->action_waiting = player->special_action_sleeping;
11269           player->anim_delay_counter--;
11270         }
11271         else if (player->post_delay_counter > 0)
11272         {
11273           player->post_delay_counter--;
11274         }
11275       }
11276     }
11277     else if (player->is_bored)
11278     {
11279       if (player->num_special_action_bored > 0)
11280       {
11281         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11282         {
11283           int special_action =
11284             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11285           int special_graphic =
11286             el_act_dir2img(player->artwork_element, special_action, move_dir);
11287
11288           player->anim_delay_counter =
11289             graphic_info[special_graphic].anim_delay_fixed +
11290             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11291           player->post_delay_counter =
11292             graphic_info[special_graphic].post_delay_fixed +
11293             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11294
11295           player->special_action_bored = special_action;
11296         }
11297
11298         if (player->anim_delay_counter > 0)
11299         {
11300           player->action_waiting = player->special_action_bored;
11301           player->anim_delay_counter--;
11302         }
11303         else if (player->post_delay_counter > 0)
11304         {
11305           player->post_delay_counter--;
11306         }
11307       }
11308     }
11309   }
11310   else if (last_waiting)        // waiting -> not waiting
11311   {
11312     player->is_waiting = FALSE;
11313     player->is_bored = FALSE;
11314     player->is_sleeping = FALSE;
11315
11316     player->frame_counter_bored = -1;
11317     player->frame_counter_sleeping = -1;
11318
11319     player->anim_delay_counter = 0;
11320     player->post_delay_counter = 0;
11321
11322     player->dir_waiting = player->MovDir;
11323     player->action_waiting = ACTION_DEFAULT;
11324
11325     player->special_action_bored = ACTION_DEFAULT;
11326     player->special_action_sleeping = ACTION_DEFAULT;
11327   }
11328 }
11329
11330 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11331 {
11332   if ((!player->is_moving  && player->was_moving) ||
11333       (player->MovPos == 0 && player->was_moving) ||
11334       (player->is_snapping && !player->was_snapping) ||
11335       (player->is_dropping && !player->was_dropping))
11336   {
11337     if (!CheckSaveEngineSnapshotToList())
11338       return;
11339
11340     player->was_moving = FALSE;
11341     player->was_snapping = TRUE;
11342     player->was_dropping = TRUE;
11343   }
11344   else
11345   {
11346     if (player->is_moving)
11347       player->was_moving = TRUE;
11348
11349     if (!player->is_snapping)
11350       player->was_snapping = FALSE;
11351
11352     if (!player->is_dropping)
11353       player->was_dropping = FALSE;
11354   }
11355
11356   static struct MouseActionInfo mouse_action_last = { 0 };
11357   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11358   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11359
11360   if (new_released)
11361     CheckSaveEngineSnapshotToList();
11362
11363   mouse_action_last = mouse_action;
11364 }
11365
11366 static void CheckSingleStepMode(struct PlayerInfo *player)
11367 {
11368   if (tape.single_step && tape.recording && !tape.pausing)
11369   {
11370     // as it is called "single step mode", just return to pause mode when the
11371     // player stopped moving after one tile (or never starts moving at all)
11372     // (reverse logic needed here in case single step mode used in team mode)
11373     if (player->is_moving ||
11374         player->is_pushing ||
11375         player->is_dropping_pressed ||
11376         player->effective_mouse_action.button)
11377       game.enter_single_step_mode = FALSE;
11378   }
11379
11380   CheckSaveEngineSnapshot(player);
11381 }
11382
11383 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11384 {
11385   int left      = player_action & JOY_LEFT;
11386   int right     = player_action & JOY_RIGHT;
11387   int up        = player_action & JOY_UP;
11388   int down      = player_action & JOY_DOWN;
11389   int button1   = player_action & JOY_BUTTON_1;
11390   int button2   = player_action & JOY_BUTTON_2;
11391   int dx        = (left ? -1 : right ? 1 : 0);
11392   int dy        = (up   ? -1 : down  ? 1 : 0);
11393
11394   if (!player->active || tape.pausing)
11395     return 0;
11396
11397   if (player_action)
11398   {
11399     if (button1)
11400       SnapField(player, dx, dy);
11401     else
11402     {
11403       if (button2)
11404         DropElement(player);
11405
11406       MovePlayer(player, dx, dy);
11407     }
11408
11409     CheckSingleStepMode(player);
11410
11411     SetPlayerWaiting(player, FALSE);
11412
11413     return player_action;
11414   }
11415   else
11416   {
11417     // no actions for this player (no input at player's configured device)
11418
11419     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11420     SnapField(player, 0, 0);
11421     CheckGravityMovementWhenNotMoving(player);
11422
11423     if (player->MovPos == 0)
11424       SetPlayerWaiting(player, TRUE);
11425
11426     if (player->MovPos == 0)    // needed for tape.playing
11427       player->is_moving = FALSE;
11428
11429     player->is_dropping = FALSE;
11430     player->is_dropping_pressed = FALSE;
11431     player->drop_pressed_delay = 0;
11432
11433     CheckSingleStepMode(player);
11434
11435     return 0;
11436   }
11437 }
11438
11439 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11440                                          byte *tape_action)
11441 {
11442   if (!tape.use_mouse_actions)
11443     return;
11444
11445   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11446   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11447   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11448 }
11449
11450 static void SetTapeActionFromMouseAction(byte *tape_action,
11451                                          struct MouseActionInfo *mouse_action)
11452 {
11453   if (!tape.use_mouse_actions)
11454     return;
11455
11456   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11457   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11458   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11459 }
11460
11461 static void CheckLevelSolved(void)
11462 {
11463   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11464   {
11465     if (game_em.level_solved &&
11466         !game_em.game_over)                             // game won
11467     {
11468       LevelSolved();
11469
11470       game_em.game_over = TRUE;
11471
11472       game.all_players_gone = TRUE;
11473     }
11474
11475     if (game_em.game_over)                              // game lost
11476       game.all_players_gone = TRUE;
11477   }
11478   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11479   {
11480     if (game_sp.level_solved &&
11481         !game_sp.game_over)                             // game won
11482     {
11483       LevelSolved();
11484
11485       game_sp.game_over = TRUE;
11486
11487       game.all_players_gone = TRUE;
11488     }
11489
11490     if (game_sp.game_over)                              // game lost
11491       game.all_players_gone = TRUE;
11492   }
11493   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11494   {
11495     if (game_mm.level_solved &&
11496         !game_mm.game_over)                             // game won
11497     {
11498       LevelSolved();
11499
11500       game_mm.game_over = TRUE;
11501
11502       game.all_players_gone = TRUE;
11503     }
11504
11505     if (game_mm.game_over)                              // game lost
11506       game.all_players_gone = TRUE;
11507   }
11508 }
11509
11510 static void CheckLevelTime(void)
11511 {
11512   int i;
11513
11514   if (TimeFrames >= FRAMES_PER_SECOND)
11515   {
11516     TimeFrames = 0;
11517     TapeTime++;
11518
11519     for (i = 0; i < MAX_PLAYERS; i++)
11520     {
11521       struct PlayerInfo *player = &stored_player[i];
11522
11523       if (SHIELD_ON(player))
11524       {
11525         player->shield_normal_time_left--;
11526
11527         if (player->shield_deadly_time_left > 0)
11528           player->shield_deadly_time_left--;
11529       }
11530     }
11531
11532     if (!game.LevelSolved && !level.use_step_counter)
11533     {
11534       TimePlayed++;
11535
11536       if (TimeLeft > 0)
11537       {
11538         TimeLeft--;
11539
11540         if (TimeLeft <= 10 && setup.time_limit)
11541           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11542
11543         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11544            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11545
11546         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11547
11548         if (!TimeLeft && setup.time_limit)
11549         {
11550           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11551             game_em.lev->killed_out_of_time = TRUE;
11552           else
11553             for (i = 0; i < MAX_PLAYERS; i++)
11554               KillPlayer(&stored_player[i]);
11555         }
11556       }
11557       else if (game.no_time_limit && !game.all_players_gone)
11558       {
11559         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11560       }
11561
11562       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11563     }
11564
11565     if (tape.recording || tape.playing)
11566       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11567   }
11568
11569   if (tape.recording || tape.playing)
11570     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11571
11572   UpdateAndDisplayGameControlValues();
11573 }
11574
11575 void AdvanceFrameAndPlayerCounters(int player_nr)
11576 {
11577   int i;
11578
11579   // advance frame counters (global frame counter and time frame counter)
11580   FrameCounter++;
11581   TimeFrames++;
11582
11583   // advance player counters (counters for move delay, move animation etc.)
11584   for (i = 0; i < MAX_PLAYERS; i++)
11585   {
11586     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11587     int move_delay_value = stored_player[i].move_delay_value;
11588     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11589
11590     if (!advance_player_counters)       // not all players may be affected
11591       continue;
11592
11593     if (move_frames == 0)       // less than one move per game frame
11594     {
11595       int stepsize = TILEX / move_delay_value;
11596       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11597       int count = (stored_player[i].is_moving ?
11598                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11599
11600       if (count % delay == 0)
11601         move_frames = 1;
11602     }
11603
11604     stored_player[i].Frame += move_frames;
11605
11606     if (stored_player[i].MovPos != 0)
11607       stored_player[i].StepFrame += move_frames;
11608
11609     if (stored_player[i].move_delay > 0)
11610       stored_player[i].move_delay--;
11611
11612     // due to bugs in previous versions, counter must count up, not down
11613     if (stored_player[i].push_delay != -1)
11614       stored_player[i].push_delay++;
11615
11616     if (stored_player[i].drop_delay > 0)
11617       stored_player[i].drop_delay--;
11618
11619     if (stored_player[i].is_dropping_pressed)
11620       stored_player[i].drop_pressed_delay++;
11621   }
11622 }
11623
11624 void StartGameActions(boolean init_network_game, boolean record_tape,
11625                       int random_seed)
11626 {
11627   unsigned int new_random_seed = InitRND(random_seed);
11628
11629   if (record_tape)
11630     TapeStartRecording(new_random_seed);
11631
11632   if (init_network_game)
11633   {
11634     SendToServer_LevelFile();
11635     SendToServer_StartPlaying();
11636
11637     return;
11638   }
11639
11640   InitGame();
11641 }
11642
11643 static void GameActionsExt(void)
11644 {
11645 #if 0
11646   static unsigned int game_frame_delay = 0;
11647 #endif
11648   unsigned int game_frame_delay_value;
11649   byte *recorded_player_action;
11650   byte summarized_player_action = 0;
11651   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11652   int i;
11653
11654   // detect endless loops, caused by custom element programming
11655   if (recursion_loop_detected && recursion_loop_depth == 0)
11656   {
11657     char *message = getStringCat3("Internal Error! Element ",
11658                                   EL_NAME(recursion_loop_element),
11659                                   " caused endless loop! Quit the game?");
11660
11661     Warn("element '%s' caused endless loop in game engine",
11662          EL_NAME(recursion_loop_element));
11663
11664     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11665
11666     recursion_loop_detected = FALSE;    // if game should be continued
11667
11668     free(message);
11669
11670     return;
11671   }
11672
11673   if (game.restart_level)
11674     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11675
11676   CheckLevelSolved();
11677
11678   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11679     GameWon();
11680
11681   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11682     TapeStop();
11683
11684   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11685     return;
11686
11687   game_frame_delay_value =
11688     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11689
11690   if (tape.playing && tape.warp_forward && !tape.pausing)
11691     game_frame_delay_value = 0;
11692
11693   SetVideoFrameDelay(game_frame_delay_value);
11694
11695   // (de)activate virtual buttons depending on current game status
11696   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11697   {
11698     if (game.all_players_gone)  // if no players there to be controlled anymore
11699       SetOverlayActive(FALSE);
11700     else if (!tape.playing)     // if game continues after tape stopped playing
11701       SetOverlayActive(TRUE);
11702   }
11703
11704 #if 0
11705 #if 0
11706   // ---------- main game synchronization point ----------
11707
11708   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11709
11710   Debug("game:playing:skip", "skip == %d", skip);
11711
11712 #else
11713   // ---------- main game synchronization point ----------
11714
11715   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11716 #endif
11717 #endif
11718
11719   if (network_playing && !network_player_action_received)
11720   {
11721     // try to get network player actions in time
11722
11723     // last chance to get network player actions without main loop delay
11724     HandleNetworking();
11725
11726     // game was quit by network peer
11727     if (game_status != GAME_MODE_PLAYING)
11728       return;
11729
11730     // check if network player actions still missing and game still running
11731     if (!network_player_action_received && !checkGameEnded())
11732       return;           // failed to get network player actions in time
11733
11734     // do not yet reset "network_player_action_received" (for tape.pausing)
11735   }
11736
11737   if (tape.pausing)
11738     return;
11739
11740   // at this point we know that we really continue executing the game
11741
11742   network_player_action_received = FALSE;
11743
11744   // when playing tape, read previously recorded player input from tape data
11745   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11746
11747   local_player->effective_mouse_action = local_player->mouse_action;
11748
11749   if (recorded_player_action != NULL)
11750     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11751                                  recorded_player_action);
11752
11753   // TapePlayAction() may return NULL when toggling to "pause before death"
11754   if (tape.pausing)
11755     return;
11756
11757   if (tape.set_centered_player)
11758   {
11759     game.centered_player_nr_next = tape.centered_player_nr_next;
11760     game.set_centered_player = TRUE;
11761   }
11762
11763   for (i = 0; i < MAX_PLAYERS; i++)
11764   {
11765     summarized_player_action |= stored_player[i].action;
11766
11767     if (!network_playing && (game.team_mode || tape.playing))
11768       stored_player[i].effective_action = stored_player[i].action;
11769   }
11770
11771   if (network_playing && !checkGameEnded())
11772     SendToServer_MovePlayer(summarized_player_action);
11773
11774   // summarize all actions at local players mapped input device position
11775   // (this allows using different input devices in single player mode)
11776   if (!network.enabled && !game.team_mode)
11777     stored_player[map_player_action[local_player->index_nr]].effective_action =
11778       summarized_player_action;
11779
11780   // summarize all actions at centered player in local team mode
11781   if (tape.recording &&
11782       setup.team_mode && !network.enabled &&
11783       setup.input_on_focus &&
11784       game.centered_player_nr != -1)
11785   {
11786     for (i = 0; i < MAX_PLAYERS; i++)
11787       stored_player[map_player_action[i]].effective_action =
11788         (i == game.centered_player_nr ? summarized_player_action : 0);
11789   }
11790
11791   if (recorded_player_action != NULL)
11792     for (i = 0; i < MAX_PLAYERS; i++)
11793       stored_player[i].effective_action = recorded_player_action[i];
11794
11795   for (i = 0; i < MAX_PLAYERS; i++)
11796   {
11797     tape_action[i] = stored_player[i].effective_action;
11798
11799     /* (this may happen in the RND game engine if a player was not present on
11800        the playfield on level start, but appeared later from a custom element */
11801     if (setup.team_mode &&
11802         tape.recording &&
11803         tape_action[i] &&
11804         !tape.player_participates[i])
11805       tape.player_participates[i] = TRUE;
11806   }
11807
11808   SetTapeActionFromMouseAction(tape_action,
11809                                &local_player->effective_mouse_action);
11810
11811   // only record actions from input devices, but not programmed actions
11812   if (tape.recording)
11813     TapeRecordAction(tape_action);
11814
11815   // remember if game was played (especially after tape stopped playing)
11816   if (!tape.playing && summarized_player_action)
11817     game.GamePlayed = TRUE;
11818
11819 #if USE_NEW_PLAYER_ASSIGNMENTS
11820   // !!! also map player actions in single player mode !!!
11821   // if (game.team_mode)
11822   if (1)
11823   {
11824     byte mapped_action[MAX_PLAYERS];
11825
11826 #if DEBUG_PLAYER_ACTIONS
11827     for (i = 0; i < MAX_PLAYERS; i++)
11828       DebugContinued("", "%d, ", stored_player[i].effective_action);
11829 #endif
11830
11831     for (i = 0; i < MAX_PLAYERS; i++)
11832       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11833
11834     for (i = 0; i < MAX_PLAYERS; i++)
11835       stored_player[i].effective_action = mapped_action[i];
11836
11837 #if DEBUG_PLAYER_ACTIONS
11838     DebugContinued("", "=> ");
11839     for (i = 0; i < MAX_PLAYERS; i++)
11840       DebugContinued("", "%d, ", stored_player[i].effective_action);
11841     DebugContinued("game:playing:player", "\n");
11842 #endif
11843   }
11844 #if DEBUG_PLAYER_ACTIONS
11845   else
11846   {
11847     for (i = 0; i < MAX_PLAYERS; i++)
11848       DebugContinued("", "%d, ", stored_player[i].effective_action);
11849     DebugContinued("game:playing:player", "\n");
11850   }
11851 #endif
11852 #endif
11853
11854   for (i = 0; i < MAX_PLAYERS; i++)
11855   {
11856     // allow engine snapshot in case of changed movement attempt
11857     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11858         (stored_player[i].effective_action & KEY_MOTION))
11859       game.snapshot.changed_action = TRUE;
11860
11861     // allow engine snapshot in case of snapping/dropping attempt
11862     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11863         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11864       game.snapshot.changed_action = TRUE;
11865
11866     game.snapshot.last_action[i] = stored_player[i].effective_action;
11867   }
11868
11869   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11870   {
11871     GameActions_EM_Main();
11872   }
11873   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11874   {
11875     GameActions_SP_Main();
11876   }
11877   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11878   {
11879     GameActions_MM_Main();
11880   }
11881   else
11882   {
11883     GameActions_RND_Main();
11884   }
11885
11886   BlitScreenToBitmap(backbuffer);
11887
11888   CheckLevelSolved();
11889   CheckLevelTime();
11890
11891   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11892
11893   if (global.show_frames_per_second)
11894   {
11895     static unsigned int fps_counter = 0;
11896     static int fps_frames = 0;
11897     unsigned int fps_delay_ms = Counter() - fps_counter;
11898
11899     fps_frames++;
11900
11901     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11902     {
11903       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11904
11905       fps_frames = 0;
11906       fps_counter = Counter();
11907
11908       // always draw FPS to screen after FPS value was updated
11909       redraw_mask |= REDRAW_FPS;
11910     }
11911
11912     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11913     if (GetDrawDeactivationMask() == REDRAW_NONE)
11914       redraw_mask |= REDRAW_FPS;
11915   }
11916 }
11917
11918 static void GameActions_CheckSaveEngineSnapshot(void)
11919 {
11920   if (!game.snapshot.save_snapshot)
11921     return;
11922
11923   // clear flag for saving snapshot _before_ saving snapshot
11924   game.snapshot.save_snapshot = FALSE;
11925
11926   SaveEngineSnapshotToList();
11927 }
11928
11929 void GameActions(void)
11930 {
11931   GameActionsExt();
11932
11933   GameActions_CheckSaveEngineSnapshot();
11934 }
11935
11936 void GameActions_EM_Main(void)
11937 {
11938   byte effective_action[MAX_PLAYERS];
11939   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11940   int i;
11941
11942   for (i = 0; i < MAX_PLAYERS; i++)
11943     effective_action[i] = stored_player[i].effective_action;
11944
11945   GameActions_EM(effective_action, warp_mode);
11946 }
11947
11948 void GameActions_SP_Main(void)
11949 {
11950   byte effective_action[MAX_PLAYERS];
11951   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11952   int i;
11953
11954   for (i = 0; i < MAX_PLAYERS; i++)
11955     effective_action[i] = stored_player[i].effective_action;
11956
11957   GameActions_SP(effective_action, warp_mode);
11958
11959   for (i = 0; i < MAX_PLAYERS; i++)
11960   {
11961     if (stored_player[i].force_dropping)
11962       stored_player[i].action |= KEY_BUTTON_DROP;
11963
11964     stored_player[i].force_dropping = FALSE;
11965   }
11966 }
11967
11968 void GameActions_MM_Main(void)
11969 {
11970   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11971
11972   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11973 }
11974
11975 void GameActions_RND_Main(void)
11976 {
11977   GameActions_RND();
11978 }
11979
11980 void GameActions_RND(void)
11981 {
11982   static struct MouseActionInfo mouse_action_last = { 0 };
11983   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11984   int magic_wall_x = 0, magic_wall_y = 0;
11985   int i, x, y, element, graphic, last_gfx_frame;
11986
11987   InitPlayfieldScanModeVars();
11988
11989   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11990   {
11991     SCAN_PLAYFIELD(x, y)
11992     {
11993       ChangeCount[x][y] = 0;
11994       ChangeEvent[x][y] = -1;
11995     }
11996   }
11997
11998   if (game.set_centered_player)
11999   {
12000     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12001
12002     // switching to "all players" only possible if all players fit to screen
12003     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12004     {
12005       game.centered_player_nr_next = game.centered_player_nr;
12006       game.set_centered_player = FALSE;
12007     }
12008
12009     // do not switch focus to non-existing (or non-active) player
12010     if (game.centered_player_nr_next >= 0 &&
12011         !stored_player[game.centered_player_nr_next].active)
12012     {
12013       game.centered_player_nr_next = game.centered_player_nr;
12014       game.set_centered_player = FALSE;
12015     }
12016   }
12017
12018   if (game.set_centered_player &&
12019       ScreenMovPos == 0)        // screen currently aligned at tile position
12020   {
12021     int sx, sy;
12022
12023     if (game.centered_player_nr_next == -1)
12024     {
12025       setScreenCenteredToAllPlayers(&sx, &sy);
12026     }
12027     else
12028     {
12029       sx = stored_player[game.centered_player_nr_next].jx;
12030       sy = stored_player[game.centered_player_nr_next].jy;
12031     }
12032
12033     game.centered_player_nr = game.centered_player_nr_next;
12034     game.set_centered_player = FALSE;
12035
12036     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12037     DrawGameDoorValues();
12038   }
12039
12040   // check single step mode (set flag and clear again if any player is active)
12041   game.enter_single_step_mode =
12042     (tape.single_step && tape.recording && !tape.pausing);
12043
12044   for (i = 0; i < MAX_PLAYERS; i++)
12045   {
12046     int actual_player_action = stored_player[i].effective_action;
12047
12048 #if 1
12049     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12050        - rnd_equinox_tetrachloride 048
12051        - rnd_equinox_tetrachloride_ii 096
12052        - rnd_emanuel_schmieg 002
12053        - doctor_sloan_ww 001, 020
12054     */
12055     if (stored_player[i].MovPos == 0)
12056       CheckGravityMovement(&stored_player[i]);
12057 #endif
12058
12059     // overwrite programmed action with tape action
12060     if (stored_player[i].programmed_action)
12061       actual_player_action = stored_player[i].programmed_action;
12062
12063     PlayerActions(&stored_player[i], actual_player_action);
12064
12065     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12066   }
12067
12068   // single step pause mode may already have been toggled by "ScrollPlayer()"
12069   if (game.enter_single_step_mode && !tape.pausing)
12070     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12071
12072   ScrollScreen(NULL, SCROLL_GO_ON);
12073
12074   /* for backwards compatibility, the following code emulates a fixed bug that
12075      occured when pushing elements (causing elements that just made their last
12076      pushing step to already (if possible) make their first falling step in the
12077      same game frame, which is bad); this code is also needed to use the famous
12078      "spring push bug" which is used in older levels and might be wanted to be
12079      used also in newer levels, but in this case the buggy pushing code is only
12080      affecting the "spring" element and no other elements */
12081
12082   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12083   {
12084     for (i = 0; i < MAX_PLAYERS; i++)
12085     {
12086       struct PlayerInfo *player = &stored_player[i];
12087       int x = player->jx;
12088       int y = player->jy;
12089
12090       if (player->active && player->is_pushing && player->is_moving &&
12091           IS_MOVING(x, y) &&
12092           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12093            Tile[x][y] == EL_SPRING))
12094       {
12095         ContinueMoving(x, y);
12096
12097         // continue moving after pushing (this is actually a bug)
12098         if (!IS_MOVING(x, y))
12099           Stop[x][y] = FALSE;
12100       }
12101     }
12102   }
12103
12104   SCAN_PLAYFIELD(x, y)
12105   {
12106     Last[x][y] = Tile[x][y];
12107
12108     ChangeCount[x][y] = 0;
12109     ChangeEvent[x][y] = -1;
12110
12111     // this must be handled before main playfield loop
12112     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12113     {
12114       MovDelay[x][y]--;
12115       if (MovDelay[x][y] <= 0)
12116         RemoveField(x, y);
12117     }
12118
12119     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12120     {
12121       MovDelay[x][y]--;
12122       if (MovDelay[x][y] <= 0)
12123       {
12124         int element = Store[x][y];
12125         int move_direction = MovDir[x][y];
12126         int player_index_bit = Store2[x][y];
12127
12128         Store[x][y] = 0;
12129         Store2[x][y] = 0;
12130
12131         RemoveField(x, y);
12132         TEST_DrawLevelField(x, y);
12133
12134         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12135       }
12136     }
12137
12138 #if DEBUG
12139     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12140     {
12141       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12142             x, y);
12143       Debug("game:playing:GameActions_RND", "This should never happen!");
12144
12145       ChangePage[x][y] = -1;
12146     }
12147 #endif
12148
12149     Stop[x][y] = FALSE;
12150     if (WasJustMoving[x][y] > 0)
12151       WasJustMoving[x][y]--;
12152     if (WasJustFalling[x][y] > 0)
12153       WasJustFalling[x][y]--;
12154     if (CheckCollision[x][y] > 0)
12155       CheckCollision[x][y]--;
12156     if (CheckImpact[x][y] > 0)
12157       CheckImpact[x][y]--;
12158
12159     GfxFrame[x][y]++;
12160
12161     /* reset finished pushing action (not done in ContinueMoving() to allow
12162        continuous pushing animation for elements with zero push delay) */
12163     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12164     {
12165       ResetGfxAnimation(x, y);
12166       TEST_DrawLevelField(x, y);
12167     }
12168
12169 #if DEBUG
12170     if (IS_BLOCKED(x, y))
12171     {
12172       int oldx, oldy;
12173
12174       Blocked2Moving(x, y, &oldx, &oldy);
12175       if (!IS_MOVING(oldx, oldy))
12176       {
12177         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12178         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12179         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12180         Debug("game:playing:GameActions_RND", "This should never happen!");
12181       }
12182     }
12183 #endif
12184   }
12185
12186   if (mouse_action.button)
12187   {
12188     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12189     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12190
12191     x = mouse_action.lx;
12192     y = mouse_action.ly;
12193     element = Tile[x][y];
12194
12195     if (new_button)
12196     {
12197       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12198       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12199                                          ch_button);
12200     }
12201
12202     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12203     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12204                                        ch_button);
12205   }
12206
12207   SCAN_PLAYFIELD(x, y)
12208   {
12209     element = Tile[x][y];
12210     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12211     last_gfx_frame = GfxFrame[x][y];
12212
12213     ResetGfxFrame(x, y);
12214
12215     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12216       DrawLevelGraphicAnimation(x, y, graphic);
12217
12218     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12219         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12220       ResetRandomAnimationValue(x, y);
12221
12222     SetRandomAnimationValue(x, y);
12223
12224     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12225
12226     if (IS_INACTIVE(element))
12227     {
12228       if (IS_ANIMATED(graphic))
12229         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12230
12231       continue;
12232     }
12233
12234     // this may take place after moving, so 'element' may have changed
12235     if (IS_CHANGING(x, y) &&
12236         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12237     {
12238       int page = element_info[element].event_page_nr[CE_DELAY];
12239
12240       HandleElementChange(x, y, page);
12241
12242       element = Tile[x][y];
12243       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12244     }
12245
12246     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12247     {
12248       StartMoving(x, y);
12249
12250       element = Tile[x][y];
12251       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12252
12253       if (IS_ANIMATED(graphic) &&
12254           !IS_MOVING(x, y) &&
12255           !Stop[x][y])
12256         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12257
12258       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12259         TEST_DrawTwinkleOnField(x, y);
12260     }
12261     else if (element == EL_ACID)
12262     {
12263       if (!Stop[x][y])
12264         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12265     }
12266     else if ((element == EL_EXIT_OPEN ||
12267               element == EL_EM_EXIT_OPEN ||
12268               element == EL_SP_EXIT_OPEN ||
12269               element == EL_STEEL_EXIT_OPEN ||
12270               element == EL_EM_STEEL_EXIT_OPEN ||
12271               element == EL_SP_TERMINAL ||
12272               element == EL_SP_TERMINAL_ACTIVE ||
12273               element == EL_EXTRA_TIME ||
12274               element == EL_SHIELD_NORMAL ||
12275               element == EL_SHIELD_DEADLY) &&
12276              IS_ANIMATED(graphic))
12277       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12278     else if (IS_MOVING(x, y))
12279       ContinueMoving(x, y);
12280     else if (IS_ACTIVE_BOMB(element))
12281       CheckDynamite(x, y);
12282     else if (element == EL_AMOEBA_GROWING)
12283       AmoebaGrowing(x, y);
12284     else if (element == EL_AMOEBA_SHRINKING)
12285       AmoebaShrinking(x, y);
12286
12287 #if !USE_NEW_AMOEBA_CODE
12288     else if (IS_AMOEBALIVE(element))
12289       AmoebaReproduce(x, y);
12290 #endif
12291
12292     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12293       Life(x, y);
12294     else if (element == EL_EXIT_CLOSED)
12295       CheckExit(x, y);
12296     else if (element == EL_EM_EXIT_CLOSED)
12297       CheckExitEM(x, y);
12298     else if (element == EL_STEEL_EXIT_CLOSED)
12299       CheckExitSteel(x, y);
12300     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12301       CheckExitSteelEM(x, y);
12302     else if (element == EL_SP_EXIT_CLOSED)
12303       CheckExitSP(x, y);
12304     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12305              element == EL_EXPANDABLE_STEELWALL_GROWING)
12306       MauerWaechst(x, y);
12307     else if (element == EL_EXPANDABLE_WALL ||
12308              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12309              element == EL_EXPANDABLE_WALL_VERTICAL ||
12310              element == EL_EXPANDABLE_WALL_ANY ||
12311              element == EL_BD_EXPANDABLE_WALL)
12312       MauerAbleger(x, y);
12313     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12314              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12315              element == EL_EXPANDABLE_STEELWALL_ANY)
12316       MauerAblegerStahl(x, y);
12317     else if (element == EL_FLAMES)
12318       CheckForDragon(x, y);
12319     else if (element == EL_EXPLOSION)
12320       ; // drawing of correct explosion animation is handled separately
12321     else if (element == EL_ELEMENT_SNAPPING ||
12322              element == EL_DIAGONAL_SHRINKING ||
12323              element == EL_DIAGONAL_GROWING)
12324     {
12325       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12326
12327       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12328     }
12329     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12330       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12331
12332     if (IS_BELT_ACTIVE(element))
12333       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12334
12335     if (game.magic_wall_active)
12336     {
12337       int jx = local_player->jx, jy = local_player->jy;
12338
12339       // play the element sound at the position nearest to the player
12340       if ((element == EL_MAGIC_WALL_FULL ||
12341            element == EL_MAGIC_WALL_ACTIVE ||
12342            element == EL_MAGIC_WALL_EMPTYING ||
12343            element == EL_BD_MAGIC_WALL_FULL ||
12344            element == EL_BD_MAGIC_WALL_ACTIVE ||
12345            element == EL_BD_MAGIC_WALL_EMPTYING ||
12346            element == EL_DC_MAGIC_WALL_FULL ||
12347            element == EL_DC_MAGIC_WALL_ACTIVE ||
12348            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12349           ABS(x - jx) + ABS(y - jy) <
12350           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12351       {
12352         magic_wall_x = x;
12353         magic_wall_y = y;
12354       }
12355     }
12356   }
12357
12358 #if USE_NEW_AMOEBA_CODE
12359   // new experimental amoeba growth stuff
12360   if (!(FrameCounter % 8))
12361   {
12362     static unsigned int random = 1684108901;
12363
12364     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12365     {
12366       x = RND(lev_fieldx);
12367       y = RND(lev_fieldy);
12368       element = Tile[x][y];
12369
12370       if (!IS_PLAYER(x,y) &&
12371           (element == EL_EMPTY ||
12372            CAN_GROW_INTO(element) ||
12373            element == EL_QUICKSAND_EMPTY ||
12374            element == EL_QUICKSAND_FAST_EMPTY ||
12375            element == EL_ACID_SPLASH_LEFT ||
12376            element == EL_ACID_SPLASH_RIGHT))
12377       {
12378         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12379             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12380             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12381             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12382           Tile[x][y] = EL_AMOEBA_DROP;
12383       }
12384
12385       random = random * 129 + 1;
12386     }
12387   }
12388 #endif
12389
12390   game.explosions_delayed = FALSE;
12391
12392   SCAN_PLAYFIELD(x, y)
12393   {
12394     element = Tile[x][y];
12395
12396     if (ExplodeField[x][y])
12397       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12398     else if (element == EL_EXPLOSION)
12399       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12400
12401     ExplodeField[x][y] = EX_TYPE_NONE;
12402   }
12403
12404   game.explosions_delayed = TRUE;
12405
12406   if (game.magic_wall_active)
12407   {
12408     if (!(game.magic_wall_time_left % 4))
12409     {
12410       int element = Tile[magic_wall_x][magic_wall_y];
12411
12412       if (element == EL_BD_MAGIC_WALL_FULL ||
12413           element == EL_BD_MAGIC_WALL_ACTIVE ||
12414           element == EL_BD_MAGIC_WALL_EMPTYING)
12415         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12416       else if (element == EL_DC_MAGIC_WALL_FULL ||
12417                element == EL_DC_MAGIC_WALL_ACTIVE ||
12418                element == EL_DC_MAGIC_WALL_EMPTYING)
12419         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12420       else
12421         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12422     }
12423
12424     if (game.magic_wall_time_left > 0)
12425     {
12426       game.magic_wall_time_left--;
12427
12428       if (!game.magic_wall_time_left)
12429       {
12430         SCAN_PLAYFIELD(x, y)
12431         {
12432           element = Tile[x][y];
12433
12434           if (element == EL_MAGIC_WALL_ACTIVE ||
12435               element == EL_MAGIC_WALL_FULL)
12436           {
12437             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12438             TEST_DrawLevelField(x, y);
12439           }
12440           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12441                    element == EL_BD_MAGIC_WALL_FULL)
12442           {
12443             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12444             TEST_DrawLevelField(x, y);
12445           }
12446           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12447                    element == EL_DC_MAGIC_WALL_FULL)
12448           {
12449             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12450             TEST_DrawLevelField(x, y);
12451           }
12452         }
12453
12454         game.magic_wall_active = FALSE;
12455       }
12456     }
12457   }
12458
12459   if (game.light_time_left > 0)
12460   {
12461     game.light_time_left--;
12462
12463     if (game.light_time_left == 0)
12464       RedrawAllLightSwitchesAndInvisibleElements();
12465   }
12466
12467   if (game.timegate_time_left > 0)
12468   {
12469     game.timegate_time_left--;
12470
12471     if (game.timegate_time_left == 0)
12472       CloseAllOpenTimegates();
12473   }
12474
12475   if (game.lenses_time_left > 0)
12476   {
12477     game.lenses_time_left--;
12478
12479     if (game.lenses_time_left == 0)
12480       RedrawAllInvisibleElementsForLenses();
12481   }
12482
12483   if (game.magnify_time_left > 0)
12484   {
12485     game.magnify_time_left--;
12486
12487     if (game.magnify_time_left == 0)
12488       RedrawAllInvisibleElementsForMagnifier();
12489   }
12490
12491   for (i = 0; i < MAX_PLAYERS; i++)
12492   {
12493     struct PlayerInfo *player = &stored_player[i];
12494
12495     if (SHIELD_ON(player))
12496     {
12497       if (player->shield_deadly_time_left)
12498         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12499       else if (player->shield_normal_time_left)
12500         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12501     }
12502   }
12503
12504 #if USE_DELAYED_GFX_REDRAW
12505   SCAN_PLAYFIELD(x, y)
12506   {
12507     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12508     {
12509       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12510          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12511
12512       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12513         DrawLevelField(x, y);
12514
12515       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12516         DrawLevelFieldCrumbled(x, y);
12517
12518       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12519         DrawLevelFieldCrumbledNeighbours(x, y);
12520
12521       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12522         DrawTwinkleOnField(x, y);
12523     }
12524
12525     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12526   }
12527 #endif
12528
12529   DrawAllPlayers();
12530   PlayAllPlayersSound();
12531
12532   for (i = 0; i < MAX_PLAYERS; i++)
12533   {
12534     struct PlayerInfo *player = &stored_player[i];
12535
12536     if (player->show_envelope != 0 && (!player->active ||
12537                                        player->MovPos == 0))
12538     {
12539       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12540
12541       player->show_envelope = 0;
12542     }
12543   }
12544
12545   // use random number generator in every frame to make it less predictable
12546   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12547     RND(1);
12548
12549   mouse_action_last = mouse_action;
12550 }
12551
12552 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12553 {
12554   int min_x = x, min_y = y, max_x = x, max_y = y;
12555   int scr_fieldx = getScreenFieldSizeX();
12556   int scr_fieldy = getScreenFieldSizeY();
12557   int i;
12558
12559   for (i = 0; i < MAX_PLAYERS; i++)
12560   {
12561     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12562
12563     if (!stored_player[i].active || &stored_player[i] == player)
12564       continue;
12565
12566     min_x = MIN(min_x, jx);
12567     min_y = MIN(min_y, jy);
12568     max_x = MAX(max_x, jx);
12569     max_y = MAX(max_y, jy);
12570   }
12571
12572   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12573 }
12574
12575 static boolean AllPlayersInVisibleScreen(void)
12576 {
12577   int i;
12578
12579   for (i = 0; i < MAX_PLAYERS; i++)
12580   {
12581     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12582
12583     if (!stored_player[i].active)
12584       continue;
12585
12586     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12587       return FALSE;
12588   }
12589
12590   return TRUE;
12591 }
12592
12593 void ScrollLevel(int dx, int dy)
12594 {
12595   int scroll_offset = 2 * TILEX_VAR;
12596   int x, y;
12597
12598   BlitBitmap(drawto_field, drawto_field,
12599              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12600              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12601              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12602              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12603              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12604              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12605
12606   if (dx != 0)
12607   {
12608     x = (dx == 1 ? BX1 : BX2);
12609     for (y = BY1; y <= BY2; y++)
12610       DrawScreenField(x, y);
12611   }
12612
12613   if (dy != 0)
12614   {
12615     y = (dy == 1 ? BY1 : BY2);
12616     for (x = BX1; x <= BX2; x++)
12617       DrawScreenField(x, y);
12618   }
12619
12620   redraw_mask |= REDRAW_FIELD;
12621 }
12622
12623 static boolean canFallDown(struct PlayerInfo *player)
12624 {
12625   int jx = player->jx, jy = player->jy;
12626
12627   return (IN_LEV_FIELD(jx, jy + 1) &&
12628           (IS_FREE(jx, jy + 1) ||
12629            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12630           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12631           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12632 }
12633
12634 static boolean canPassField(int x, int y, int move_dir)
12635 {
12636   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12637   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12638   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12639   int nextx = x + dx;
12640   int nexty = y + dy;
12641   int element = Tile[x][y];
12642
12643   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12644           !CAN_MOVE(element) &&
12645           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12646           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12647           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12648 }
12649
12650 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12651 {
12652   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12653   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12654   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12655   int newx = x + dx;
12656   int newy = y + dy;
12657
12658   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12659           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12660           (IS_DIGGABLE(Tile[newx][newy]) ||
12661            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12662            canPassField(newx, newy, move_dir)));
12663 }
12664
12665 static void CheckGravityMovement(struct PlayerInfo *player)
12666 {
12667   if (player->gravity && !player->programmed_action)
12668   {
12669     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12670     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12671     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12672     int jx = player->jx, jy = player->jy;
12673     boolean player_is_moving_to_valid_field =
12674       (!player_is_snapping &&
12675        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12676         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12677     boolean player_can_fall_down = canFallDown(player);
12678
12679     if (player_can_fall_down &&
12680         !player_is_moving_to_valid_field)
12681       player->programmed_action = MV_DOWN;
12682   }
12683 }
12684
12685 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12686 {
12687   return CheckGravityMovement(player);
12688
12689   if (player->gravity && !player->programmed_action)
12690   {
12691     int jx = player->jx, jy = player->jy;
12692     boolean field_under_player_is_free =
12693       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12694     boolean player_is_standing_on_valid_field =
12695       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12696        (IS_WALKABLE(Tile[jx][jy]) &&
12697         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12698
12699     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12700       player->programmed_action = MV_DOWN;
12701   }
12702 }
12703
12704 /*
12705   MovePlayerOneStep()
12706   -----------------------------------------------------------------------------
12707   dx, dy:               direction (non-diagonal) to try to move the player to
12708   real_dx, real_dy:     direction as read from input device (can be diagonal)
12709 */
12710
12711 boolean MovePlayerOneStep(struct PlayerInfo *player,
12712                           int dx, int dy, int real_dx, int real_dy)
12713 {
12714   int jx = player->jx, jy = player->jy;
12715   int new_jx = jx + dx, new_jy = jy + dy;
12716   int can_move;
12717   boolean player_can_move = !player->cannot_move;
12718
12719   if (!player->active || (!dx && !dy))
12720     return MP_NO_ACTION;
12721
12722   player->MovDir = (dx < 0 ? MV_LEFT :
12723                     dx > 0 ? MV_RIGHT :
12724                     dy < 0 ? MV_UP :
12725                     dy > 0 ? MV_DOWN :  MV_NONE);
12726
12727   if (!IN_LEV_FIELD(new_jx, new_jy))
12728     return MP_NO_ACTION;
12729
12730   if (!player_can_move)
12731   {
12732     if (player->MovPos == 0)
12733     {
12734       player->is_moving = FALSE;
12735       player->is_digging = FALSE;
12736       player->is_collecting = FALSE;
12737       player->is_snapping = FALSE;
12738       player->is_pushing = FALSE;
12739     }
12740   }
12741
12742   if (!network.enabled && game.centered_player_nr == -1 &&
12743       !AllPlayersInSight(player, new_jx, new_jy))
12744     return MP_NO_ACTION;
12745
12746   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12747   if (can_move != MP_MOVING)
12748     return can_move;
12749
12750   // check if DigField() has caused relocation of the player
12751   if (player->jx != jx || player->jy != jy)
12752     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12753
12754   StorePlayer[jx][jy] = 0;
12755   player->last_jx = jx;
12756   player->last_jy = jy;
12757   player->jx = new_jx;
12758   player->jy = new_jy;
12759   StorePlayer[new_jx][new_jy] = player->element_nr;
12760
12761   if (player->move_delay_value_next != -1)
12762   {
12763     player->move_delay_value = player->move_delay_value_next;
12764     player->move_delay_value_next = -1;
12765   }
12766
12767   player->MovPos =
12768     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12769
12770   player->step_counter++;
12771
12772   PlayerVisit[jx][jy] = FrameCounter;
12773
12774   player->is_moving = TRUE;
12775
12776 #if 1
12777   // should better be called in MovePlayer(), but this breaks some tapes
12778   ScrollPlayer(player, SCROLL_INIT);
12779 #endif
12780
12781   return MP_MOVING;
12782 }
12783
12784 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12785 {
12786   int jx = player->jx, jy = player->jy;
12787   int old_jx = jx, old_jy = jy;
12788   int moved = MP_NO_ACTION;
12789
12790   if (!player->active)
12791     return FALSE;
12792
12793   if (!dx && !dy)
12794   {
12795     if (player->MovPos == 0)
12796     {
12797       player->is_moving = FALSE;
12798       player->is_digging = FALSE;
12799       player->is_collecting = FALSE;
12800       player->is_snapping = FALSE;
12801       player->is_pushing = FALSE;
12802     }
12803
12804     return FALSE;
12805   }
12806
12807   if (player->move_delay > 0)
12808     return FALSE;
12809
12810   player->move_delay = -1;              // set to "uninitialized" value
12811
12812   // store if player is automatically moved to next field
12813   player->is_auto_moving = (player->programmed_action != MV_NONE);
12814
12815   // remove the last programmed player action
12816   player->programmed_action = 0;
12817
12818   if (player->MovPos)
12819   {
12820     // should only happen if pre-1.2 tape recordings are played
12821     // this is only for backward compatibility
12822
12823     int original_move_delay_value = player->move_delay_value;
12824
12825 #if DEBUG
12826     Debug("game:playing:MovePlayer",
12827           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12828           tape.counter);
12829 #endif
12830
12831     // scroll remaining steps with finest movement resolution
12832     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12833
12834     while (player->MovPos)
12835     {
12836       ScrollPlayer(player, SCROLL_GO_ON);
12837       ScrollScreen(NULL, SCROLL_GO_ON);
12838
12839       AdvanceFrameAndPlayerCounters(player->index_nr);
12840
12841       DrawAllPlayers();
12842       BackToFront_WithFrameDelay(0);
12843     }
12844
12845     player->move_delay_value = original_move_delay_value;
12846   }
12847
12848   player->is_active = FALSE;
12849
12850   if (player->last_move_dir & MV_HORIZONTAL)
12851   {
12852     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12853       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12854   }
12855   else
12856   {
12857     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12858       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12859   }
12860
12861   if (!moved && !player->is_active)
12862   {
12863     player->is_moving = FALSE;
12864     player->is_digging = FALSE;
12865     player->is_collecting = FALSE;
12866     player->is_snapping = FALSE;
12867     player->is_pushing = FALSE;
12868   }
12869
12870   jx = player->jx;
12871   jy = player->jy;
12872
12873   if (moved & MP_MOVING && !ScreenMovPos &&
12874       (player->index_nr == game.centered_player_nr ||
12875        game.centered_player_nr == -1))
12876   {
12877     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12878
12879     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12880     {
12881       // actual player has left the screen -- scroll in that direction
12882       if (jx != old_jx)         // player has moved horizontally
12883         scroll_x += (jx - old_jx);
12884       else                      // player has moved vertically
12885         scroll_y += (jy - old_jy);
12886     }
12887     else
12888     {
12889       int offset_raw = game.scroll_delay_value;
12890
12891       if (jx != old_jx)         // player has moved horizontally
12892       {
12893         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12894         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12895         int new_scroll_x = jx - MIDPOSX + offset_x;
12896
12897         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12898             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12899           scroll_x = new_scroll_x;
12900
12901         // don't scroll over playfield boundaries
12902         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12903
12904         // don't scroll more than one field at a time
12905         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12906
12907         // don't scroll against the player's moving direction
12908         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12909             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12910           scroll_x = old_scroll_x;
12911       }
12912       else                      // player has moved vertically
12913       {
12914         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12915         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12916         int new_scroll_y = jy - MIDPOSY + offset_y;
12917
12918         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12919             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12920           scroll_y = new_scroll_y;
12921
12922         // don't scroll over playfield boundaries
12923         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12924
12925         // don't scroll more than one field at a time
12926         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12927
12928         // don't scroll against the player's moving direction
12929         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12930             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12931           scroll_y = old_scroll_y;
12932       }
12933     }
12934
12935     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12936     {
12937       if (!network.enabled && game.centered_player_nr == -1 &&
12938           !AllPlayersInVisibleScreen())
12939       {
12940         scroll_x = old_scroll_x;
12941         scroll_y = old_scroll_y;
12942       }
12943       else
12944       {
12945         ScrollScreen(player, SCROLL_INIT);
12946         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12947       }
12948     }
12949   }
12950
12951   player->StepFrame = 0;
12952
12953   if (moved & MP_MOVING)
12954   {
12955     if (old_jx != jx && old_jy == jy)
12956       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12957     else if (old_jx == jx && old_jy != jy)
12958       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12959
12960     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12961
12962     player->last_move_dir = player->MovDir;
12963     player->is_moving = TRUE;
12964     player->is_snapping = FALSE;
12965     player->is_switching = FALSE;
12966     player->is_dropping = FALSE;
12967     player->is_dropping_pressed = FALSE;
12968     player->drop_pressed_delay = 0;
12969
12970 #if 0
12971     // should better be called here than above, but this breaks some tapes
12972     ScrollPlayer(player, SCROLL_INIT);
12973 #endif
12974   }
12975   else
12976   {
12977     CheckGravityMovementWhenNotMoving(player);
12978
12979     player->is_moving = FALSE;
12980
12981     /* at this point, the player is allowed to move, but cannot move right now
12982        (e.g. because of something blocking the way) -- ensure that the player
12983        is also allowed to move in the next frame (in old versions before 3.1.1,
12984        the player was forced to wait again for eight frames before next try) */
12985
12986     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12987       player->move_delay = 0;   // allow direct movement in the next frame
12988   }
12989
12990   if (player->move_delay == -1)         // not yet initialized by DigField()
12991     player->move_delay = player->move_delay_value;
12992
12993   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12994   {
12995     TestIfPlayerTouchesBadThing(jx, jy);
12996     TestIfPlayerTouchesCustomElement(jx, jy);
12997   }
12998
12999   if (!player->active)
13000     RemovePlayer(player);
13001
13002   return moved;
13003 }
13004
13005 void ScrollPlayer(struct PlayerInfo *player, int mode)
13006 {
13007   int jx = player->jx, jy = player->jy;
13008   int last_jx = player->last_jx, last_jy = player->last_jy;
13009   int move_stepsize = TILEX / player->move_delay_value;
13010
13011   if (!player->active)
13012     return;
13013
13014   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13015     return;
13016
13017   if (mode == SCROLL_INIT)
13018   {
13019     player->actual_frame_counter = FrameCounter;
13020     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13021
13022     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13023         Tile[last_jx][last_jy] == EL_EMPTY)
13024     {
13025       int last_field_block_delay = 0;   // start with no blocking at all
13026       int block_delay_adjustment = player->block_delay_adjustment;
13027
13028       // if player blocks last field, add delay for exactly one move
13029       if (player->block_last_field)
13030       {
13031         last_field_block_delay += player->move_delay_value;
13032
13033         // when blocking enabled, prevent moving up despite gravity
13034         if (player->gravity && player->MovDir == MV_UP)
13035           block_delay_adjustment = -1;
13036       }
13037
13038       // add block delay adjustment (also possible when not blocking)
13039       last_field_block_delay += block_delay_adjustment;
13040
13041       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13042       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13043     }
13044
13045     if (player->MovPos != 0)    // player has not yet reached destination
13046       return;
13047   }
13048   else if (!FrameReached(&player->actual_frame_counter, 1))
13049     return;
13050
13051   if (player->MovPos != 0)
13052   {
13053     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13054     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13055
13056     // before DrawPlayer() to draw correct player graphic for this case
13057     if (player->MovPos == 0)
13058       CheckGravityMovement(player);
13059   }
13060
13061   if (player->MovPos == 0)      // player reached destination field
13062   {
13063     if (player->move_delay_reset_counter > 0)
13064     {
13065       player->move_delay_reset_counter--;
13066
13067       if (player->move_delay_reset_counter == 0)
13068       {
13069         // continue with normal speed after quickly moving through gate
13070         HALVE_PLAYER_SPEED(player);
13071
13072         // be able to make the next move without delay
13073         player->move_delay = 0;
13074       }
13075     }
13076
13077     player->last_jx = jx;
13078     player->last_jy = jy;
13079
13080     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13081         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13082         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13083         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13084         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13085         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13086         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13087         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13088     {
13089       ExitPlayer(player);
13090
13091       if (game.players_still_needed == 0 &&
13092           (game.friends_still_needed == 0 ||
13093            IS_SP_ELEMENT(Tile[jx][jy])))
13094         LevelSolved();
13095     }
13096
13097     // this breaks one level: "machine", level 000
13098     {
13099       int move_direction = player->MovDir;
13100       int enter_side = MV_DIR_OPPOSITE(move_direction);
13101       int leave_side = move_direction;
13102       int old_jx = last_jx;
13103       int old_jy = last_jy;
13104       int old_element = Tile[old_jx][old_jy];
13105       int new_element = Tile[jx][jy];
13106
13107       if (IS_CUSTOM_ELEMENT(old_element))
13108         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13109                                    CE_LEFT_BY_PLAYER,
13110                                    player->index_bit, leave_side);
13111
13112       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13113                                           CE_PLAYER_LEAVES_X,
13114                                           player->index_bit, leave_side);
13115
13116       if (IS_CUSTOM_ELEMENT(new_element))
13117         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13118                                    player->index_bit, enter_side);
13119
13120       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13121                                           CE_PLAYER_ENTERS_X,
13122                                           player->index_bit, enter_side);
13123
13124       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13125                                         CE_MOVE_OF_X, move_direction);
13126     }
13127
13128     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13129     {
13130       TestIfPlayerTouchesBadThing(jx, jy);
13131       TestIfPlayerTouchesCustomElement(jx, jy);
13132
13133       /* needed because pushed element has not yet reached its destination,
13134          so it would trigger a change event at its previous field location */
13135       if (!player->is_pushing)
13136         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13137
13138       if (level.finish_dig_collect &&
13139           (player->is_digging || player->is_collecting))
13140       {
13141         int last_element = player->last_removed_element;
13142         int move_direction = player->MovDir;
13143         int enter_side = MV_DIR_OPPOSITE(move_direction);
13144         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13145                             CE_PLAYER_COLLECTS_X);
13146
13147         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13148                                             player->index_bit, enter_side);
13149
13150         player->last_removed_element = EL_UNDEFINED;
13151       }
13152
13153       if (!player->active)
13154         RemovePlayer(player);
13155     }
13156
13157     if (level.use_step_counter)
13158     {
13159       int i;
13160
13161       TimePlayed++;
13162
13163       if (TimeLeft > 0)
13164       {
13165         TimeLeft--;
13166
13167         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13168           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13169
13170         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13171
13172         DisplayGameControlValues();
13173
13174         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13175           for (i = 0; i < MAX_PLAYERS; i++)
13176             KillPlayer(&stored_player[i]);
13177       }
13178       else if (game.no_time_limit && !game.all_players_gone)
13179       {
13180         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13181
13182         DisplayGameControlValues();
13183       }
13184     }
13185
13186     if (tape.single_step && tape.recording && !tape.pausing &&
13187         !player->programmed_action)
13188       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13189
13190     if (!player->programmed_action)
13191       CheckSaveEngineSnapshot(player);
13192   }
13193 }
13194
13195 void ScrollScreen(struct PlayerInfo *player, int mode)
13196 {
13197   static unsigned int screen_frame_counter = 0;
13198
13199   if (mode == SCROLL_INIT)
13200   {
13201     // set scrolling step size according to actual player's moving speed
13202     ScrollStepSize = TILEX / player->move_delay_value;
13203
13204     screen_frame_counter = FrameCounter;
13205     ScreenMovDir = player->MovDir;
13206     ScreenMovPos = player->MovPos;
13207     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13208     return;
13209   }
13210   else if (!FrameReached(&screen_frame_counter, 1))
13211     return;
13212
13213   if (ScreenMovPos)
13214   {
13215     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13216     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13217     redraw_mask |= REDRAW_FIELD;
13218   }
13219   else
13220     ScreenMovDir = MV_NONE;
13221 }
13222
13223 void TestIfPlayerTouchesCustomElement(int x, int y)
13224 {
13225   static int xy[4][2] =
13226   {
13227     { 0, -1 },
13228     { -1, 0 },
13229     { +1, 0 },
13230     { 0, +1 }
13231   };
13232   static int trigger_sides[4][2] =
13233   {
13234     // center side       border side
13235     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13236     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13237     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13238     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13239   };
13240   static int touch_dir[4] =
13241   {
13242     MV_LEFT | MV_RIGHT,
13243     MV_UP   | MV_DOWN,
13244     MV_UP   | MV_DOWN,
13245     MV_LEFT | MV_RIGHT
13246   };
13247   int center_element = Tile[x][y];      // should always be non-moving!
13248   int i;
13249
13250   for (i = 0; i < NUM_DIRECTIONS; i++)
13251   {
13252     int xx = x + xy[i][0];
13253     int yy = y + xy[i][1];
13254     int center_side = trigger_sides[i][0];
13255     int border_side = trigger_sides[i][1];
13256     int border_element;
13257
13258     if (!IN_LEV_FIELD(xx, yy))
13259       continue;
13260
13261     if (IS_PLAYER(x, y))                // player found at center element
13262     {
13263       struct PlayerInfo *player = PLAYERINFO(x, y);
13264
13265       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13266         border_element = Tile[xx][yy];          // may be moving!
13267       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13268         border_element = Tile[xx][yy];
13269       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13270         border_element = MovingOrBlocked2Element(xx, yy);
13271       else
13272         continue;               // center and border element do not touch
13273
13274       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13275                                  player->index_bit, border_side);
13276       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13277                                           CE_PLAYER_TOUCHES_X,
13278                                           player->index_bit, border_side);
13279
13280       {
13281         /* use player element that is initially defined in the level playfield,
13282            not the player element that corresponds to the runtime player number
13283            (example: a level that contains EL_PLAYER_3 as the only player would
13284            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13285         int player_element = PLAYERINFO(x, y)->initial_element;
13286
13287         CheckElementChangeBySide(xx, yy, border_element, player_element,
13288                                  CE_TOUCHING_X, border_side);
13289       }
13290     }
13291     else if (IS_PLAYER(xx, yy))         // player found at border element
13292     {
13293       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13294
13295       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13296       {
13297         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13298           continue;             // center and border element do not touch
13299       }
13300
13301       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13302                                  player->index_bit, center_side);
13303       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13304                                           CE_PLAYER_TOUCHES_X,
13305                                           player->index_bit, center_side);
13306
13307       {
13308         /* use player element that is initially defined in the level playfield,
13309            not the player element that corresponds to the runtime player number
13310            (example: a level that contains EL_PLAYER_3 as the only player would
13311            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13312         int player_element = PLAYERINFO(xx, yy)->initial_element;
13313
13314         CheckElementChangeBySide(x, y, center_element, player_element,
13315                                  CE_TOUCHING_X, center_side);
13316       }
13317
13318       break;
13319     }
13320   }
13321 }
13322
13323 void TestIfElementTouchesCustomElement(int x, int y)
13324 {
13325   static int xy[4][2] =
13326   {
13327     { 0, -1 },
13328     { -1, 0 },
13329     { +1, 0 },
13330     { 0, +1 }
13331   };
13332   static int trigger_sides[4][2] =
13333   {
13334     // center side      border side
13335     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13336     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13337     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13338     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13339   };
13340   static int touch_dir[4] =
13341   {
13342     MV_LEFT | MV_RIGHT,
13343     MV_UP   | MV_DOWN,
13344     MV_UP   | MV_DOWN,
13345     MV_LEFT | MV_RIGHT
13346   };
13347   boolean change_center_element = FALSE;
13348   int center_element = Tile[x][y];      // should always be non-moving!
13349   int border_element_old[NUM_DIRECTIONS];
13350   int i;
13351
13352   for (i = 0; i < NUM_DIRECTIONS; i++)
13353   {
13354     int xx = x + xy[i][0];
13355     int yy = y + xy[i][1];
13356     int border_element;
13357
13358     border_element_old[i] = -1;
13359
13360     if (!IN_LEV_FIELD(xx, yy))
13361       continue;
13362
13363     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13364       border_element = Tile[xx][yy];    // may be moving!
13365     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13366       border_element = Tile[xx][yy];
13367     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13368       border_element = MovingOrBlocked2Element(xx, yy);
13369     else
13370       continue;                 // center and border element do not touch
13371
13372     border_element_old[i] = border_element;
13373   }
13374
13375   for (i = 0; i < NUM_DIRECTIONS; i++)
13376   {
13377     int xx = x + xy[i][0];
13378     int yy = y + xy[i][1];
13379     int center_side = trigger_sides[i][0];
13380     int border_element = border_element_old[i];
13381
13382     if (border_element == -1)
13383       continue;
13384
13385     // check for change of border element
13386     CheckElementChangeBySide(xx, yy, border_element, center_element,
13387                              CE_TOUCHING_X, center_side);
13388
13389     // (center element cannot be player, so we dont have to check this here)
13390   }
13391
13392   for (i = 0; i < NUM_DIRECTIONS; i++)
13393   {
13394     int xx = x + xy[i][0];
13395     int yy = y + xy[i][1];
13396     int border_side = trigger_sides[i][1];
13397     int border_element = border_element_old[i];
13398
13399     if (border_element == -1)
13400       continue;
13401
13402     // check for change of center element (but change it only once)
13403     if (!change_center_element)
13404       change_center_element =
13405         CheckElementChangeBySide(x, y, center_element, border_element,
13406                                  CE_TOUCHING_X, border_side);
13407
13408     if (IS_PLAYER(xx, yy))
13409     {
13410       /* use player element that is initially defined in the level playfield,
13411          not the player element that corresponds to the runtime player number
13412          (example: a level that contains EL_PLAYER_3 as the only player would
13413          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13414       int player_element = PLAYERINFO(xx, yy)->initial_element;
13415
13416       CheckElementChangeBySide(x, y, center_element, player_element,
13417                                CE_TOUCHING_X, border_side);
13418     }
13419   }
13420 }
13421
13422 void TestIfElementHitsCustomElement(int x, int y, int direction)
13423 {
13424   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13425   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13426   int hitx = x + dx, hity = y + dy;
13427   int hitting_element = Tile[x][y];
13428   int touched_element;
13429
13430   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13431     return;
13432
13433   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13434                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13435
13436   if (IN_LEV_FIELD(hitx, hity))
13437   {
13438     int opposite_direction = MV_DIR_OPPOSITE(direction);
13439     int hitting_side = direction;
13440     int touched_side = opposite_direction;
13441     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13442                           MovDir[hitx][hity] != direction ||
13443                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13444
13445     object_hit = TRUE;
13446
13447     if (object_hit)
13448     {
13449       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13450                                CE_HITTING_X, touched_side);
13451
13452       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13453                                CE_HIT_BY_X, hitting_side);
13454
13455       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13456                                CE_HIT_BY_SOMETHING, opposite_direction);
13457
13458       if (IS_PLAYER(hitx, hity))
13459       {
13460         /* use player element that is initially defined in the level playfield,
13461            not the player element that corresponds to the runtime player number
13462            (example: a level that contains EL_PLAYER_3 as the only player would
13463            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13464         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13465
13466         CheckElementChangeBySide(x, y, hitting_element, player_element,
13467                                  CE_HITTING_X, touched_side);
13468       }
13469     }
13470   }
13471
13472   // "hitting something" is also true when hitting the playfield border
13473   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13474                            CE_HITTING_SOMETHING, direction);
13475 }
13476
13477 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13478 {
13479   int i, kill_x = -1, kill_y = -1;
13480
13481   int bad_element = -1;
13482   static int test_xy[4][2] =
13483   {
13484     { 0, -1 },
13485     { -1, 0 },
13486     { +1, 0 },
13487     { 0, +1 }
13488   };
13489   static int test_dir[4] =
13490   {
13491     MV_UP,
13492     MV_LEFT,
13493     MV_RIGHT,
13494     MV_DOWN
13495   };
13496
13497   for (i = 0; i < NUM_DIRECTIONS; i++)
13498   {
13499     int test_x, test_y, test_move_dir, test_element;
13500
13501     test_x = good_x + test_xy[i][0];
13502     test_y = good_y + test_xy[i][1];
13503
13504     if (!IN_LEV_FIELD(test_x, test_y))
13505       continue;
13506
13507     test_move_dir =
13508       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13509
13510     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13511
13512     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13513        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13514     */
13515     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13516         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13517     {
13518       kill_x = test_x;
13519       kill_y = test_y;
13520       bad_element = test_element;
13521
13522       break;
13523     }
13524   }
13525
13526   if (kill_x != -1 || kill_y != -1)
13527   {
13528     if (IS_PLAYER(good_x, good_y))
13529     {
13530       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13531
13532       if (player->shield_deadly_time_left > 0 &&
13533           !IS_INDESTRUCTIBLE(bad_element))
13534         Bang(kill_x, kill_y);
13535       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13536         KillPlayer(player);
13537     }
13538     else
13539       Bang(good_x, good_y);
13540   }
13541 }
13542
13543 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13544 {
13545   int i, kill_x = -1, kill_y = -1;
13546   int bad_element = Tile[bad_x][bad_y];
13547   static int test_xy[4][2] =
13548   {
13549     { 0, -1 },
13550     { -1, 0 },
13551     { +1, 0 },
13552     { 0, +1 }
13553   };
13554   static int touch_dir[4] =
13555   {
13556     MV_LEFT | MV_RIGHT,
13557     MV_UP   | MV_DOWN,
13558     MV_UP   | MV_DOWN,
13559     MV_LEFT | MV_RIGHT
13560   };
13561   static int test_dir[4] =
13562   {
13563     MV_UP,
13564     MV_LEFT,
13565     MV_RIGHT,
13566     MV_DOWN
13567   };
13568
13569   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13570     return;
13571
13572   for (i = 0; i < NUM_DIRECTIONS; i++)
13573   {
13574     int test_x, test_y, test_move_dir, test_element;
13575
13576     test_x = bad_x + test_xy[i][0];
13577     test_y = bad_y + test_xy[i][1];
13578
13579     if (!IN_LEV_FIELD(test_x, test_y))
13580       continue;
13581
13582     test_move_dir =
13583       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13584
13585     test_element = Tile[test_x][test_y];
13586
13587     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13588        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13589     */
13590     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13591         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13592     {
13593       // good thing is player or penguin that does not move away
13594       if (IS_PLAYER(test_x, test_y))
13595       {
13596         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13597
13598         if (bad_element == EL_ROBOT && player->is_moving)
13599           continue;     // robot does not kill player if he is moving
13600
13601         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13602         {
13603           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13604             continue;           // center and border element do not touch
13605         }
13606
13607         kill_x = test_x;
13608         kill_y = test_y;
13609
13610         break;
13611       }
13612       else if (test_element == EL_PENGUIN)
13613       {
13614         kill_x = test_x;
13615         kill_y = test_y;
13616
13617         break;
13618       }
13619     }
13620   }
13621
13622   if (kill_x != -1 || kill_y != -1)
13623   {
13624     if (IS_PLAYER(kill_x, kill_y))
13625     {
13626       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13627
13628       if (player->shield_deadly_time_left > 0 &&
13629           !IS_INDESTRUCTIBLE(bad_element))
13630         Bang(bad_x, bad_y);
13631       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13632         KillPlayer(player);
13633     }
13634     else
13635       Bang(kill_x, kill_y);
13636   }
13637 }
13638
13639 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13640 {
13641   int bad_element = Tile[bad_x][bad_y];
13642   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13643   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13644   int test_x = bad_x + dx, test_y = bad_y + dy;
13645   int test_move_dir, test_element;
13646   int kill_x = -1, kill_y = -1;
13647
13648   if (!IN_LEV_FIELD(test_x, test_y))
13649     return;
13650
13651   test_move_dir =
13652     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13653
13654   test_element = Tile[test_x][test_y];
13655
13656   if (test_move_dir != bad_move_dir)
13657   {
13658     // good thing can be player or penguin that does not move away
13659     if (IS_PLAYER(test_x, test_y))
13660     {
13661       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13662
13663       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13664          player as being hit when he is moving towards the bad thing, because
13665          the "get hit by" condition would be lost after the player stops) */
13666       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13667         return;         // player moves away from bad thing
13668
13669       kill_x = test_x;
13670       kill_y = test_y;
13671     }
13672     else if (test_element == EL_PENGUIN)
13673     {
13674       kill_x = test_x;
13675       kill_y = test_y;
13676     }
13677   }
13678
13679   if (kill_x != -1 || kill_y != -1)
13680   {
13681     if (IS_PLAYER(kill_x, kill_y))
13682     {
13683       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13684
13685       if (player->shield_deadly_time_left > 0 &&
13686           !IS_INDESTRUCTIBLE(bad_element))
13687         Bang(bad_x, bad_y);
13688       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13689         KillPlayer(player);
13690     }
13691     else
13692       Bang(kill_x, kill_y);
13693   }
13694 }
13695
13696 void TestIfPlayerTouchesBadThing(int x, int y)
13697 {
13698   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13699 }
13700
13701 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13702 {
13703   TestIfGoodThingHitsBadThing(x, y, move_dir);
13704 }
13705
13706 void TestIfBadThingTouchesPlayer(int x, int y)
13707 {
13708   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13709 }
13710
13711 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13712 {
13713   TestIfBadThingHitsGoodThing(x, y, move_dir);
13714 }
13715
13716 void TestIfFriendTouchesBadThing(int x, int y)
13717 {
13718   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13719 }
13720
13721 void TestIfBadThingTouchesFriend(int x, int y)
13722 {
13723   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13724 }
13725
13726 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13727 {
13728   int i, kill_x = bad_x, kill_y = bad_y;
13729   static int xy[4][2] =
13730   {
13731     { 0, -1 },
13732     { -1, 0 },
13733     { +1, 0 },
13734     { 0, +1 }
13735   };
13736
13737   for (i = 0; i < NUM_DIRECTIONS; i++)
13738   {
13739     int x, y, element;
13740
13741     x = bad_x + xy[i][0];
13742     y = bad_y + xy[i][1];
13743     if (!IN_LEV_FIELD(x, y))
13744       continue;
13745
13746     element = Tile[x][y];
13747     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13748         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13749     {
13750       kill_x = x;
13751       kill_y = y;
13752       break;
13753     }
13754   }
13755
13756   if (kill_x != bad_x || kill_y != bad_y)
13757     Bang(bad_x, bad_y);
13758 }
13759
13760 void KillPlayer(struct PlayerInfo *player)
13761 {
13762   int jx = player->jx, jy = player->jy;
13763
13764   if (!player->active)
13765     return;
13766
13767 #if 0
13768   Debug("game:playing:KillPlayer",
13769         "0: killed == %d, active == %d, reanimated == %d",
13770         player->killed, player->active, player->reanimated);
13771 #endif
13772
13773   /* the following code was introduced to prevent an infinite loop when calling
13774      -> Bang()
13775      -> CheckTriggeredElementChangeExt()
13776      -> ExecuteCustomElementAction()
13777      -> KillPlayer()
13778      -> (infinitely repeating the above sequence of function calls)
13779      which occurs when killing the player while having a CE with the setting
13780      "kill player X when explosion of <player X>"; the solution using a new
13781      field "player->killed" was chosen for backwards compatibility, although
13782      clever use of the fields "player->active" etc. would probably also work */
13783 #if 1
13784   if (player->killed)
13785     return;
13786 #endif
13787
13788   player->killed = TRUE;
13789
13790   // remove accessible field at the player's position
13791   Tile[jx][jy] = EL_EMPTY;
13792
13793   // deactivate shield (else Bang()/Explode() would not work right)
13794   player->shield_normal_time_left = 0;
13795   player->shield_deadly_time_left = 0;
13796
13797 #if 0
13798   Debug("game:playing:KillPlayer",
13799         "1: killed == %d, active == %d, reanimated == %d",
13800         player->killed, player->active, player->reanimated);
13801 #endif
13802
13803   Bang(jx, jy);
13804
13805 #if 0
13806   Debug("game:playing:KillPlayer",
13807         "2: killed == %d, active == %d, reanimated == %d",
13808         player->killed, player->active, player->reanimated);
13809 #endif
13810
13811   if (player->reanimated)       // killed player may have been reanimated
13812     player->killed = player->reanimated = FALSE;
13813   else
13814     BuryPlayer(player);
13815 }
13816
13817 static void KillPlayerUnlessEnemyProtected(int x, int y)
13818 {
13819   if (!PLAYER_ENEMY_PROTECTED(x, y))
13820     KillPlayer(PLAYERINFO(x, y));
13821 }
13822
13823 static void KillPlayerUnlessExplosionProtected(int x, int y)
13824 {
13825   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13826     KillPlayer(PLAYERINFO(x, y));
13827 }
13828
13829 void BuryPlayer(struct PlayerInfo *player)
13830 {
13831   int jx = player->jx, jy = player->jy;
13832
13833   if (!player->active)
13834     return;
13835
13836   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13837   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13838
13839   RemovePlayer(player);
13840
13841   player->buried = TRUE;
13842
13843   if (game.all_players_gone)
13844     game.GameOver = TRUE;
13845 }
13846
13847 void RemovePlayer(struct PlayerInfo *player)
13848 {
13849   int jx = player->jx, jy = player->jy;
13850   int i, found = FALSE;
13851
13852   player->present = FALSE;
13853   player->active = FALSE;
13854
13855   // required for some CE actions (even if the player is not active anymore)
13856   player->MovPos = 0;
13857
13858   if (!ExplodeField[jx][jy])
13859     StorePlayer[jx][jy] = 0;
13860
13861   if (player->is_moving)
13862     TEST_DrawLevelField(player->last_jx, player->last_jy);
13863
13864   for (i = 0; i < MAX_PLAYERS; i++)
13865     if (stored_player[i].active)
13866       found = TRUE;
13867
13868   if (!found)
13869   {
13870     game.all_players_gone = TRUE;
13871     game.GameOver = TRUE;
13872   }
13873
13874   game.exit_x = game.robot_wheel_x = jx;
13875   game.exit_y = game.robot_wheel_y = jy;
13876 }
13877
13878 void ExitPlayer(struct PlayerInfo *player)
13879 {
13880   DrawPlayer(player);   // needed here only to cleanup last field
13881   RemovePlayer(player);
13882
13883   if (game.players_still_needed > 0)
13884     game.players_still_needed--;
13885 }
13886
13887 static void SetFieldForSnapping(int x, int y, int element, int direction,
13888                                 int player_index_bit)
13889 {
13890   struct ElementInfo *ei = &element_info[element];
13891   int direction_bit = MV_DIR_TO_BIT(direction);
13892   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13893   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13894                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13895
13896   Tile[x][y] = EL_ELEMENT_SNAPPING;
13897   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13898   MovDir[x][y] = direction;
13899   Store[x][y] = element;
13900   Store2[x][y] = player_index_bit;
13901
13902   ResetGfxAnimation(x, y);
13903
13904   GfxElement[x][y] = element;
13905   GfxAction[x][y] = action;
13906   GfxDir[x][y] = direction;
13907   GfxFrame[x][y] = -1;
13908 }
13909
13910 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13911                                    int player_index_bit)
13912 {
13913   TestIfElementTouchesCustomElement(x, y);      // for empty space
13914
13915   if (level.finish_dig_collect)
13916   {
13917     int dig_side = MV_DIR_OPPOSITE(direction);
13918
13919     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13920                                         player_index_bit, dig_side);
13921   }
13922 }
13923
13924 /*
13925   =============================================================================
13926   checkDiagonalPushing()
13927   -----------------------------------------------------------------------------
13928   check if diagonal input device direction results in pushing of object
13929   (by checking if the alternative direction is walkable, diggable, ...)
13930   =============================================================================
13931 */
13932
13933 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13934                                     int x, int y, int real_dx, int real_dy)
13935 {
13936   int jx, jy, dx, dy, xx, yy;
13937
13938   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13939     return TRUE;
13940
13941   // diagonal direction: check alternative direction
13942   jx = player->jx;
13943   jy = player->jy;
13944   dx = x - jx;
13945   dy = y - jy;
13946   xx = jx + (dx == 0 ? real_dx : 0);
13947   yy = jy + (dy == 0 ? real_dy : 0);
13948
13949   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13950 }
13951
13952 /*
13953   =============================================================================
13954   DigField()
13955   -----------------------------------------------------------------------------
13956   x, y:                 field next to player (non-diagonal) to try to dig to
13957   real_dx, real_dy:     direction as read from input device (can be diagonal)
13958   =============================================================================
13959 */
13960
13961 static int DigField(struct PlayerInfo *player,
13962                     int oldx, int oldy, int x, int y,
13963                     int real_dx, int real_dy, int mode)
13964 {
13965   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13966   boolean player_was_pushing = player->is_pushing;
13967   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13968   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13969   int jx = oldx, jy = oldy;
13970   int dx = x - jx, dy = y - jy;
13971   int nextx = x + dx, nexty = y + dy;
13972   int move_direction = (dx == -1 ? MV_LEFT  :
13973                         dx == +1 ? MV_RIGHT :
13974                         dy == -1 ? MV_UP    :
13975                         dy == +1 ? MV_DOWN  : MV_NONE);
13976   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13977   int dig_side = MV_DIR_OPPOSITE(move_direction);
13978   int old_element = Tile[jx][jy];
13979   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13980   int collect_count;
13981
13982   if (is_player)                // function can also be called by EL_PENGUIN
13983   {
13984     if (player->MovPos == 0)
13985     {
13986       player->is_digging = FALSE;
13987       player->is_collecting = FALSE;
13988     }
13989
13990     if (player->MovPos == 0)    // last pushing move finished
13991       player->is_pushing = FALSE;
13992
13993     if (mode == DF_NO_PUSH)     // player just stopped pushing
13994     {
13995       player->is_switching = FALSE;
13996       player->push_delay = -1;
13997
13998       return MP_NO_ACTION;
13999     }
14000   }
14001
14002   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14003     old_element = Back[jx][jy];
14004
14005   // in case of element dropped at player position, check background
14006   else if (Back[jx][jy] != EL_EMPTY &&
14007            game.engine_version >= VERSION_IDENT(2,2,0,0))
14008     old_element = Back[jx][jy];
14009
14010   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14011     return MP_NO_ACTION;        // field has no opening in this direction
14012
14013   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14014     return MP_NO_ACTION;        // field has no opening in this direction
14015
14016   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14017   {
14018     SplashAcid(x, y);
14019
14020     Tile[jx][jy] = player->artwork_element;
14021     InitMovingField(jx, jy, MV_DOWN);
14022     Store[jx][jy] = EL_ACID;
14023     ContinueMoving(jx, jy);
14024     BuryPlayer(player);
14025
14026     return MP_DONT_RUN_INTO;
14027   }
14028
14029   if (player_can_move && DONT_RUN_INTO(element))
14030   {
14031     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14032
14033     return MP_DONT_RUN_INTO;
14034   }
14035
14036   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14037     return MP_NO_ACTION;
14038
14039   collect_count = element_info[element].collect_count_initial;
14040
14041   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14042     return MP_NO_ACTION;
14043
14044   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14045     player_can_move = player_can_move_or_snap;
14046
14047   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14048       game.engine_version >= VERSION_IDENT(2,2,0,0))
14049   {
14050     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14051                                player->index_bit, dig_side);
14052     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14053                                         player->index_bit, dig_side);
14054
14055     if (element == EL_DC_LANDMINE)
14056       Bang(x, y);
14057
14058     if (Tile[x][y] != element)          // field changed by snapping
14059       return MP_ACTION;
14060
14061     return MP_NO_ACTION;
14062   }
14063
14064   if (player->gravity && is_player && !player->is_auto_moving &&
14065       canFallDown(player) && move_direction != MV_DOWN &&
14066       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14067     return MP_NO_ACTION;        // player cannot walk here due to gravity
14068
14069   if (player_can_move &&
14070       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14071   {
14072     int sound_element = SND_ELEMENT(element);
14073     int sound_action = ACTION_WALKING;
14074
14075     if (IS_RND_GATE(element))
14076     {
14077       if (!player->key[RND_GATE_NR(element)])
14078         return MP_NO_ACTION;
14079     }
14080     else if (IS_RND_GATE_GRAY(element))
14081     {
14082       if (!player->key[RND_GATE_GRAY_NR(element)])
14083         return MP_NO_ACTION;
14084     }
14085     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14086     {
14087       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14088         return MP_NO_ACTION;
14089     }
14090     else if (element == EL_EXIT_OPEN ||
14091              element == EL_EM_EXIT_OPEN ||
14092              element == EL_EM_EXIT_OPENING ||
14093              element == EL_STEEL_EXIT_OPEN ||
14094              element == EL_EM_STEEL_EXIT_OPEN ||
14095              element == EL_EM_STEEL_EXIT_OPENING ||
14096              element == EL_SP_EXIT_OPEN ||
14097              element == EL_SP_EXIT_OPENING)
14098     {
14099       sound_action = ACTION_PASSING;    // player is passing exit
14100     }
14101     else if (element == EL_EMPTY)
14102     {
14103       sound_action = ACTION_MOVING;             // nothing to walk on
14104     }
14105
14106     // play sound from background or player, whatever is available
14107     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14108       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14109     else
14110       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14111   }
14112   else if (player_can_move &&
14113            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14114   {
14115     if (!ACCESS_FROM(element, opposite_direction))
14116       return MP_NO_ACTION;      // field not accessible from this direction
14117
14118     if (CAN_MOVE(element))      // only fixed elements can be passed!
14119       return MP_NO_ACTION;
14120
14121     if (IS_EM_GATE(element))
14122     {
14123       if (!player->key[EM_GATE_NR(element)])
14124         return MP_NO_ACTION;
14125     }
14126     else if (IS_EM_GATE_GRAY(element))
14127     {
14128       if (!player->key[EM_GATE_GRAY_NR(element)])
14129         return MP_NO_ACTION;
14130     }
14131     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14132     {
14133       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14134         return MP_NO_ACTION;
14135     }
14136     else if (IS_EMC_GATE(element))
14137     {
14138       if (!player->key[EMC_GATE_NR(element)])
14139         return MP_NO_ACTION;
14140     }
14141     else if (IS_EMC_GATE_GRAY(element))
14142     {
14143       if (!player->key[EMC_GATE_GRAY_NR(element)])
14144         return MP_NO_ACTION;
14145     }
14146     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14147     {
14148       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14149         return MP_NO_ACTION;
14150     }
14151     else if (element == EL_DC_GATE_WHITE ||
14152              element == EL_DC_GATE_WHITE_GRAY ||
14153              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14154     {
14155       if (player->num_white_keys == 0)
14156         return MP_NO_ACTION;
14157
14158       player->num_white_keys--;
14159     }
14160     else if (IS_SP_PORT(element))
14161     {
14162       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14163           element == EL_SP_GRAVITY_PORT_RIGHT ||
14164           element == EL_SP_GRAVITY_PORT_UP ||
14165           element == EL_SP_GRAVITY_PORT_DOWN)
14166         player->gravity = !player->gravity;
14167       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14168                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14169                element == EL_SP_GRAVITY_ON_PORT_UP ||
14170                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14171         player->gravity = TRUE;
14172       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14173                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14174                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14175                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14176         player->gravity = FALSE;
14177     }
14178
14179     // automatically move to the next field with double speed
14180     player->programmed_action = move_direction;
14181
14182     if (player->move_delay_reset_counter == 0)
14183     {
14184       player->move_delay_reset_counter = 2;     // two double speed steps
14185
14186       DOUBLE_PLAYER_SPEED(player);
14187     }
14188
14189     PlayLevelSoundAction(x, y, ACTION_PASSING);
14190   }
14191   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14192   {
14193     RemoveField(x, y);
14194
14195     if (mode != DF_SNAP)
14196     {
14197       GfxElement[x][y] = GFX_ELEMENT(element);
14198       player->is_digging = TRUE;
14199     }
14200
14201     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14202
14203     // use old behaviour for old levels (digging)
14204     if (!level.finish_dig_collect)
14205     {
14206       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14207                                           player->index_bit, dig_side);
14208
14209       // if digging triggered player relocation, finish digging tile
14210       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14211         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14212     }
14213
14214     if (mode == DF_SNAP)
14215     {
14216       if (level.block_snap_field)
14217         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14218       else
14219         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14220
14221       // use old behaviour for old levels (snapping)
14222       if (!level.finish_dig_collect)
14223         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14224                                             player->index_bit, dig_side);
14225     }
14226   }
14227   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14228   {
14229     RemoveField(x, y);
14230
14231     if (is_player && mode != DF_SNAP)
14232     {
14233       GfxElement[x][y] = element;
14234       player->is_collecting = TRUE;
14235     }
14236
14237     if (element == EL_SPEED_PILL)
14238     {
14239       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14240     }
14241     else if (element == EL_EXTRA_TIME && level.time > 0)
14242     {
14243       TimeLeft += level.extra_time;
14244
14245       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14246
14247       DisplayGameControlValues();
14248     }
14249     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14250     {
14251       player->shield_normal_time_left += level.shield_normal_time;
14252       if (element == EL_SHIELD_DEADLY)
14253         player->shield_deadly_time_left += level.shield_deadly_time;
14254     }
14255     else if (element == EL_DYNAMITE ||
14256              element == EL_EM_DYNAMITE ||
14257              element == EL_SP_DISK_RED)
14258     {
14259       if (player->inventory_size < MAX_INVENTORY_SIZE)
14260         player->inventory_element[player->inventory_size++] = element;
14261
14262       DrawGameDoorValues();
14263     }
14264     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14265     {
14266       player->dynabomb_count++;
14267       player->dynabombs_left++;
14268     }
14269     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14270     {
14271       player->dynabomb_size++;
14272     }
14273     else if (element == EL_DYNABOMB_INCREASE_POWER)
14274     {
14275       player->dynabomb_xl = TRUE;
14276     }
14277     else if (IS_KEY(element))
14278     {
14279       player->key[KEY_NR(element)] = TRUE;
14280
14281       DrawGameDoorValues();
14282     }
14283     else if (element == EL_DC_KEY_WHITE)
14284     {
14285       player->num_white_keys++;
14286
14287       // display white keys?
14288       // DrawGameDoorValues();
14289     }
14290     else if (IS_ENVELOPE(element))
14291     {
14292       player->show_envelope = element;
14293     }
14294     else if (element == EL_EMC_LENSES)
14295     {
14296       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14297
14298       RedrawAllInvisibleElementsForLenses();
14299     }
14300     else if (element == EL_EMC_MAGNIFIER)
14301     {
14302       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14303
14304       RedrawAllInvisibleElementsForMagnifier();
14305     }
14306     else if (IS_DROPPABLE(element) ||
14307              IS_THROWABLE(element))     // can be collected and dropped
14308     {
14309       int i;
14310
14311       if (collect_count == 0)
14312         player->inventory_infinite_element = element;
14313       else
14314         for (i = 0; i < collect_count; i++)
14315           if (player->inventory_size < MAX_INVENTORY_SIZE)
14316             player->inventory_element[player->inventory_size++] = element;
14317
14318       DrawGameDoorValues();
14319     }
14320     else if (collect_count > 0)
14321     {
14322       game.gems_still_needed -= collect_count;
14323       if (game.gems_still_needed < 0)
14324         game.gems_still_needed = 0;
14325
14326       game.snapshot.collected_item = TRUE;
14327
14328       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14329
14330       DisplayGameControlValues();
14331     }
14332
14333     RaiseScoreElement(element);
14334     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14335
14336     // use old behaviour for old levels (collecting)
14337     if (!level.finish_dig_collect && is_player)
14338     {
14339       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14340                                           player->index_bit, dig_side);
14341
14342       // if collecting triggered player relocation, finish collecting tile
14343       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14344         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14345     }
14346
14347     if (mode == DF_SNAP)
14348     {
14349       if (level.block_snap_field)
14350         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14351       else
14352         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14353
14354       // use old behaviour for old levels (snapping)
14355       if (!level.finish_dig_collect)
14356         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14357                                             player->index_bit, dig_side);
14358     }
14359   }
14360   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14361   {
14362     if (mode == DF_SNAP && element != EL_BD_ROCK)
14363       return MP_NO_ACTION;
14364
14365     if (CAN_FALL(element) && dy)
14366       return MP_NO_ACTION;
14367
14368     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14369         !(element == EL_SPRING && level.use_spring_bug))
14370       return MP_NO_ACTION;
14371
14372     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14373         ((move_direction & MV_VERTICAL &&
14374           ((element_info[element].move_pattern & MV_LEFT &&
14375             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14376            (element_info[element].move_pattern & MV_RIGHT &&
14377             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14378          (move_direction & MV_HORIZONTAL &&
14379           ((element_info[element].move_pattern & MV_UP &&
14380             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14381            (element_info[element].move_pattern & MV_DOWN &&
14382             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14383       return MP_NO_ACTION;
14384
14385     // do not push elements already moving away faster than player
14386     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14387         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14388       return MP_NO_ACTION;
14389
14390     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14391     {
14392       if (player->push_delay_value == -1 || !player_was_pushing)
14393         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14394     }
14395     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14396     {
14397       if (player->push_delay_value == -1)
14398         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14399     }
14400     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14401     {
14402       if (!player->is_pushing)
14403         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14404     }
14405
14406     player->is_pushing = TRUE;
14407     player->is_active = TRUE;
14408
14409     if (!(IN_LEV_FIELD(nextx, nexty) &&
14410           (IS_FREE(nextx, nexty) ||
14411            (IS_SB_ELEMENT(element) &&
14412             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14413            (IS_CUSTOM_ELEMENT(element) &&
14414             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14415       return MP_NO_ACTION;
14416
14417     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14418       return MP_NO_ACTION;
14419
14420     if (player->push_delay == -1)       // new pushing; restart delay
14421       player->push_delay = 0;
14422
14423     if (player->push_delay < player->push_delay_value &&
14424         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14425         element != EL_SPRING && element != EL_BALLOON)
14426     {
14427       // make sure that there is no move delay before next try to push
14428       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14429         player->move_delay = 0;
14430
14431       return MP_NO_ACTION;
14432     }
14433
14434     if (IS_CUSTOM_ELEMENT(element) &&
14435         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14436     {
14437       if (!DigFieldByCE(nextx, nexty, element))
14438         return MP_NO_ACTION;
14439     }
14440
14441     if (IS_SB_ELEMENT(element))
14442     {
14443       boolean sokoban_task_solved = FALSE;
14444
14445       if (element == EL_SOKOBAN_FIELD_FULL)
14446       {
14447         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14448
14449         IncrementSokobanFieldsNeeded();
14450         IncrementSokobanObjectsNeeded();
14451       }
14452
14453       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14454       {
14455         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14456
14457         DecrementSokobanFieldsNeeded();
14458         DecrementSokobanObjectsNeeded();
14459
14460         // sokoban object was pushed from empty field to sokoban field
14461         if (Back[x][y] == EL_EMPTY)
14462           sokoban_task_solved = TRUE;
14463       }
14464
14465       Tile[x][y] = EL_SOKOBAN_OBJECT;
14466
14467       if (Back[x][y] == Back[nextx][nexty])
14468         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14469       else if (Back[x][y] != 0)
14470         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14471                                     ACTION_EMPTYING);
14472       else
14473         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14474                                     ACTION_FILLING);
14475
14476       if (sokoban_task_solved &&
14477           game.sokoban_fields_still_needed == 0 &&
14478           game.sokoban_objects_still_needed == 0 &&
14479           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14480       {
14481         game.players_still_needed = 0;
14482
14483         LevelSolved();
14484
14485         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14486       }
14487     }
14488     else
14489       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14490
14491     InitMovingField(x, y, move_direction);
14492     GfxAction[x][y] = ACTION_PUSHING;
14493
14494     if (mode == DF_SNAP)
14495       ContinueMoving(x, y);
14496     else
14497       MovPos[x][y] = (dx != 0 ? dx : dy);
14498
14499     Pushed[x][y] = TRUE;
14500     Pushed[nextx][nexty] = TRUE;
14501
14502     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14503       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14504     else
14505       player->push_delay_value = -1;    // get new value later
14506
14507     // check for element change _after_ element has been pushed
14508     if (game.use_change_when_pushing_bug)
14509     {
14510       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14511                                  player->index_bit, dig_side);
14512       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14513                                           player->index_bit, dig_side);
14514     }
14515   }
14516   else if (IS_SWITCHABLE(element))
14517   {
14518     if (PLAYER_SWITCHING(player, x, y))
14519     {
14520       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14521                                           player->index_bit, dig_side);
14522
14523       return MP_ACTION;
14524     }
14525
14526     player->is_switching = TRUE;
14527     player->switch_x = x;
14528     player->switch_y = y;
14529
14530     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14531
14532     if (element == EL_ROBOT_WHEEL)
14533     {
14534       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14535
14536       game.robot_wheel_x = x;
14537       game.robot_wheel_y = y;
14538       game.robot_wheel_active = TRUE;
14539
14540       TEST_DrawLevelField(x, y);
14541     }
14542     else if (element == EL_SP_TERMINAL)
14543     {
14544       int xx, yy;
14545
14546       SCAN_PLAYFIELD(xx, yy)
14547       {
14548         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14549         {
14550           Bang(xx, yy);
14551         }
14552         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14553         {
14554           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14555
14556           ResetGfxAnimation(xx, yy);
14557           TEST_DrawLevelField(xx, yy);
14558         }
14559       }
14560     }
14561     else if (IS_BELT_SWITCH(element))
14562     {
14563       ToggleBeltSwitch(x, y);
14564     }
14565     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14566              element == EL_SWITCHGATE_SWITCH_DOWN ||
14567              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14568              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14569     {
14570       ToggleSwitchgateSwitch(x, y);
14571     }
14572     else if (element == EL_LIGHT_SWITCH ||
14573              element == EL_LIGHT_SWITCH_ACTIVE)
14574     {
14575       ToggleLightSwitch(x, y);
14576     }
14577     else if (element == EL_TIMEGATE_SWITCH ||
14578              element == EL_DC_TIMEGATE_SWITCH)
14579     {
14580       ActivateTimegateSwitch(x, y);
14581     }
14582     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14583              element == EL_BALLOON_SWITCH_RIGHT ||
14584              element == EL_BALLOON_SWITCH_UP    ||
14585              element == EL_BALLOON_SWITCH_DOWN  ||
14586              element == EL_BALLOON_SWITCH_NONE  ||
14587              element == EL_BALLOON_SWITCH_ANY)
14588     {
14589       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14590                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14591                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14592                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14593                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14594                              move_direction);
14595     }
14596     else if (element == EL_LAMP)
14597     {
14598       Tile[x][y] = EL_LAMP_ACTIVE;
14599       game.lights_still_needed--;
14600
14601       ResetGfxAnimation(x, y);
14602       TEST_DrawLevelField(x, y);
14603     }
14604     else if (element == EL_TIME_ORB_FULL)
14605     {
14606       Tile[x][y] = EL_TIME_ORB_EMPTY;
14607
14608       if (level.time > 0 || level.use_time_orb_bug)
14609       {
14610         TimeLeft += level.time_orb_time;
14611         game.no_time_limit = FALSE;
14612
14613         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14614
14615         DisplayGameControlValues();
14616       }
14617
14618       ResetGfxAnimation(x, y);
14619       TEST_DrawLevelField(x, y);
14620     }
14621     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14622              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14623     {
14624       int xx, yy;
14625
14626       game.ball_active = !game.ball_active;
14627
14628       SCAN_PLAYFIELD(xx, yy)
14629       {
14630         int e = Tile[xx][yy];
14631
14632         if (game.ball_active)
14633         {
14634           if (e == EL_EMC_MAGIC_BALL)
14635             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14636           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14637             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14638         }
14639         else
14640         {
14641           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14642             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14643           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14644             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14645         }
14646       }
14647     }
14648
14649     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14650                                         player->index_bit, dig_side);
14651
14652     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14653                                         player->index_bit, dig_side);
14654
14655     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14656                                         player->index_bit, dig_side);
14657
14658     return MP_ACTION;
14659   }
14660   else
14661   {
14662     if (!PLAYER_SWITCHING(player, x, y))
14663     {
14664       player->is_switching = TRUE;
14665       player->switch_x = x;
14666       player->switch_y = y;
14667
14668       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14669                                  player->index_bit, dig_side);
14670       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14671                                           player->index_bit, dig_side);
14672
14673       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14674                                  player->index_bit, dig_side);
14675       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14676                                           player->index_bit, dig_side);
14677     }
14678
14679     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14680                                player->index_bit, dig_side);
14681     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14682                                         player->index_bit, dig_side);
14683
14684     return MP_NO_ACTION;
14685   }
14686
14687   player->push_delay = -1;
14688
14689   if (is_player)                // function can also be called by EL_PENGUIN
14690   {
14691     if (Tile[x][y] != element)          // really digged/collected something
14692     {
14693       player->is_collecting = !player->is_digging;
14694       player->is_active = TRUE;
14695
14696       player->last_removed_element = element;
14697     }
14698   }
14699
14700   return MP_MOVING;
14701 }
14702
14703 static boolean DigFieldByCE(int x, int y, int digging_element)
14704 {
14705   int element = Tile[x][y];
14706
14707   if (!IS_FREE(x, y))
14708   {
14709     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14710                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14711                   ACTION_BREAKING);
14712
14713     // no element can dig solid indestructible elements
14714     if (IS_INDESTRUCTIBLE(element) &&
14715         !IS_DIGGABLE(element) &&
14716         !IS_COLLECTIBLE(element))
14717       return FALSE;
14718
14719     if (AmoebaNr[x][y] &&
14720         (element == EL_AMOEBA_FULL ||
14721          element == EL_BD_AMOEBA ||
14722          element == EL_AMOEBA_GROWING))
14723     {
14724       AmoebaCnt[AmoebaNr[x][y]]--;
14725       AmoebaCnt2[AmoebaNr[x][y]]--;
14726     }
14727
14728     if (IS_MOVING(x, y))
14729       RemoveMovingField(x, y);
14730     else
14731     {
14732       RemoveField(x, y);
14733       TEST_DrawLevelField(x, y);
14734     }
14735
14736     // if digged element was about to explode, prevent the explosion
14737     ExplodeField[x][y] = EX_TYPE_NONE;
14738
14739     PlayLevelSoundAction(x, y, action);
14740   }
14741
14742   Store[x][y] = EL_EMPTY;
14743
14744   // this makes it possible to leave the removed element again
14745   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14746     Store[x][y] = element;
14747
14748   return TRUE;
14749 }
14750
14751 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14752 {
14753   int jx = player->jx, jy = player->jy;
14754   int x = jx + dx, y = jy + dy;
14755   int snap_direction = (dx == -1 ? MV_LEFT  :
14756                         dx == +1 ? MV_RIGHT :
14757                         dy == -1 ? MV_UP    :
14758                         dy == +1 ? MV_DOWN  : MV_NONE);
14759   boolean can_continue_snapping = (level.continuous_snapping &&
14760                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14761
14762   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14763     return FALSE;
14764
14765   if (!player->active || !IN_LEV_FIELD(x, y))
14766     return FALSE;
14767
14768   if (dx && dy)
14769     return FALSE;
14770
14771   if (!dx && !dy)
14772   {
14773     if (player->MovPos == 0)
14774       player->is_pushing = FALSE;
14775
14776     player->is_snapping = FALSE;
14777
14778     if (player->MovPos == 0)
14779     {
14780       player->is_moving = FALSE;
14781       player->is_digging = FALSE;
14782       player->is_collecting = FALSE;
14783     }
14784
14785     return FALSE;
14786   }
14787
14788   // prevent snapping with already pressed snap key when not allowed
14789   if (player->is_snapping && !can_continue_snapping)
14790     return FALSE;
14791
14792   player->MovDir = snap_direction;
14793
14794   if (player->MovPos == 0)
14795   {
14796     player->is_moving = FALSE;
14797     player->is_digging = FALSE;
14798     player->is_collecting = FALSE;
14799   }
14800
14801   player->is_dropping = FALSE;
14802   player->is_dropping_pressed = FALSE;
14803   player->drop_pressed_delay = 0;
14804
14805   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14806     return FALSE;
14807
14808   player->is_snapping = TRUE;
14809   player->is_active = TRUE;
14810
14811   if (player->MovPos == 0)
14812   {
14813     player->is_moving = FALSE;
14814     player->is_digging = FALSE;
14815     player->is_collecting = FALSE;
14816   }
14817
14818   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14819     TEST_DrawLevelField(player->last_jx, player->last_jy);
14820
14821   TEST_DrawLevelField(x, y);
14822
14823   return TRUE;
14824 }
14825
14826 static boolean DropElement(struct PlayerInfo *player)
14827 {
14828   int old_element, new_element;
14829   int dropx = player->jx, dropy = player->jy;
14830   int drop_direction = player->MovDir;
14831   int drop_side = drop_direction;
14832   int drop_element = get_next_dropped_element(player);
14833
14834   /* do not drop an element on top of another element; when holding drop key
14835      pressed without moving, dropped element must move away before the next
14836      element can be dropped (this is especially important if the next element
14837      is dynamite, which can be placed on background for historical reasons) */
14838   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14839     return MP_ACTION;
14840
14841   if (IS_THROWABLE(drop_element))
14842   {
14843     dropx += GET_DX_FROM_DIR(drop_direction);
14844     dropy += GET_DY_FROM_DIR(drop_direction);
14845
14846     if (!IN_LEV_FIELD(dropx, dropy))
14847       return FALSE;
14848   }
14849
14850   old_element = Tile[dropx][dropy];     // old element at dropping position
14851   new_element = drop_element;           // default: no change when dropping
14852
14853   // check if player is active, not moving and ready to drop
14854   if (!player->active || player->MovPos || player->drop_delay > 0)
14855     return FALSE;
14856
14857   // check if player has anything that can be dropped
14858   if (new_element == EL_UNDEFINED)
14859     return FALSE;
14860
14861   // only set if player has anything that can be dropped
14862   player->is_dropping_pressed = TRUE;
14863
14864   // check if drop key was pressed long enough for EM style dynamite
14865   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14866     return FALSE;
14867
14868   // check if anything can be dropped at the current position
14869   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14870     return FALSE;
14871
14872   // collected custom elements can only be dropped on empty fields
14873   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14874     return FALSE;
14875
14876   if (old_element != EL_EMPTY)
14877     Back[dropx][dropy] = old_element;   // store old element on this field
14878
14879   ResetGfxAnimation(dropx, dropy);
14880   ResetRandomAnimationValue(dropx, dropy);
14881
14882   if (player->inventory_size > 0 ||
14883       player->inventory_infinite_element != EL_UNDEFINED)
14884   {
14885     if (player->inventory_size > 0)
14886     {
14887       player->inventory_size--;
14888
14889       DrawGameDoorValues();
14890
14891       if (new_element == EL_DYNAMITE)
14892         new_element = EL_DYNAMITE_ACTIVE;
14893       else if (new_element == EL_EM_DYNAMITE)
14894         new_element = EL_EM_DYNAMITE_ACTIVE;
14895       else if (new_element == EL_SP_DISK_RED)
14896         new_element = EL_SP_DISK_RED_ACTIVE;
14897     }
14898
14899     Tile[dropx][dropy] = new_element;
14900
14901     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14902       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14903                           el2img(Tile[dropx][dropy]), 0);
14904
14905     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14906
14907     // needed if previous element just changed to "empty" in the last frame
14908     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14909
14910     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14911                                player->index_bit, drop_side);
14912     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14913                                         CE_PLAYER_DROPS_X,
14914                                         player->index_bit, drop_side);
14915
14916     TestIfElementTouchesCustomElement(dropx, dropy);
14917   }
14918   else          // player is dropping a dyna bomb
14919   {
14920     player->dynabombs_left--;
14921
14922     Tile[dropx][dropy] = new_element;
14923
14924     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14925       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14926                           el2img(Tile[dropx][dropy]), 0);
14927
14928     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14929   }
14930
14931   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14932     InitField_WithBug1(dropx, dropy, FALSE);
14933
14934   new_element = Tile[dropx][dropy];     // element might have changed
14935
14936   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14937       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14938   {
14939     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14940       MovDir[dropx][dropy] = drop_direction;
14941
14942     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14943
14944     // do not cause impact style collision by dropping elements that can fall
14945     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14946   }
14947
14948   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14949   player->is_dropping = TRUE;
14950
14951   player->drop_pressed_delay = 0;
14952   player->is_dropping_pressed = FALSE;
14953
14954   player->drop_x = dropx;
14955   player->drop_y = dropy;
14956
14957   return TRUE;
14958 }
14959
14960 // ----------------------------------------------------------------------------
14961 // game sound playing functions
14962 // ----------------------------------------------------------------------------
14963
14964 static int *loop_sound_frame = NULL;
14965 static int *loop_sound_volume = NULL;
14966
14967 void InitPlayLevelSound(void)
14968 {
14969   int num_sounds = getSoundListSize();
14970
14971   checked_free(loop_sound_frame);
14972   checked_free(loop_sound_volume);
14973
14974   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14975   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14976 }
14977
14978 static void PlayLevelSound(int x, int y, int nr)
14979 {
14980   int sx = SCREENX(x), sy = SCREENY(y);
14981   int volume, stereo_position;
14982   int max_distance = 8;
14983   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14984
14985   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14986       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14987     return;
14988
14989   if (!IN_LEV_FIELD(x, y) ||
14990       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14991       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14992     return;
14993
14994   volume = SOUND_MAX_VOLUME;
14995
14996   if (!IN_SCR_FIELD(sx, sy))
14997   {
14998     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14999     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15000
15001     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15002   }
15003
15004   stereo_position = (SOUND_MAX_LEFT +
15005                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15006                      (SCR_FIELDX + 2 * max_distance));
15007
15008   if (IS_LOOP_SOUND(nr))
15009   {
15010     /* This assures that quieter loop sounds do not overwrite louder ones,
15011        while restarting sound volume comparison with each new game frame. */
15012
15013     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15014       return;
15015
15016     loop_sound_volume[nr] = volume;
15017     loop_sound_frame[nr] = FrameCounter;
15018   }
15019
15020   PlaySoundExt(nr, volume, stereo_position, type);
15021 }
15022
15023 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15024 {
15025   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15026                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15027                  y < LEVELY(BY1) ? LEVELY(BY1) :
15028                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15029                  sound_action);
15030 }
15031
15032 static void PlayLevelSoundAction(int x, int y, int action)
15033 {
15034   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15035 }
15036
15037 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15038 {
15039   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15040
15041   if (sound_effect != SND_UNDEFINED)
15042     PlayLevelSound(x, y, sound_effect);
15043 }
15044
15045 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15046                                               int action)
15047 {
15048   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15049
15050   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15051     PlayLevelSound(x, y, sound_effect);
15052 }
15053
15054 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15055 {
15056   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15057
15058   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15059     PlayLevelSound(x, y, sound_effect);
15060 }
15061
15062 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15063 {
15064   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15065
15066   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15067     StopSound(sound_effect);
15068 }
15069
15070 static int getLevelMusicNr(void)
15071 {
15072   if (levelset.music[level_nr] != MUS_UNDEFINED)
15073     return levelset.music[level_nr];            // from config file
15074   else
15075     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15076 }
15077
15078 static void FadeLevelSounds(void)
15079 {
15080   FadeSounds();
15081 }
15082
15083 static void FadeLevelMusic(void)
15084 {
15085   int music_nr = getLevelMusicNr();
15086   char *curr_music = getCurrentlyPlayingMusicFilename();
15087   char *next_music = getMusicInfoEntryFilename(music_nr);
15088
15089   if (!strEqual(curr_music, next_music))
15090     FadeMusic();
15091 }
15092
15093 void FadeLevelSoundsAndMusic(void)
15094 {
15095   FadeLevelSounds();
15096   FadeLevelMusic();
15097 }
15098
15099 static void PlayLevelMusic(void)
15100 {
15101   int music_nr = getLevelMusicNr();
15102   char *curr_music = getCurrentlyPlayingMusicFilename();
15103   char *next_music = getMusicInfoEntryFilename(music_nr);
15104
15105   if (!strEqual(curr_music, next_music))
15106     PlayMusicLoop(music_nr);
15107 }
15108
15109 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15110 {
15111   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15112   int offset = 0;
15113   int x = xx - offset;
15114   int y = yy - offset;
15115
15116   switch (sample)
15117   {
15118     case SOUND_blank:
15119       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15120       break;
15121
15122     case SOUND_roll:
15123       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15124       break;
15125
15126     case SOUND_stone:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15128       break;
15129
15130     case SOUND_nut:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15132       break;
15133
15134     case SOUND_crack:
15135       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15136       break;
15137
15138     case SOUND_bug:
15139       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15140       break;
15141
15142     case SOUND_tank:
15143       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15144       break;
15145
15146     case SOUND_android_clone:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15148       break;
15149
15150     case SOUND_android_move:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15152       break;
15153
15154     case SOUND_spring:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15156       break;
15157
15158     case SOUND_slurp:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15160       break;
15161
15162     case SOUND_eater:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15164       break;
15165
15166     case SOUND_eater_eat:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15168       break;
15169
15170     case SOUND_alien:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15172       break;
15173
15174     case SOUND_collect:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15176       break;
15177
15178     case SOUND_diamond:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15180       break;
15181
15182     case SOUND_squash:
15183       // !!! CHECK THIS !!!
15184 #if 1
15185       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15186 #else
15187       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15188 #endif
15189       break;
15190
15191     case SOUND_wonderfall:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15193       break;
15194
15195     case SOUND_drip:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15197       break;
15198
15199     case SOUND_push:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15201       break;
15202
15203     case SOUND_dirt:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15205       break;
15206
15207     case SOUND_acid:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15209       break;
15210
15211     case SOUND_ball:
15212       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15213       break;
15214
15215     case SOUND_slide:
15216       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15217       break;
15218
15219     case SOUND_wonder:
15220       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15221       break;
15222
15223     case SOUND_door:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15225       break;
15226
15227     case SOUND_exit_open:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15229       break;
15230
15231     case SOUND_exit_leave:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15233       break;
15234
15235     case SOUND_dynamite:
15236       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15237       break;
15238
15239     case SOUND_tick:
15240       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15241       break;
15242
15243     case SOUND_press:
15244       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15245       break;
15246
15247     case SOUND_wheel:
15248       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15249       break;
15250
15251     case SOUND_boom:
15252       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15253       break;
15254
15255     case SOUND_die:
15256       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15257       break;
15258
15259     case SOUND_time:
15260       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15261       break;
15262
15263     default:
15264       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15265       break;
15266   }
15267 }
15268
15269 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15270 {
15271   int element = map_element_SP_to_RND(element_sp);
15272   int action = map_action_SP_to_RND(action_sp);
15273   int offset = (setup.sp_show_border_elements ? 0 : 1);
15274   int x = xx - offset;
15275   int y = yy - offset;
15276
15277   PlayLevelSoundElementAction(x, y, element, action);
15278 }
15279
15280 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15281 {
15282   int element = map_element_MM_to_RND(element_mm);
15283   int action = map_action_MM_to_RND(action_mm);
15284   int offset = 0;
15285   int x = xx - offset;
15286   int y = yy - offset;
15287
15288   if (!IS_MM_ELEMENT(element))
15289     element = EL_MM_DEFAULT;
15290
15291   PlayLevelSoundElementAction(x, y, element, action);
15292 }
15293
15294 void PlaySound_MM(int sound_mm)
15295 {
15296   int sound = map_sound_MM_to_RND(sound_mm);
15297
15298   if (sound == SND_UNDEFINED)
15299     return;
15300
15301   PlaySound(sound);
15302 }
15303
15304 void PlaySoundLoop_MM(int sound_mm)
15305 {
15306   int sound = map_sound_MM_to_RND(sound_mm);
15307
15308   if (sound == SND_UNDEFINED)
15309     return;
15310
15311   PlaySoundLoop(sound);
15312 }
15313
15314 void StopSound_MM(int sound_mm)
15315 {
15316   int sound = map_sound_MM_to_RND(sound_mm);
15317
15318   if (sound == SND_UNDEFINED)
15319     return;
15320
15321   StopSound(sound);
15322 }
15323
15324 void RaiseScore(int value)
15325 {
15326   game.score += value;
15327
15328   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15329
15330   DisplayGameControlValues();
15331 }
15332
15333 void RaiseScoreElement(int element)
15334 {
15335   switch (element)
15336   {
15337     case EL_EMERALD:
15338     case EL_BD_DIAMOND:
15339     case EL_EMERALD_YELLOW:
15340     case EL_EMERALD_RED:
15341     case EL_EMERALD_PURPLE:
15342     case EL_SP_INFOTRON:
15343       RaiseScore(level.score[SC_EMERALD]);
15344       break;
15345     case EL_DIAMOND:
15346       RaiseScore(level.score[SC_DIAMOND]);
15347       break;
15348     case EL_CRYSTAL:
15349       RaiseScore(level.score[SC_CRYSTAL]);
15350       break;
15351     case EL_PEARL:
15352       RaiseScore(level.score[SC_PEARL]);
15353       break;
15354     case EL_BUG:
15355     case EL_BD_BUTTERFLY:
15356     case EL_SP_ELECTRON:
15357       RaiseScore(level.score[SC_BUG]);
15358       break;
15359     case EL_SPACESHIP:
15360     case EL_BD_FIREFLY:
15361     case EL_SP_SNIKSNAK:
15362       RaiseScore(level.score[SC_SPACESHIP]);
15363       break;
15364     case EL_YAMYAM:
15365     case EL_DARK_YAMYAM:
15366       RaiseScore(level.score[SC_YAMYAM]);
15367       break;
15368     case EL_ROBOT:
15369       RaiseScore(level.score[SC_ROBOT]);
15370       break;
15371     case EL_PACMAN:
15372       RaiseScore(level.score[SC_PACMAN]);
15373       break;
15374     case EL_NUT:
15375       RaiseScore(level.score[SC_NUT]);
15376       break;
15377     case EL_DYNAMITE:
15378     case EL_EM_DYNAMITE:
15379     case EL_SP_DISK_RED:
15380     case EL_DYNABOMB_INCREASE_NUMBER:
15381     case EL_DYNABOMB_INCREASE_SIZE:
15382     case EL_DYNABOMB_INCREASE_POWER:
15383       RaiseScore(level.score[SC_DYNAMITE]);
15384       break;
15385     case EL_SHIELD_NORMAL:
15386     case EL_SHIELD_DEADLY:
15387       RaiseScore(level.score[SC_SHIELD]);
15388       break;
15389     case EL_EXTRA_TIME:
15390       RaiseScore(level.extra_time_score);
15391       break;
15392     case EL_KEY_1:
15393     case EL_KEY_2:
15394     case EL_KEY_3:
15395     case EL_KEY_4:
15396     case EL_EM_KEY_1:
15397     case EL_EM_KEY_2:
15398     case EL_EM_KEY_3:
15399     case EL_EM_KEY_4:
15400     case EL_EMC_KEY_5:
15401     case EL_EMC_KEY_6:
15402     case EL_EMC_KEY_7:
15403     case EL_EMC_KEY_8:
15404     case EL_DC_KEY_WHITE:
15405       RaiseScore(level.score[SC_KEY]);
15406       break;
15407     default:
15408       RaiseScore(element_info[element].collect_score);
15409       break;
15410   }
15411 }
15412
15413 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15414 {
15415   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15416   {
15417     if (!quick_quit)
15418     {
15419       // prevent short reactivation of overlay buttons while closing door
15420       SetOverlayActive(FALSE);
15421
15422       // door may still be open due to skipped or envelope style request
15423       CloseDoor(DOOR_CLOSE_1);
15424     }
15425
15426     if (network.enabled)
15427       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15428     else
15429     {
15430       if (quick_quit)
15431         FadeSkipNextFadeIn();
15432
15433       SetGameStatus(GAME_MODE_MAIN);
15434
15435       DrawMainMenu();
15436     }
15437   }
15438   else          // continue playing the game
15439   {
15440     if (tape.playing && tape.deactivate_display)
15441       TapeDeactivateDisplayOff(TRUE);
15442
15443     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15444
15445     if (tape.playing && tape.deactivate_display)
15446       TapeDeactivateDisplayOn();
15447   }
15448 }
15449
15450 void RequestQuitGame(boolean escape_key_pressed)
15451 {
15452   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15453   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15454                         level_editor_test_game);
15455   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15456                           quick_quit);
15457
15458   RequestQuitGameExt(skip_request, quick_quit,
15459                      "Do you really want to quit the game?");
15460 }
15461
15462 void RequestRestartGame(char *message)
15463 {
15464   game.restart_game_message = NULL;
15465
15466   boolean has_started_game = hasStartedNetworkGame();
15467   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15468
15469   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15470   {
15471     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15472   }
15473   else
15474   {
15475     // needed in case of envelope request to close game panel
15476     CloseDoor(DOOR_CLOSE_1);
15477
15478     SetGameStatus(GAME_MODE_MAIN);
15479
15480     DrawMainMenu();
15481   }
15482 }
15483
15484 void CheckGameOver(void)
15485 {
15486   static boolean last_game_over = FALSE;
15487   static int game_over_delay = 0;
15488   int game_over_delay_value = 50;
15489   boolean game_over = checkGameFailed();
15490
15491   // do not handle game over if request dialog is already active
15492   if (game.request_active)
15493     return;
15494
15495   // do not ask to play again if game was never actually played
15496   if (!game.GamePlayed)
15497     return;
15498
15499   if (!game_over)
15500   {
15501     last_game_over = FALSE;
15502     game_over_delay = game_over_delay_value;
15503
15504     return;
15505   }
15506
15507   if (game_over_delay > 0)
15508   {
15509     game_over_delay--;
15510
15511     return;
15512   }
15513
15514   if (last_game_over != game_over)
15515     game.restart_game_message = (hasStartedNetworkGame() ?
15516                                  "Game over! Play it again?" :
15517                                  "Game over!");
15518
15519   last_game_over = game_over;
15520 }
15521
15522 boolean checkGameSolved(void)
15523 {
15524   // set for all game engines if level was solved
15525   return game.LevelSolved_GameEnd;
15526 }
15527
15528 boolean checkGameFailed(void)
15529 {
15530   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15531     return (game_em.game_over && !game_em.level_solved);
15532   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15533     return (game_sp.game_over && !game_sp.level_solved);
15534   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15535     return (game_mm.game_over && !game_mm.level_solved);
15536   else                          // GAME_ENGINE_TYPE_RND
15537     return (game.GameOver && !game.LevelSolved);
15538 }
15539
15540 boolean checkGameEnded(void)
15541 {
15542   return (checkGameSolved() || checkGameFailed());
15543 }
15544
15545
15546 // ----------------------------------------------------------------------------
15547 // random generator functions
15548 // ----------------------------------------------------------------------------
15549
15550 unsigned int InitEngineRandom_RND(int seed)
15551 {
15552   game.num_random_calls = 0;
15553
15554   return InitEngineRandom(seed);
15555 }
15556
15557 unsigned int RND(int max)
15558 {
15559   if (max > 0)
15560   {
15561     game.num_random_calls++;
15562
15563     return GetEngineRandom(max);
15564   }
15565
15566   return 0;
15567 }
15568
15569
15570 // ----------------------------------------------------------------------------
15571 // game engine snapshot handling functions
15572 // ----------------------------------------------------------------------------
15573
15574 struct EngineSnapshotInfo
15575 {
15576   // runtime values for custom element collect score
15577   int collect_score[NUM_CUSTOM_ELEMENTS];
15578
15579   // runtime values for group element choice position
15580   int choice_pos[NUM_GROUP_ELEMENTS];
15581
15582   // runtime values for belt position animations
15583   int belt_graphic[4][NUM_BELT_PARTS];
15584   int belt_anim_mode[4][NUM_BELT_PARTS];
15585 };
15586
15587 static struct EngineSnapshotInfo engine_snapshot_rnd;
15588 static char *snapshot_level_identifier = NULL;
15589 static int snapshot_level_nr = -1;
15590
15591 static void SaveEngineSnapshotValues_RND(void)
15592 {
15593   static int belt_base_active_element[4] =
15594   {
15595     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15596     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15597     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15598     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15599   };
15600   int i, j;
15601
15602   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15603   {
15604     int element = EL_CUSTOM_START + i;
15605
15606     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15607   }
15608
15609   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15610   {
15611     int element = EL_GROUP_START + i;
15612
15613     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15614   }
15615
15616   for (i = 0; i < 4; i++)
15617   {
15618     for (j = 0; j < NUM_BELT_PARTS; j++)
15619     {
15620       int element = belt_base_active_element[i] + j;
15621       int graphic = el2img(element);
15622       int anim_mode = graphic_info[graphic].anim_mode;
15623
15624       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15625       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15626     }
15627   }
15628 }
15629
15630 static void LoadEngineSnapshotValues_RND(void)
15631 {
15632   unsigned int num_random_calls = game.num_random_calls;
15633   int i, j;
15634
15635   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15636   {
15637     int element = EL_CUSTOM_START + i;
15638
15639     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15640   }
15641
15642   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15643   {
15644     int element = EL_GROUP_START + i;
15645
15646     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15647   }
15648
15649   for (i = 0; i < 4; i++)
15650   {
15651     for (j = 0; j < NUM_BELT_PARTS; j++)
15652     {
15653       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15654       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15655
15656       graphic_info[graphic].anim_mode = anim_mode;
15657     }
15658   }
15659
15660   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15661   {
15662     InitRND(tape.random_seed);
15663     for (i = 0; i < num_random_calls; i++)
15664       RND(1);
15665   }
15666
15667   if (game.num_random_calls != num_random_calls)
15668   {
15669     Error("number of random calls out of sync");
15670     Error("number of random calls should be %d", num_random_calls);
15671     Error("number of random calls is %d", game.num_random_calls);
15672
15673     Fail("this should not happen -- please debug");
15674   }
15675 }
15676
15677 void FreeEngineSnapshotSingle(void)
15678 {
15679   FreeSnapshotSingle();
15680
15681   setString(&snapshot_level_identifier, NULL);
15682   snapshot_level_nr = -1;
15683 }
15684
15685 void FreeEngineSnapshotList(void)
15686 {
15687   FreeSnapshotList();
15688 }
15689
15690 static ListNode *SaveEngineSnapshotBuffers(void)
15691 {
15692   ListNode *buffers = NULL;
15693
15694   // copy some special values to a structure better suited for the snapshot
15695
15696   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15697     SaveEngineSnapshotValues_RND();
15698   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15699     SaveEngineSnapshotValues_EM();
15700   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15701     SaveEngineSnapshotValues_SP(&buffers);
15702   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15703     SaveEngineSnapshotValues_MM(&buffers);
15704
15705   // save values stored in special snapshot structure
15706
15707   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15708     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15709   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15710     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15711   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15712     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15713   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15714     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15715
15716   // save further RND engine values
15717
15718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15721
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15727
15728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15731
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15733
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15736
15737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15755
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15758
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15762
15763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15765
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15771
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15774
15775 #if 0
15776   ListNode *node = engine_snapshot_list_rnd;
15777   int num_bytes = 0;
15778
15779   while (node != NULL)
15780   {
15781     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15782
15783     node = node->next;
15784   }
15785
15786   Debug("game:playing:SaveEngineSnapshotBuffers",
15787         "size of engine snapshot: %d bytes", num_bytes);
15788 #endif
15789
15790   return buffers;
15791 }
15792
15793 void SaveEngineSnapshotSingle(void)
15794 {
15795   ListNode *buffers = SaveEngineSnapshotBuffers();
15796
15797   // finally save all snapshot buffers to single snapshot
15798   SaveSnapshotSingle(buffers);
15799
15800   // save level identification information
15801   setString(&snapshot_level_identifier, leveldir_current->identifier);
15802   snapshot_level_nr = level_nr;
15803 }
15804
15805 boolean CheckSaveEngineSnapshotToList(void)
15806 {
15807   boolean save_snapshot =
15808     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15809      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15810       game.snapshot.changed_action) ||
15811      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15812       game.snapshot.collected_item));
15813
15814   game.snapshot.changed_action = FALSE;
15815   game.snapshot.collected_item = FALSE;
15816   game.snapshot.save_snapshot = save_snapshot;
15817
15818   return save_snapshot;
15819 }
15820
15821 void SaveEngineSnapshotToList(void)
15822 {
15823   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15824       tape.quick_resume)
15825     return;
15826
15827   ListNode *buffers = SaveEngineSnapshotBuffers();
15828
15829   // finally save all snapshot buffers to snapshot list
15830   SaveSnapshotToList(buffers);
15831 }
15832
15833 void SaveEngineSnapshotToListInitial(void)
15834 {
15835   FreeEngineSnapshotList();
15836
15837   SaveEngineSnapshotToList();
15838 }
15839
15840 static void LoadEngineSnapshotValues(void)
15841 {
15842   // restore special values from snapshot structure
15843
15844   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15845     LoadEngineSnapshotValues_RND();
15846   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15847     LoadEngineSnapshotValues_EM();
15848   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15849     LoadEngineSnapshotValues_SP();
15850   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15851     LoadEngineSnapshotValues_MM();
15852 }
15853
15854 void LoadEngineSnapshotSingle(void)
15855 {
15856   LoadSnapshotSingle();
15857
15858   LoadEngineSnapshotValues();
15859 }
15860
15861 static void LoadEngineSnapshot_Undo(int steps)
15862 {
15863   LoadSnapshotFromList_Older(steps);
15864
15865   LoadEngineSnapshotValues();
15866 }
15867
15868 static void LoadEngineSnapshot_Redo(int steps)
15869 {
15870   LoadSnapshotFromList_Newer(steps);
15871
15872   LoadEngineSnapshotValues();
15873 }
15874
15875 boolean CheckEngineSnapshotSingle(void)
15876 {
15877   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15878           snapshot_level_nr == level_nr);
15879 }
15880
15881 boolean CheckEngineSnapshotList(void)
15882 {
15883   return CheckSnapshotList();
15884 }
15885
15886
15887 // ---------- new game button stuff -------------------------------------------
15888
15889 static struct
15890 {
15891   int graphic;
15892   struct XY *pos;
15893   int gadget_id;
15894   boolean *setup_value;
15895   boolean allowed_on_tape;
15896   boolean is_touch_button;
15897   char *infotext;
15898 } gamebutton_info[NUM_GAME_BUTTONS] =
15899 {
15900   {
15901     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15902     GAME_CTRL_ID_STOP,                          NULL,
15903     TRUE, FALSE,                                "stop game"
15904   },
15905   {
15906     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15907     GAME_CTRL_ID_PAUSE,                         NULL,
15908     TRUE, FALSE,                                "pause game"
15909   },
15910   {
15911     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15912     GAME_CTRL_ID_PLAY,                          NULL,
15913     TRUE, FALSE,                                "play game"
15914   },
15915   {
15916     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15917     GAME_CTRL_ID_UNDO,                          NULL,
15918     TRUE, FALSE,                                "undo step"
15919   },
15920   {
15921     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15922     GAME_CTRL_ID_REDO,                          NULL,
15923     TRUE, FALSE,                                "redo step"
15924   },
15925   {
15926     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15927     GAME_CTRL_ID_SAVE,                          NULL,
15928     TRUE, FALSE,                                "save game"
15929   },
15930   {
15931     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15932     GAME_CTRL_ID_PAUSE2,                        NULL,
15933     TRUE, FALSE,                                "pause game"
15934   },
15935   {
15936     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15937     GAME_CTRL_ID_LOAD,                          NULL,
15938     TRUE, FALSE,                                "load game"
15939   },
15940   {
15941     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15942     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15943     FALSE, FALSE,                               "stop game"
15944   },
15945   {
15946     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15947     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15948     FALSE, FALSE,                               "pause game"
15949   },
15950   {
15951     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15952     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15953     FALSE, FALSE,                               "play game"
15954   },
15955   {
15956     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15957     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15958     FALSE, TRUE,                                "stop game"
15959   },
15960   {
15961     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15962     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15963     FALSE, TRUE,                                "pause game"
15964   },
15965   {
15966     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15967     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15968     TRUE, FALSE,                                "background music on/off"
15969   },
15970   {
15971     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15972     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15973     TRUE, FALSE,                                "sound loops on/off"
15974   },
15975   {
15976     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15977     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15978     TRUE, FALSE,                                "normal sounds on/off"
15979   },
15980   {
15981     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15982     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15983     FALSE, FALSE,                               "background music on/off"
15984   },
15985   {
15986     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15987     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15988     FALSE, FALSE,                               "sound loops on/off"
15989   },
15990   {
15991     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15992     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15993     FALSE, FALSE,                               "normal sounds on/off"
15994   }
15995 };
15996
15997 void CreateGameButtons(void)
15998 {
15999   int i;
16000
16001   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16002   {
16003     int graphic = gamebutton_info[i].graphic;
16004     struct GraphicInfo *gfx = &graphic_info[graphic];
16005     struct XY *pos = gamebutton_info[i].pos;
16006     struct GadgetInfo *gi;
16007     int button_type;
16008     boolean checked;
16009     unsigned int event_mask;
16010     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16011     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16012     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16013     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16014     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16015     int gd_x   = gfx->src_x;
16016     int gd_y   = gfx->src_y;
16017     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16018     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16019     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16020     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16021     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16022     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16023     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16024     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16025     int id = i;
16026
16027     if (gfx->bitmap == NULL)
16028     {
16029       game_gadget[id] = NULL;
16030
16031       continue;
16032     }
16033
16034     if (id == GAME_CTRL_ID_STOP ||
16035         id == GAME_CTRL_ID_PANEL_STOP ||
16036         id == GAME_CTRL_ID_TOUCH_STOP ||
16037         id == GAME_CTRL_ID_PLAY ||
16038         id == GAME_CTRL_ID_PANEL_PLAY ||
16039         id == GAME_CTRL_ID_SAVE ||
16040         id == GAME_CTRL_ID_LOAD)
16041     {
16042       button_type = GD_TYPE_NORMAL_BUTTON;
16043       checked = FALSE;
16044       event_mask = GD_EVENT_RELEASED;
16045     }
16046     else if (id == GAME_CTRL_ID_UNDO ||
16047              id == GAME_CTRL_ID_REDO)
16048     {
16049       button_type = GD_TYPE_NORMAL_BUTTON;
16050       checked = FALSE;
16051       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16052     }
16053     else
16054     {
16055       button_type = GD_TYPE_CHECK_BUTTON;
16056       checked = (gamebutton_info[i].setup_value != NULL ?
16057                  *gamebutton_info[i].setup_value : FALSE);
16058       event_mask = GD_EVENT_PRESSED;
16059     }
16060
16061     gi = CreateGadget(GDI_CUSTOM_ID, id,
16062                       GDI_IMAGE_ID, graphic,
16063                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16064                       GDI_X, base_x + x,
16065                       GDI_Y, base_y + y,
16066                       GDI_WIDTH, gfx->width,
16067                       GDI_HEIGHT, gfx->height,
16068                       GDI_TYPE, button_type,
16069                       GDI_STATE, GD_BUTTON_UNPRESSED,
16070                       GDI_CHECKED, checked,
16071                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16072                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16073                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16074                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16075                       GDI_DIRECT_DRAW, FALSE,
16076                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16077                       GDI_EVENT_MASK, event_mask,
16078                       GDI_CALLBACK_ACTION, HandleGameButtons,
16079                       GDI_END);
16080
16081     if (gi == NULL)
16082       Fail("cannot create gadget");
16083
16084     game_gadget[id] = gi;
16085   }
16086 }
16087
16088 void FreeGameButtons(void)
16089 {
16090   int i;
16091
16092   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16093     FreeGadget(game_gadget[i]);
16094 }
16095
16096 static void UnmapGameButtonsAtSamePosition(int id)
16097 {
16098   int i;
16099
16100   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16101     if (i != id &&
16102         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16103         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16104       UnmapGadget(game_gadget[i]);
16105 }
16106
16107 static void UnmapGameButtonsAtSamePosition_All(void)
16108 {
16109   if (setup.show_snapshot_buttons)
16110   {
16111     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16112     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16113     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16114   }
16115   else
16116   {
16117     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16118     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16119     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16120
16121     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16122     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16123     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16124   }
16125 }
16126
16127 static void MapGameButtonsAtSamePosition(int id)
16128 {
16129   int i;
16130
16131   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16132     if (i != id &&
16133         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16134         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16135       MapGadget(game_gadget[i]);
16136
16137   UnmapGameButtonsAtSamePosition_All();
16138 }
16139
16140 void MapUndoRedoButtons(void)
16141 {
16142   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16143   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16144
16145   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16146   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16147 }
16148
16149 void UnmapUndoRedoButtons(void)
16150 {
16151   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16152   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16153
16154   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16155   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16156 }
16157
16158 void ModifyPauseButtons(void)
16159 {
16160   static int ids[] =
16161   {
16162     GAME_CTRL_ID_PAUSE,
16163     GAME_CTRL_ID_PAUSE2,
16164     GAME_CTRL_ID_PANEL_PAUSE,
16165     GAME_CTRL_ID_TOUCH_PAUSE,
16166     -1
16167   };
16168   int i;
16169
16170   for (i = 0; ids[i] > -1; i++)
16171     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16172 }
16173
16174 static void MapGameButtonsExt(boolean on_tape)
16175 {
16176   int i;
16177
16178   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16179     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16180         i != GAME_CTRL_ID_UNDO &&
16181         i != GAME_CTRL_ID_REDO)
16182       MapGadget(game_gadget[i]);
16183
16184   UnmapGameButtonsAtSamePosition_All();
16185
16186   RedrawGameButtons();
16187 }
16188
16189 static void UnmapGameButtonsExt(boolean on_tape)
16190 {
16191   int i;
16192
16193   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16194     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16195       UnmapGadget(game_gadget[i]);
16196 }
16197
16198 static void RedrawGameButtonsExt(boolean on_tape)
16199 {
16200   int i;
16201
16202   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16203     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16204       RedrawGadget(game_gadget[i]);
16205 }
16206
16207 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16208 {
16209   if (gi == NULL)
16210     return;
16211
16212   gi->checked = state;
16213 }
16214
16215 static void RedrawSoundButtonGadget(int id)
16216 {
16217   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16218              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16219              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16220              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16221              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16222              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16223              id);
16224
16225   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16226   RedrawGadget(game_gadget[id2]);
16227 }
16228
16229 void MapGameButtons(void)
16230 {
16231   MapGameButtonsExt(FALSE);
16232 }
16233
16234 void UnmapGameButtons(void)
16235 {
16236   UnmapGameButtonsExt(FALSE);
16237 }
16238
16239 void RedrawGameButtons(void)
16240 {
16241   RedrawGameButtonsExt(FALSE);
16242 }
16243
16244 void MapGameButtonsOnTape(void)
16245 {
16246   MapGameButtonsExt(TRUE);
16247 }
16248
16249 void UnmapGameButtonsOnTape(void)
16250 {
16251   UnmapGameButtonsExt(TRUE);
16252 }
16253
16254 void RedrawGameButtonsOnTape(void)
16255 {
16256   RedrawGameButtonsExt(TRUE);
16257 }
16258
16259 static void GameUndoRedoExt(void)
16260 {
16261   ClearPlayerAction();
16262
16263   tape.pausing = TRUE;
16264
16265   RedrawPlayfield();
16266   UpdateAndDisplayGameControlValues();
16267
16268   DrawCompleteVideoDisplay();
16269   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16270   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16271   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16272
16273   BackToFront();
16274 }
16275
16276 static void GameUndo(int steps)
16277 {
16278   if (!CheckEngineSnapshotList())
16279     return;
16280
16281   LoadEngineSnapshot_Undo(steps);
16282
16283   GameUndoRedoExt();
16284 }
16285
16286 static void GameRedo(int steps)
16287 {
16288   if (!CheckEngineSnapshotList())
16289     return;
16290
16291   LoadEngineSnapshot_Redo(steps);
16292
16293   GameUndoRedoExt();
16294 }
16295
16296 static void HandleGameButtonsExt(int id, int button)
16297 {
16298   static boolean game_undo_executed = FALSE;
16299   int steps = BUTTON_STEPSIZE(button);
16300   boolean handle_game_buttons =
16301     (game_status == GAME_MODE_PLAYING ||
16302      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16303
16304   if (!handle_game_buttons)
16305     return;
16306
16307   switch (id)
16308   {
16309     case GAME_CTRL_ID_STOP:
16310     case GAME_CTRL_ID_PANEL_STOP:
16311     case GAME_CTRL_ID_TOUCH_STOP:
16312       if (game_status == GAME_MODE_MAIN)
16313         break;
16314
16315       if (tape.playing)
16316         TapeStop();
16317       else
16318         RequestQuitGame(FALSE);
16319
16320       break;
16321
16322     case GAME_CTRL_ID_PAUSE:
16323     case GAME_CTRL_ID_PAUSE2:
16324     case GAME_CTRL_ID_PANEL_PAUSE:
16325     case GAME_CTRL_ID_TOUCH_PAUSE:
16326       if (network.enabled && game_status == GAME_MODE_PLAYING)
16327       {
16328         if (tape.pausing)
16329           SendToServer_ContinuePlaying();
16330         else
16331           SendToServer_PausePlaying();
16332       }
16333       else
16334         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16335
16336       game_undo_executed = FALSE;
16337
16338       break;
16339
16340     case GAME_CTRL_ID_PLAY:
16341     case GAME_CTRL_ID_PANEL_PLAY:
16342       if (game_status == GAME_MODE_MAIN)
16343       {
16344         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16345       }
16346       else if (tape.pausing)
16347       {
16348         if (network.enabled)
16349           SendToServer_ContinuePlaying();
16350         else
16351           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16352       }
16353       break;
16354
16355     case GAME_CTRL_ID_UNDO:
16356       // Important: When using "save snapshot when collecting an item" mode,
16357       // load last (current) snapshot for first "undo" after pressing "pause"
16358       // (else the last-but-one snapshot would be loaded, because the snapshot
16359       // pointer already points to the last snapshot when pressing "pause",
16360       // which is fine for "every step/move" mode, but not for "every collect")
16361       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16362           !game_undo_executed)
16363         steps--;
16364
16365       game_undo_executed = TRUE;
16366
16367       GameUndo(steps);
16368       break;
16369
16370     case GAME_CTRL_ID_REDO:
16371       GameRedo(steps);
16372       break;
16373
16374     case GAME_CTRL_ID_SAVE:
16375       TapeQuickSave();
16376       break;
16377
16378     case GAME_CTRL_ID_LOAD:
16379       TapeQuickLoad();
16380       break;
16381
16382     case SOUND_CTRL_ID_MUSIC:
16383     case SOUND_CTRL_ID_PANEL_MUSIC:
16384       if (setup.sound_music)
16385       { 
16386         setup.sound_music = FALSE;
16387
16388         FadeMusic();
16389       }
16390       else if (audio.music_available)
16391       { 
16392         setup.sound = setup.sound_music = TRUE;
16393
16394         SetAudioMode(setup.sound);
16395
16396         if (game_status == GAME_MODE_PLAYING)
16397           PlayLevelMusic();
16398       }
16399
16400       RedrawSoundButtonGadget(id);
16401
16402       break;
16403
16404     case SOUND_CTRL_ID_LOOPS:
16405     case SOUND_CTRL_ID_PANEL_LOOPS:
16406       if (setup.sound_loops)
16407         setup.sound_loops = FALSE;
16408       else if (audio.loops_available)
16409       {
16410         setup.sound = setup.sound_loops = TRUE;
16411
16412         SetAudioMode(setup.sound);
16413       }
16414
16415       RedrawSoundButtonGadget(id);
16416
16417       break;
16418
16419     case SOUND_CTRL_ID_SIMPLE:
16420     case SOUND_CTRL_ID_PANEL_SIMPLE:
16421       if (setup.sound_simple)
16422         setup.sound_simple = FALSE;
16423       else if (audio.sound_available)
16424       {
16425         setup.sound = setup.sound_simple = TRUE;
16426
16427         SetAudioMode(setup.sound);
16428       }
16429
16430       RedrawSoundButtonGadget(id);
16431
16432       break;
16433
16434     default:
16435       break;
16436   }
16437 }
16438
16439 static void HandleGameButtons(struct GadgetInfo *gi)
16440 {
16441   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16442 }
16443
16444 void HandleSoundButtonKeys(Key key)
16445 {
16446   if (key == setup.shortcut.sound_simple)
16447     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16448   else if (key == setup.shortcut.sound_loops)
16449     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16450   else if (key == setup.shortcut.sound_music)
16451     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16452 }