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