added saving high scores to score server
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 int NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963   int highlight_position;
4964
4965   game.LevelSolved_GameEnd = TRUE;
4966
4967   if (game.LevelSolved_SaveTape)
4968   {
4969     // make sure that request dialog to save tape does not open door again
4970     if (!global.use_envelope_request)
4971       CloseDoor(DOOR_CLOSE_1);
4972
4973     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4974
4975     // set unique basename for score tape (also saved in high score table)
4976     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4977   }
4978
4979   // if no tape is to be saved, close both doors simultaneously
4980   CloseDoor(DOOR_CLOSE_ALL);
4981
4982   if (level_editor_test_game)
4983   {
4984     SetGameStatus(GAME_MODE_MAIN);
4985
4986     DrawMainMenu();
4987
4988     return;
4989   }
4990
4991   if (!game.LevelSolved_SaveScore)
4992   {
4993     SetGameStatus(GAME_MODE_MAIN);
4994
4995     DrawMainMenu();
4996
4997     return;
4998   }
4999
5000   if (level_nr == leveldir_current->handicap_level)
5001   {
5002     leveldir_current->handicap_level++;
5003
5004     SaveLevelSetup_SeriesInfo();
5005   }
5006
5007   // save score and score tape before potentially erasing tape below
5008   highlight_position = NewHighScore(last_level_nr);
5009
5010   if (setup.increment_levels &&
5011       level_nr < leveldir_current->last_level &&
5012       !network_playing)
5013   {
5014     level_nr++;         // advance to next level
5015     TapeErase();        // start with empty tape
5016
5017     if (setup.auto_play_next_level)
5018     {
5019       LoadLevel(level_nr);
5020
5021       SaveLevelSetup_SeriesInfo();
5022     }
5023   }
5024
5025   if (highlight_position >= 0 && setup.show_scores_after_game)
5026   {
5027     SetGameStatus(GAME_MODE_SCORES);
5028
5029     DrawHallOfFame(last_level_nr, highlight_position);
5030   }
5031   else if (setup.auto_play_next_level && setup.increment_levels &&
5032            last_level_nr < leveldir_current->last_level &&
5033            !network_playing)
5034   {
5035     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5036   }
5037   else
5038   {
5039     SetGameStatus(GAME_MODE_MAIN);
5040
5041     DrawMainMenu();
5042   }
5043 }
5044
5045 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
5046 {
5047   boolean one_score_entry_per_name = !program.many_scores_per_name;
5048   int i;
5049
5050   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME))
5051     return -1;
5052
5053   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5054   {
5055     struct ScoreEntry *entry = &list->entry[i];
5056     boolean score_is_better = (new_entry->score >  entry->score);
5057     boolean score_is_equal  = (new_entry->score == entry->score);
5058     boolean time_is_better  = (new_entry->time  <  entry->time);
5059     boolean time_is_equal   = (new_entry->time  == entry->time);
5060     boolean better_by_score = (score_is_better ||
5061                                (score_is_equal && time_is_better));
5062     boolean better_by_time  = (time_is_better ||
5063                                (time_is_equal && score_is_better));
5064     boolean is_better = (level.rate_time_over_score ? better_by_time :
5065                          better_by_score);
5066     boolean entry_is_empty = (entry->score == 0 &&
5067                               entry->time == 0);
5068
5069     if (is_better || entry_is_empty)
5070     {
5071       // player has made it to the hall of fame
5072
5073       if (i < MAX_SCORE_ENTRIES - 1)
5074       {
5075         int m = MAX_SCORE_ENTRIES - 1;
5076         int l;
5077
5078         if (one_score_entry_per_name)
5079         {
5080           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5081             if (strEqual(list->entry[l].name, setup.player_name))
5082               m = l;
5083
5084           if (m == i)   // player's new highscore overwrites his old one
5085             goto put_into_list;
5086         }
5087
5088         for (l = m; l > i; l--)
5089           list->entry[l] = list->entry[l - 1];
5090       }
5091
5092       put_into_list:
5093
5094       *entry = *new_entry;
5095
5096       return i;
5097     }
5098     else if (one_score_entry_per_name &&
5099              strEqual(entry->name, setup.player_name))
5100     {
5101       // player already in high score list with better score or time
5102
5103       return -1;
5104     }
5105   }
5106
5107   return -1;
5108 }
5109
5110 int NewHighScore(int level_nr)
5111 {
5112   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5113
5114   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5115   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5116
5117   new_entry.score = game.score_final;
5118   new_entry.time = game.score_time_final;
5119
5120   LoadScore(level_nr);
5121
5122   scores.last_added = addScoreEntry(&scores, &new_entry);
5123
5124   if (scores.last_added >= 0)
5125   {
5126     SaveScore(level_nr);
5127
5128     if (game.LevelSolved_SaveTape)
5129     {
5130       SaveScoreTape(level_nr);
5131       SaveServerScore(level_nr);
5132     }
5133   }
5134
5135   return scores.last_added;
5136 }
5137
5138 static int getElementMoveStepsizeExt(int x, int y, int direction)
5139 {
5140   int element = Tile[x][y];
5141   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5142   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5143   int horiz_move = (dx != 0);
5144   int sign = (horiz_move ? dx : dy);
5145   int step = sign * element_info[element].move_stepsize;
5146
5147   // special values for move stepsize for spring and things on conveyor belt
5148   if (horiz_move)
5149   {
5150     if (CAN_FALL(element) &&
5151         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5152       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5153     else if (element == EL_SPRING)
5154       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5155   }
5156
5157   return step;
5158 }
5159
5160 static int getElementMoveStepsize(int x, int y)
5161 {
5162   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5163 }
5164
5165 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5166 {
5167   if (player->GfxAction != action || player->GfxDir != dir)
5168   {
5169     player->GfxAction = action;
5170     player->GfxDir = dir;
5171     player->Frame = 0;
5172     player->StepFrame = 0;
5173   }
5174 }
5175
5176 static void ResetGfxFrame(int x, int y)
5177 {
5178   // profiling showed that "autotest" spends 10~20% of its time in this function
5179   if (DrawingDeactivatedField())
5180     return;
5181
5182   int element = Tile[x][y];
5183   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5184
5185   if (graphic_info[graphic].anim_global_sync)
5186     GfxFrame[x][y] = FrameCounter;
5187   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5188     GfxFrame[x][y] = CustomValue[x][y];
5189   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5190     GfxFrame[x][y] = element_info[element].collect_score;
5191   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5192     GfxFrame[x][y] = ChangeDelay[x][y];
5193 }
5194
5195 static void ResetGfxAnimation(int x, int y)
5196 {
5197   GfxAction[x][y] = ACTION_DEFAULT;
5198   GfxDir[x][y] = MovDir[x][y];
5199   GfxFrame[x][y] = 0;
5200
5201   ResetGfxFrame(x, y);
5202 }
5203
5204 static void ResetRandomAnimationValue(int x, int y)
5205 {
5206   GfxRandom[x][y] = INIT_GFX_RANDOM();
5207 }
5208
5209 static void InitMovingField(int x, int y, int direction)
5210 {
5211   int element = Tile[x][y];
5212   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5213   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5214   int newx = x + dx;
5215   int newy = y + dy;
5216   boolean is_moving_before, is_moving_after;
5217
5218   // check if element was/is moving or being moved before/after mode change
5219   is_moving_before = (WasJustMoving[x][y] != 0);
5220   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5221
5222   // reset animation only for moving elements which change direction of moving
5223   // or which just started or stopped moving
5224   // (else CEs with property "can move" / "not moving" are reset each frame)
5225   if (is_moving_before != is_moving_after ||
5226       direction != MovDir[x][y])
5227     ResetGfxAnimation(x, y);
5228
5229   MovDir[x][y] = direction;
5230   GfxDir[x][y] = direction;
5231
5232   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5233                      direction == MV_DOWN && CAN_FALL(element) ?
5234                      ACTION_FALLING : ACTION_MOVING);
5235
5236   // this is needed for CEs with property "can move" / "not moving"
5237
5238   if (is_moving_after)
5239   {
5240     if (Tile[newx][newy] == EL_EMPTY)
5241       Tile[newx][newy] = EL_BLOCKED;
5242
5243     MovDir[newx][newy] = MovDir[x][y];
5244
5245     CustomValue[newx][newy] = CustomValue[x][y];
5246
5247     GfxFrame[newx][newy] = GfxFrame[x][y];
5248     GfxRandom[newx][newy] = GfxRandom[x][y];
5249     GfxAction[newx][newy] = GfxAction[x][y];
5250     GfxDir[newx][newy] = GfxDir[x][y];
5251   }
5252 }
5253
5254 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5255 {
5256   int direction = MovDir[x][y];
5257   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5258   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5259
5260   *goes_to_x = newx;
5261   *goes_to_y = newy;
5262 }
5263
5264 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5265 {
5266   int oldx = x, oldy = y;
5267   int direction = MovDir[x][y];
5268
5269   if (direction == MV_LEFT)
5270     oldx++;
5271   else if (direction == MV_RIGHT)
5272     oldx--;
5273   else if (direction == MV_UP)
5274     oldy++;
5275   else if (direction == MV_DOWN)
5276     oldy--;
5277
5278   *comes_from_x = oldx;
5279   *comes_from_y = oldy;
5280 }
5281
5282 static int MovingOrBlocked2Element(int x, int y)
5283 {
5284   int element = Tile[x][y];
5285
5286   if (element == EL_BLOCKED)
5287   {
5288     int oldx, oldy;
5289
5290     Blocked2Moving(x, y, &oldx, &oldy);
5291     return Tile[oldx][oldy];
5292   }
5293   else
5294     return element;
5295 }
5296
5297 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5298 {
5299   // like MovingOrBlocked2Element(), but if element is moving
5300   // and (x,y) is the field the moving element is just leaving,
5301   // return EL_BLOCKED instead of the element value
5302   int element = Tile[x][y];
5303
5304   if (IS_MOVING(x, y))
5305   {
5306     if (element == EL_BLOCKED)
5307     {
5308       int oldx, oldy;
5309
5310       Blocked2Moving(x, y, &oldx, &oldy);
5311       return Tile[oldx][oldy];
5312     }
5313     else
5314       return EL_BLOCKED;
5315   }
5316   else
5317     return element;
5318 }
5319
5320 static void RemoveField(int x, int y)
5321 {
5322   Tile[x][y] = EL_EMPTY;
5323
5324   MovPos[x][y] = 0;
5325   MovDir[x][y] = 0;
5326   MovDelay[x][y] = 0;
5327
5328   CustomValue[x][y] = 0;
5329
5330   AmoebaNr[x][y] = 0;
5331   ChangeDelay[x][y] = 0;
5332   ChangePage[x][y] = -1;
5333   Pushed[x][y] = FALSE;
5334
5335   GfxElement[x][y] = EL_UNDEFINED;
5336   GfxAction[x][y] = ACTION_DEFAULT;
5337   GfxDir[x][y] = MV_NONE;
5338 }
5339
5340 static void RemoveMovingField(int x, int y)
5341 {
5342   int oldx = x, oldy = y, newx = x, newy = y;
5343   int element = Tile[x][y];
5344   int next_element = EL_UNDEFINED;
5345
5346   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5347     return;
5348
5349   if (IS_MOVING(x, y))
5350   {
5351     Moving2Blocked(x, y, &newx, &newy);
5352
5353     if (Tile[newx][newy] != EL_BLOCKED)
5354     {
5355       // element is moving, but target field is not free (blocked), but
5356       // already occupied by something different (example: acid pool);
5357       // in this case, only remove the moving field, but not the target
5358
5359       RemoveField(oldx, oldy);
5360
5361       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5362
5363       TEST_DrawLevelField(oldx, oldy);
5364
5365       return;
5366     }
5367   }
5368   else if (element == EL_BLOCKED)
5369   {
5370     Blocked2Moving(x, y, &oldx, &oldy);
5371     if (!IS_MOVING(oldx, oldy))
5372       return;
5373   }
5374
5375   if (element == EL_BLOCKED &&
5376       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5377        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5378        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5379        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5380        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5381        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5382     next_element = get_next_element(Tile[oldx][oldy]);
5383
5384   RemoveField(oldx, oldy);
5385   RemoveField(newx, newy);
5386
5387   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5388
5389   if (next_element != EL_UNDEFINED)
5390     Tile[oldx][oldy] = next_element;
5391
5392   TEST_DrawLevelField(oldx, oldy);
5393   TEST_DrawLevelField(newx, newy);
5394 }
5395
5396 void DrawDynamite(int x, int y)
5397 {
5398   int sx = SCREENX(x), sy = SCREENY(y);
5399   int graphic = el2img(Tile[x][y]);
5400   int frame;
5401
5402   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5403     return;
5404
5405   if (IS_WALKABLE_INSIDE(Back[x][y]))
5406     return;
5407
5408   if (Back[x][y])
5409     DrawLevelElement(x, y, Back[x][y]);
5410   else if (Store[x][y])
5411     DrawLevelElement(x, y, Store[x][y]);
5412   else if (game.use_masked_elements)
5413     DrawLevelElement(x, y, EL_EMPTY);
5414
5415   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5416
5417   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5418     DrawGraphicThruMask(sx, sy, graphic, frame);
5419   else
5420     DrawGraphic(sx, sy, graphic, frame);
5421 }
5422
5423 static void CheckDynamite(int x, int y)
5424 {
5425   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5426   {
5427     MovDelay[x][y]--;
5428
5429     if (MovDelay[x][y] != 0)
5430     {
5431       DrawDynamite(x, y);
5432       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5433
5434       return;
5435     }
5436   }
5437
5438   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5439
5440   Bang(x, y);
5441 }
5442
5443 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5444 {
5445   boolean num_checked_players = 0;
5446   int i;
5447
5448   for (i = 0; i < MAX_PLAYERS; i++)
5449   {
5450     if (stored_player[i].active)
5451     {
5452       int sx = stored_player[i].jx;
5453       int sy = stored_player[i].jy;
5454
5455       if (num_checked_players == 0)
5456       {
5457         *sx1 = *sx2 = sx;
5458         *sy1 = *sy2 = sy;
5459       }
5460       else
5461       {
5462         *sx1 = MIN(*sx1, sx);
5463         *sy1 = MIN(*sy1, sy);
5464         *sx2 = MAX(*sx2, sx);
5465         *sy2 = MAX(*sy2, sy);
5466       }
5467
5468       num_checked_players++;
5469     }
5470   }
5471 }
5472
5473 static boolean checkIfAllPlayersFitToScreen_RND(void)
5474 {
5475   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5476
5477   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5478
5479   return (sx2 - sx1 < SCR_FIELDX &&
5480           sy2 - sy1 < SCR_FIELDY);
5481 }
5482
5483 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5484 {
5485   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5486
5487   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5488
5489   *sx = (sx1 + sx2) / 2;
5490   *sy = (sy1 + sy2) / 2;
5491 }
5492
5493 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5494                                boolean center_screen, boolean quick_relocation)
5495 {
5496   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5497   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5498   boolean no_delay = (tape.warp_forward);
5499   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5500   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5501   int new_scroll_x, new_scroll_y;
5502
5503   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5504   {
5505     // case 1: quick relocation inside visible screen (without scrolling)
5506
5507     RedrawPlayfield();
5508
5509     return;
5510   }
5511
5512   if (!level.shifted_relocation || center_screen)
5513   {
5514     // relocation _with_ centering of screen
5515
5516     new_scroll_x = SCROLL_POSITION_X(x);
5517     new_scroll_y = SCROLL_POSITION_Y(y);
5518   }
5519   else
5520   {
5521     // relocation _without_ centering of screen
5522
5523     int center_scroll_x = SCROLL_POSITION_X(old_x);
5524     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5525     int offset_x = x + (scroll_x - center_scroll_x);
5526     int offset_y = y + (scroll_y - center_scroll_y);
5527
5528     // for new screen position, apply previous offset to center position
5529     new_scroll_x = SCROLL_POSITION_X(offset_x);
5530     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5531   }
5532
5533   if (quick_relocation)
5534   {
5535     // case 2: quick relocation (redraw without visible scrolling)
5536
5537     scroll_x = new_scroll_x;
5538     scroll_y = new_scroll_y;
5539
5540     RedrawPlayfield();
5541
5542     return;
5543   }
5544
5545   // case 3: visible relocation (with scrolling to new position)
5546
5547   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5548
5549   SetVideoFrameDelay(wait_delay_value);
5550
5551   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5552   {
5553     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5554     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5555
5556     if (dx == 0 && dy == 0)             // no scrolling needed at all
5557       break;
5558
5559     scroll_x -= dx;
5560     scroll_y -= dy;
5561
5562     // set values for horizontal/vertical screen scrolling (half tile size)
5563     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5564     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5565     int pos_x = dx * TILEX / 2;
5566     int pos_y = dy * TILEY / 2;
5567     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5568     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5569
5570     ScrollLevel(dx, dy);
5571     DrawAllPlayers();
5572
5573     // scroll in two steps of half tile size to make things smoother
5574     BlitScreenToBitmapExt_RND(window, fx, fy);
5575
5576     // scroll second step to align at full tile size
5577     BlitScreenToBitmap(window);
5578   }
5579
5580   DrawAllPlayers();
5581   BackToFront();
5582
5583   SetVideoFrameDelay(frame_delay_value_old);
5584 }
5585
5586 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5587 {
5588   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5589   int player_nr = GET_PLAYER_NR(el_player);
5590   struct PlayerInfo *player = &stored_player[player_nr];
5591   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5592   boolean no_delay = (tape.warp_forward);
5593   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5594   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5595   int old_jx = player->jx;
5596   int old_jy = player->jy;
5597   int old_element = Tile[old_jx][old_jy];
5598   int element = Tile[jx][jy];
5599   boolean player_relocated = (old_jx != jx || old_jy != jy);
5600
5601   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5602   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5603   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5604   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5605   int leave_side_horiz = move_dir_horiz;
5606   int leave_side_vert  = move_dir_vert;
5607   int enter_side = enter_side_horiz | enter_side_vert;
5608   int leave_side = leave_side_horiz | leave_side_vert;
5609
5610   if (player->buried)           // do not reanimate dead player
5611     return;
5612
5613   if (!player_relocated)        // no need to relocate the player
5614     return;
5615
5616   if (IS_PLAYER(jx, jy))        // player already placed at new position
5617   {
5618     RemoveField(jx, jy);        // temporarily remove newly placed player
5619     DrawLevelField(jx, jy);
5620   }
5621
5622   if (player->present)
5623   {
5624     while (player->MovPos)
5625     {
5626       ScrollPlayer(player, SCROLL_GO_ON);
5627       ScrollScreen(NULL, SCROLL_GO_ON);
5628
5629       AdvanceFrameAndPlayerCounters(player->index_nr);
5630
5631       DrawPlayer(player);
5632
5633       BackToFront_WithFrameDelay(wait_delay_value);
5634     }
5635
5636     DrawPlayer(player);         // needed here only to cleanup last field
5637     DrawLevelField(player->jx, player->jy);     // remove player graphic
5638
5639     player->is_moving = FALSE;
5640   }
5641
5642   if (IS_CUSTOM_ELEMENT(old_element))
5643     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5644                                CE_LEFT_BY_PLAYER,
5645                                player->index_bit, leave_side);
5646
5647   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5648                                       CE_PLAYER_LEAVES_X,
5649                                       player->index_bit, leave_side);
5650
5651   Tile[jx][jy] = el_player;
5652   InitPlayerField(jx, jy, el_player, TRUE);
5653
5654   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5655      possible that the relocation target field did not contain a player element,
5656      but a walkable element, to which the new player was relocated -- in this
5657      case, restore that (already initialized!) element on the player field */
5658   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5659   {
5660     Tile[jx][jy] = element;     // restore previously existing element
5661   }
5662
5663   // only visually relocate centered player
5664   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5665                      FALSE, level.instant_relocation);
5666
5667   TestIfPlayerTouchesBadThing(jx, jy);
5668   TestIfPlayerTouchesCustomElement(jx, jy);
5669
5670   if (IS_CUSTOM_ELEMENT(element))
5671     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5672                                player->index_bit, enter_side);
5673
5674   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5675                                       player->index_bit, enter_side);
5676
5677   if (player->is_switching)
5678   {
5679     /* ensure that relocation while still switching an element does not cause
5680        a new element to be treated as also switched directly after relocation
5681        (this is important for teleporter switches that teleport the player to
5682        a place where another teleporter switch is in the same direction, which
5683        would then incorrectly be treated as immediately switched before the
5684        direction key that caused the switch was released) */
5685
5686     player->switch_x += jx - old_jx;
5687     player->switch_y += jy - old_jy;
5688   }
5689 }
5690
5691 static void Explode(int ex, int ey, int phase, int mode)
5692 {
5693   int x, y;
5694   int last_phase;
5695   int border_element;
5696
5697   // !!! eliminate this variable !!!
5698   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5699
5700   if (game.explosions_delayed)
5701   {
5702     ExplodeField[ex][ey] = mode;
5703     return;
5704   }
5705
5706   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5707   {
5708     int center_element = Tile[ex][ey];
5709     int artwork_element, explosion_element;     // set these values later
5710
5711     // remove things displayed in background while burning dynamite
5712     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5713       Back[ex][ey] = 0;
5714
5715     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5716     {
5717       // put moving element to center field (and let it explode there)
5718       center_element = MovingOrBlocked2Element(ex, ey);
5719       RemoveMovingField(ex, ey);
5720       Tile[ex][ey] = center_element;
5721     }
5722
5723     // now "center_element" is finally determined -- set related values now
5724     artwork_element = center_element;           // for custom player artwork
5725     explosion_element = center_element;         // for custom player artwork
5726
5727     if (IS_PLAYER(ex, ey))
5728     {
5729       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5730
5731       artwork_element = stored_player[player_nr].artwork_element;
5732
5733       if (level.use_explosion_element[player_nr])
5734       {
5735         explosion_element = level.explosion_element[player_nr];
5736         artwork_element = explosion_element;
5737       }
5738     }
5739
5740     if (mode == EX_TYPE_NORMAL ||
5741         mode == EX_TYPE_CENTER ||
5742         mode == EX_TYPE_CROSS)
5743       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5744
5745     last_phase = element_info[explosion_element].explosion_delay + 1;
5746
5747     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5748     {
5749       int xx = x - ex + 1;
5750       int yy = y - ey + 1;
5751       int element;
5752
5753       if (!IN_LEV_FIELD(x, y) ||
5754           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5755           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5756         continue;
5757
5758       element = Tile[x][y];
5759
5760       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5761       {
5762         element = MovingOrBlocked2Element(x, y);
5763
5764         if (!IS_EXPLOSION_PROOF(element))
5765           RemoveMovingField(x, y);
5766       }
5767
5768       // indestructible elements can only explode in center (but not flames)
5769       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5770                                            mode == EX_TYPE_BORDER)) ||
5771           element == EL_FLAMES)
5772         continue;
5773
5774       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5775          behaviour, for example when touching a yamyam that explodes to rocks
5776          with active deadly shield, a rock is created under the player !!! */
5777       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5778 #if 0
5779       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5780           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5781            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5782 #else
5783       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5784 #endif
5785       {
5786         if (IS_ACTIVE_BOMB(element))
5787         {
5788           // re-activate things under the bomb like gate or penguin
5789           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5790           Back[x][y] = 0;
5791         }
5792
5793         continue;
5794       }
5795
5796       // save walkable background elements while explosion on same tile
5797       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5798           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5799         Back[x][y] = element;
5800
5801       // ignite explodable elements reached by other explosion
5802       if (element == EL_EXPLOSION)
5803         element = Store2[x][y];
5804
5805       if (AmoebaNr[x][y] &&
5806           (element == EL_AMOEBA_FULL ||
5807            element == EL_BD_AMOEBA ||
5808            element == EL_AMOEBA_GROWING))
5809       {
5810         AmoebaCnt[AmoebaNr[x][y]]--;
5811         AmoebaCnt2[AmoebaNr[x][y]]--;
5812       }
5813
5814       RemoveField(x, y);
5815
5816       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5817       {
5818         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5819
5820         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5821
5822         if (PLAYERINFO(ex, ey)->use_murphy)
5823           Store[x][y] = EL_EMPTY;
5824       }
5825
5826       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5827       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5828       else if (ELEM_IS_PLAYER(center_element))
5829         Store[x][y] = EL_EMPTY;
5830       else if (center_element == EL_YAMYAM)
5831         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5832       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5833         Store[x][y] = element_info[center_element].content.e[xx][yy];
5834 #if 1
5835       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5836       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5837       // otherwise) -- FIX THIS !!!
5838       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5839         Store[x][y] = element_info[element].content.e[1][1];
5840 #else
5841       else if (!CAN_EXPLODE(element))
5842         Store[x][y] = element_info[element].content.e[1][1];
5843 #endif
5844       else
5845         Store[x][y] = EL_EMPTY;
5846
5847       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5848           center_element == EL_AMOEBA_TO_DIAMOND)
5849         Store2[x][y] = element;
5850
5851       Tile[x][y] = EL_EXPLOSION;
5852       GfxElement[x][y] = artwork_element;
5853
5854       ExplodePhase[x][y] = 1;
5855       ExplodeDelay[x][y] = last_phase;
5856
5857       Stop[x][y] = TRUE;
5858     }
5859
5860     if (center_element == EL_YAMYAM)
5861       game.yamyam_content_nr =
5862         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5863
5864     return;
5865   }
5866
5867   if (Stop[ex][ey])
5868     return;
5869
5870   x = ex;
5871   y = ey;
5872
5873   if (phase == 1)
5874     GfxFrame[x][y] = 0;         // restart explosion animation
5875
5876   last_phase = ExplodeDelay[x][y];
5877
5878   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5879
5880   // this can happen if the player leaves an explosion just in time
5881   if (GfxElement[x][y] == EL_UNDEFINED)
5882     GfxElement[x][y] = EL_EMPTY;
5883
5884   border_element = Store2[x][y];
5885   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5886     border_element = StorePlayer[x][y];
5887
5888   if (phase == element_info[border_element].ignition_delay ||
5889       phase == last_phase)
5890   {
5891     boolean border_explosion = FALSE;
5892
5893     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5894         !PLAYER_EXPLOSION_PROTECTED(x, y))
5895     {
5896       KillPlayerUnlessExplosionProtected(x, y);
5897       border_explosion = TRUE;
5898     }
5899     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5900     {
5901       Tile[x][y] = Store2[x][y];
5902       Store2[x][y] = 0;
5903       Bang(x, y);
5904       border_explosion = TRUE;
5905     }
5906     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5907     {
5908       AmoebaToDiamond(x, y);
5909       Store2[x][y] = 0;
5910       border_explosion = TRUE;
5911     }
5912
5913     // if an element just explodes due to another explosion (chain-reaction),
5914     // do not immediately end the new explosion when it was the last frame of
5915     // the explosion (as it would be done in the following "if"-statement!)
5916     if (border_explosion && phase == last_phase)
5917       return;
5918   }
5919
5920   if (phase == last_phase)
5921   {
5922     int element;
5923
5924     element = Tile[x][y] = Store[x][y];
5925     Store[x][y] = Store2[x][y] = 0;
5926     GfxElement[x][y] = EL_UNDEFINED;
5927
5928     // player can escape from explosions and might therefore be still alive
5929     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5930         element <= EL_PLAYER_IS_EXPLODING_4)
5931     {
5932       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5933       int explosion_element = EL_PLAYER_1 + player_nr;
5934       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5935       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5936
5937       if (level.use_explosion_element[player_nr])
5938         explosion_element = level.explosion_element[player_nr];
5939
5940       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5941                     element_info[explosion_element].content.e[xx][yy]);
5942     }
5943
5944     // restore probably existing indestructible background element
5945     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5946       element = Tile[x][y] = Back[x][y];
5947     Back[x][y] = 0;
5948
5949     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5950     GfxDir[x][y] = MV_NONE;
5951     ChangeDelay[x][y] = 0;
5952     ChangePage[x][y] = -1;
5953
5954     CustomValue[x][y] = 0;
5955
5956     InitField_WithBug2(x, y, FALSE);
5957
5958     TEST_DrawLevelField(x, y);
5959
5960     TestIfElementTouchesCustomElement(x, y);
5961
5962     if (GFX_CRUMBLED(element))
5963       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5964
5965     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5966       StorePlayer[x][y] = 0;
5967
5968     if (ELEM_IS_PLAYER(element))
5969       RelocatePlayer(x, y, element);
5970   }
5971   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5972   {
5973     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5974     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5975
5976     if (phase == delay)
5977       TEST_DrawLevelFieldCrumbled(x, y);
5978
5979     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5980     {
5981       DrawLevelElement(x, y, Back[x][y]);
5982       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5983     }
5984     else if (IS_WALKABLE_UNDER(Back[x][y]))
5985     {
5986       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5987       DrawLevelElementThruMask(x, y, Back[x][y]);
5988     }
5989     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5990       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5991   }
5992 }
5993
5994 static void DynaExplode(int ex, int ey)
5995 {
5996   int i, j;
5997   int dynabomb_element = Tile[ex][ey];
5998   int dynabomb_size = 1;
5999   boolean dynabomb_xl = FALSE;
6000   struct PlayerInfo *player;
6001   static int xy[4][2] =
6002   {
6003     { 0, -1 },
6004     { -1, 0 },
6005     { +1, 0 },
6006     { 0, +1 }
6007   };
6008
6009   if (IS_ACTIVE_BOMB(dynabomb_element))
6010   {
6011     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6012     dynabomb_size = player->dynabomb_size;
6013     dynabomb_xl = player->dynabomb_xl;
6014     player->dynabombs_left++;
6015   }
6016
6017   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6018
6019   for (i = 0; i < NUM_DIRECTIONS; i++)
6020   {
6021     for (j = 1; j <= dynabomb_size; j++)
6022     {
6023       int x = ex + j * xy[i][0];
6024       int y = ey + j * xy[i][1];
6025       int element;
6026
6027       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6028         break;
6029
6030       element = Tile[x][y];
6031
6032       // do not restart explosions of fields with active bombs
6033       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6034         continue;
6035
6036       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6037
6038       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6039           !IS_DIGGABLE(element) && !dynabomb_xl)
6040         break;
6041     }
6042   }
6043 }
6044
6045 void Bang(int x, int y)
6046 {
6047   int element = MovingOrBlocked2Element(x, y);
6048   int explosion_type = EX_TYPE_NORMAL;
6049
6050   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6051   {
6052     struct PlayerInfo *player = PLAYERINFO(x, y);
6053
6054     element = Tile[x][y] = player->initial_element;
6055
6056     if (level.use_explosion_element[player->index_nr])
6057     {
6058       int explosion_element = level.explosion_element[player->index_nr];
6059
6060       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6061         explosion_type = EX_TYPE_CROSS;
6062       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6063         explosion_type = EX_TYPE_CENTER;
6064     }
6065   }
6066
6067   switch (element)
6068   {
6069     case EL_BUG:
6070     case EL_SPACESHIP:
6071     case EL_BD_BUTTERFLY:
6072     case EL_BD_FIREFLY:
6073     case EL_YAMYAM:
6074     case EL_DARK_YAMYAM:
6075     case EL_ROBOT:
6076     case EL_PACMAN:
6077     case EL_MOLE:
6078       RaiseScoreElement(element);
6079       break;
6080
6081     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6082     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6083     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6084     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6085     case EL_DYNABOMB_INCREASE_NUMBER:
6086     case EL_DYNABOMB_INCREASE_SIZE:
6087     case EL_DYNABOMB_INCREASE_POWER:
6088       explosion_type = EX_TYPE_DYNA;
6089       break;
6090
6091     case EL_DC_LANDMINE:
6092       explosion_type = EX_TYPE_CENTER;
6093       break;
6094
6095     case EL_PENGUIN:
6096     case EL_LAMP:
6097     case EL_LAMP_ACTIVE:
6098     case EL_AMOEBA_TO_DIAMOND:
6099       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6100         explosion_type = EX_TYPE_CENTER;
6101       break;
6102
6103     default:
6104       if (element_info[element].explosion_type == EXPLODES_CROSS)
6105         explosion_type = EX_TYPE_CROSS;
6106       else if (element_info[element].explosion_type == EXPLODES_1X1)
6107         explosion_type = EX_TYPE_CENTER;
6108       break;
6109   }
6110
6111   if (explosion_type == EX_TYPE_DYNA)
6112     DynaExplode(x, y);
6113   else
6114     Explode(x, y, EX_PHASE_START, explosion_type);
6115
6116   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6117 }
6118
6119 static void SplashAcid(int x, int y)
6120 {
6121   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6122       (!IN_LEV_FIELD(x - 1, y - 2) ||
6123        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6124     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6125
6126   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6127       (!IN_LEV_FIELD(x + 1, y - 2) ||
6128        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6129     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6130
6131   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6132 }
6133
6134 static void InitBeltMovement(void)
6135 {
6136   static int belt_base_element[4] =
6137   {
6138     EL_CONVEYOR_BELT_1_LEFT,
6139     EL_CONVEYOR_BELT_2_LEFT,
6140     EL_CONVEYOR_BELT_3_LEFT,
6141     EL_CONVEYOR_BELT_4_LEFT
6142   };
6143   static int belt_base_active_element[4] =
6144   {
6145     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6146     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6147     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6148     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6149   };
6150
6151   int x, y, i, j;
6152
6153   // set frame order for belt animation graphic according to belt direction
6154   for (i = 0; i < NUM_BELTS; i++)
6155   {
6156     int belt_nr = i;
6157
6158     for (j = 0; j < NUM_BELT_PARTS; j++)
6159     {
6160       int element = belt_base_active_element[belt_nr] + j;
6161       int graphic_1 = el2img(element);
6162       int graphic_2 = el2panelimg(element);
6163
6164       if (game.belt_dir[i] == MV_LEFT)
6165       {
6166         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6167         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6168       }
6169       else
6170       {
6171         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6172         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6173       }
6174     }
6175   }
6176
6177   SCAN_PLAYFIELD(x, y)
6178   {
6179     int element = Tile[x][y];
6180
6181     for (i = 0; i < NUM_BELTS; i++)
6182     {
6183       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6184       {
6185         int e_belt_nr = getBeltNrFromBeltElement(element);
6186         int belt_nr = i;
6187
6188         if (e_belt_nr == belt_nr)
6189         {
6190           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6191
6192           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6193         }
6194       }
6195     }
6196   }
6197 }
6198
6199 static void ToggleBeltSwitch(int x, int y)
6200 {
6201   static int belt_base_element[4] =
6202   {
6203     EL_CONVEYOR_BELT_1_LEFT,
6204     EL_CONVEYOR_BELT_2_LEFT,
6205     EL_CONVEYOR_BELT_3_LEFT,
6206     EL_CONVEYOR_BELT_4_LEFT
6207   };
6208   static int belt_base_active_element[4] =
6209   {
6210     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6211     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6212     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6213     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6214   };
6215   static int belt_base_switch_element[4] =
6216   {
6217     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6218     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6219     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6220     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6221   };
6222   static int belt_move_dir[4] =
6223   {
6224     MV_LEFT,
6225     MV_NONE,
6226     MV_RIGHT,
6227     MV_NONE,
6228   };
6229
6230   int element = Tile[x][y];
6231   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6232   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6233   int belt_dir = belt_move_dir[belt_dir_nr];
6234   int xx, yy, i;
6235
6236   if (!IS_BELT_SWITCH(element))
6237     return;
6238
6239   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6240   game.belt_dir[belt_nr] = belt_dir;
6241
6242   if (belt_dir_nr == 3)
6243     belt_dir_nr = 1;
6244
6245   // set frame order for belt animation graphic according to belt direction
6246   for (i = 0; i < NUM_BELT_PARTS; i++)
6247   {
6248     int element = belt_base_active_element[belt_nr] + i;
6249     int graphic_1 = el2img(element);
6250     int graphic_2 = el2panelimg(element);
6251
6252     if (belt_dir == MV_LEFT)
6253     {
6254       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6255       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6256     }
6257     else
6258     {
6259       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6260       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6261     }
6262   }
6263
6264   SCAN_PLAYFIELD(xx, yy)
6265   {
6266     int element = Tile[xx][yy];
6267
6268     if (IS_BELT_SWITCH(element))
6269     {
6270       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6271
6272       if (e_belt_nr == belt_nr)
6273       {
6274         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6275         TEST_DrawLevelField(xx, yy);
6276       }
6277     }
6278     else if (IS_BELT(element) && belt_dir != MV_NONE)
6279     {
6280       int e_belt_nr = getBeltNrFromBeltElement(element);
6281
6282       if (e_belt_nr == belt_nr)
6283       {
6284         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6285
6286         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6287         TEST_DrawLevelField(xx, yy);
6288       }
6289     }
6290     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6291     {
6292       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6293
6294       if (e_belt_nr == belt_nr)
6295       {
6296         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6297
6298         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6299         TEST_DrawLevelField(xx, yy);
6300       }
6301     }
6302   }
6303 }
6304
6305 static void ToggleSwitchgateSwitch(int x, int y)
6306 {
6307   int xx, yy;
6308
6309   game.switchgate_pos = !game.switchgate_pos;
6310
6311   SCAN_PLAYFIELD(xx, yy)
6312   {
6313     int element = Tile[xx][yy];
6314
6315     if (element == EL_SWITCHGATE_SWITCH_UP)
6316     {
6317       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6318       TEST_DrawLevelField(xx, yy);
6319     }
6320     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6321     {
6322       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6323       TEST_DrawLevelField(xx, yy);
6324     }
6325     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6326     {
6327       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6328       TEST_DrawLevelField(xx, yy);
6329     }
6330     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6331     {
6332       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6333       TEST_DrawLevelField(xx, yy);
6334     }
6335     else if (element == EL_SWITCHGATE_OPEN ||
6336              element == EL_SWITCHGATE_OPENING)
6337     {
6338       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6339
6340       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6341     }
6342     else if (element == EL_SWITCHGATE_CLOSED ||
6343              element == EL_SWITCHGATE_CLOSING)
6344     {
6345       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6346
6347       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6348     }
6349   }
6350 }
6351
6352 static int getInvisibleActiveFromInvisibleElement(int element)
6353 {
6354   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6355           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6356           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6357           element);
6358 }
6359
6360 static int getInvisibleFromInvisibleActiveElement(int element)
6361 {
6362   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6363           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6364           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6365           element);
6366 }
6367
6368 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6369 {
6370   int x, y;
6371
6372   SCAN_PLAYFIELD(x, y)
6373   {
6374     int element = Tile[x][y];
6375
6376     if (element == EL_LIGHT_SWITCH &&
6377         game.light_time_left > 0)
6378     {
6379       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6380       TEST_DrawLevelField(x, y);
6381     }
6382     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6383              game.light_time_left == 0)
6384     {
6385       Tile[x][y] = EL_LIGHT_SWITCH;
6386       TEST_DrawLevelField(x, y);
6387     }
6388     else if (element == EL_EMC_DRIPPER &&
6389              game.light_time_left > 0)
6390     {
6391       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6392       TEST_DrawLevelField(x, y);
6393     }
6394     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6395              game.light_time_left == 0)
6396     {
6397       Tile[x][y] = EL_EMC_DRIPPER;
6398       TEST_DrawLevelField(x, y);
6399     }
6400     else if (element == EL_INVISIBLE_STEELWALL ||
6401              element == EL_INVISIBLE_WALL ||
6402              element == EL_INVISIBLE_SAND)
6403     {
6404       if (game.light_time_left > 0)
6405         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6406
6407       TEST_DrawLevelField(x, y);
6408
6409       // uncrumble neighbour fields, if needed
6410       if (element == EL_INVISIBLE_SAND)
6411         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6412     }
6413     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6414              element == EL_INVISIBLE_WALL_ACTIVE ||
6415              element == EL_INVISIBLE_SAND_ACTIVE)
6416     {
6417       if (game.light_time_left == 0)
6418         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6419
6420       TEST_DrawLevelField(x, y);
6421
6422       // re-crumble neighbour fields, if needed
6423       if (element == EL_INVISIBLE_SAND)
6424         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6425     }
6426   }
6427 }
6428
6429 static void RedrawAllInvisibleElementsForLenses(void)
6430 {
6431   int x, y;
6432
6433   SCAN_PLAYFIELD(x, y)
6434   {
6435     int element = Tile[x][y];
6436
6437     if (element == EL_EMC_DRIPPER &&
6438         game.lenses_time_left > 0)
6439     {
6440       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6441       TEST_DrawLevelField(x, y);
6442     }
6443     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6444              game.lenses_time_left == 0)
6445     {
6446       Tile[x][y] = EL_EMC_DRIPPER;
6447       TEST_DrawLevelField(x, y);
6448     }
6449     else if (element == EL_INVISIBLE_STEELWALL ||
6450              element == EL_INVISIBLE_WALL ||
6451              element == EL_INVISIBLE_SAND)
6452     {
6453       if (game.lenses_time_left > 0)
6454         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6455
6456       TEST_DrawLevelField(x, y);
6457
6458       // uncrumble neighbour fields, if needed
6459       if (element == EL_INVISIBLE_SAND)
6460         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6461     }
6462     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6463              element == EL_INVISIBLE_WALL_ACTIVE ||
6464              element == EL_INVISIBLE_SAND_ACTIVE)
6465     {
6466       if (game.lenses_time_left == 0)
6467         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6468
6469       TEST_DrawLevelField(x, y);
6470
6471       // re-crumble neighbour fields, if needed
6472       if (element == EL_INVISIBLE_SAND)
6473         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6474     }
6475   }
6476 }
6477
6478 static void RedrawAllInvisibleElementsForMagnifier(void)
6479 {
6480   int x, y;
6481
6482   SCAN_PLAYFIELD(x, y)
6483   {
6484     int element = Tile[x][y];
6485
6486     if (element == EL_EMC_FAKE_GRASS &&
6487         game.magnify_time_left > 0)
6488     {
6489       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6490       TEST_DrawLevelField(x, y);
6491     }
6492     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6493              game.magnify_time_left == 0)
6494     {
6495       Tile[x][y] = EL_EMC_FAKE_GRASS;
6496       TEST_DrawLevelField(x, y);
6497     }
6498     else if (IS_GATE_GRAY(element) &&
6499              game.magnify_time_left > 0)
6500     {
6501       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6502                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6503                     IS_EM_GATE_GRAY(element) ?
6504                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6505                     IS_EMC_GATE_GRAY(element) ?
6506                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6507                     IS_DC_GATE_GRAY(element) ?
6508                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6509                     element);
6510       TEST_DrawLevelField(x, y);
6511     }
6512     else if (IS_GATE_GRAY_ACTIVE(element) &&
6513              game.magnify_time_left == 0)
6514     {
6515       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6516                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6517                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6518                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6519                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6520                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6521                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6522                     EL_DC_GATE_WHITE_GRAY :
6523                     element);
6524       TEST_DrawLevelField(x, y);
6525     }
6526   }
6527 }
6528
6529 static void ToggleLightSwitch(int x, int y)
6530 {
6531   int element = Tile[x][y];
6532
6533   game.light_time_left =
6534     (element == EL_LIGHT_SWITCH ?
6535      level.time_light * FRAMES_PER_SECOND : 0);
6536
6537   RedrawAllLightSwitchesAndInvisibleElements();
6538 }
6539
6540 static void ActivateTimegateSwitch(int x, int y)
6541 {
6542   int xx, yy;
6543
6544   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6545
6546   SCAN_PLAYFIELD(xx, yy)
6547   {
6548     int element = Tile[xx][yy];
6549
6550     if (element == EL_TIMEGATE_CLOSED ||
6551         element == EL_TIMEGATE_CLOSING)
6552     {
6553       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6554       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6555     }
6556
6557     /*
6558     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6559     {
6560       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6561       TEST_DrawLevelField(xx, yy);
6562     }
6563     */
6564
6565   }
6566
6567   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6568                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6569 }
6570
6571 static void Impact(int x, int y)
6572 {
6573   boolean last_line = (y == lev_fieldy - 1);
6574   boolean object_hit = FALSE;
6575   boolean impact = (last_line || object_hit);
6576   int element = Tile[x][y];
6577   int smashed = EL_STEELWALL;
6578
6579   if (!last_line)       // check if element below was hit
6580   {
6581     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6582       return;
6583
6584     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6585                                          MovDir[x][y + 1] != MV_DOWN ||
6586                                          MovPos[x][y + 1] <= TILEY / 2));
6587
6588     // do not smash moving elements that left the smashed field in time
6589     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6590         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6591       object_hit = FALSE;
6592
6593 #if USE_QUICKSAND_IMPACT_BUGFIX
6594     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6595     {
6596       RemoveMovingField(x, y + 1);
6597       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6598       Tile[x][y + 2] = EL_ROCK;
6599       TEST_DrawLevelField(x, y + 2);
6600
6601       object_hit = TRUE;
6602     }
6603
6604     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6605     {
6606       RemoveMovingField(x, y + 1);
6607       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6608       Tile[x][y + 2] = EL_ROCK;
6609       TEST_DrawLevelField(x, y + 2);
6610
6611       object_hit = TRUE;
6612     }
6613 #endif
6614
6615     if (object_hit)
6616       smashed = MovingOrBlocked2Element(x, y + 1);
6617
6618     impact = (last_line || object_hit);
6619   }
6620
6621   if (!last_line && smashed == EL_ACID) // element falls into acid
6622   {
6623     SplashAcid(x, y + 1);
6624     return;
6625   }
6626
6627   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6628   // only reset graphic animation if graphic really changes after impact
6629   if (impact &&
6630       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6631   {
6632     ResetGfxAnimation(x, y);
6633     TEST_DrawLevelField(x, y);
6634   }
6635
6636   if (impact && CAN_EXPLODE_IMPACT(element))
6637   {
6638     Bang(x, y);
6639     return;
6640   }
6641   else if (impact && element == EL_PEARL &&
6642            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6643   {
6644     ResetGfxAnimation(x, y);
6645
6646     Tile[x][y] = EL_PEARL_BREAKING;
6647     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6648     return;
6649   }
6650   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6651   {
6652     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6653
6654     return;
6655   }
6656
6657   if (impact && element == EL_AMOEBA_DROP)
6658   {
6659     if (object_hit && IS_PLAYER(x, y + 1))
6660       KillPlayerUnlessEnemyProtected(x, y + 1);
6661     else if (object_hit && smashed == EL_PENGUIN)
6662       Bang(x, y + 1);
6663     else
6664     {
6665       Tile[x][y] = EL_AMOEBA_GROWING;
6666       Store[x][y] = EL_AMOEBA_WET;
6667
6668       ResetRandomAnimationValue(x, y);
6669     }
6670     return;
6671   }
6672
6673   if (object_hit)               // check which object was hit
6674   {
6675     if ((CAN_PASS_MAGIC_WALL(element) && 
6676          (smashed == EL_MAGIC_WALL ||
6677           smashed == EL_BD_MAGIC_WALL)) ||
6678         (CAN_PASS_DC_MAGIC_WALL(element) &&
6679          smashed == EL_DC_MAGIC_WALL))
6680     {
6681       int xx, yy;
6682       int activated_magic_wall =
6683         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6684          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6685          EL_DC_MAGIC_WALL_ACTIVE);
6686
6687       // activate magic wall / mill
6688       SCAN_PLAYFIELD(xx, yy)
6689       {
6690         if (Tile[xx][yy] == smashed)
6691           Tile[xx][yy] = activated_magic_wall;
6692       }
6693
6694       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6695       game.magic_wall_active = TRUE;
6696
6697       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6698                             SND_MAGIC_WALL_ACTIVATING :
6699                             smashed == EL_BD_MAGIC_WALL ?
6700                             SND_BD_MAGIC_WALL_ACTIVATING :
6701                             SND_DC_MAGIC_WALL_ACTIVATING));
6702     }
6703
6704     if (IS_PLAYER(x, y + 1))
6705     {
6706       if (CAN_SMASH_PLAYER(element))
6707       {
6708         KillPlayerUnlessEnemyProtected(x, y + 1);
6709         return;
6710       }
6711     }
6712     else if (smashed == EL_PENGUIN)
6713     {
6714       if (CAN_SMASH_PLAYER(element))
6715       {
6716         Bang(x, y + 1);
6717         return;
6718       }
6719     }
6720     else if (element == EL_BD_DIAMOND)
6721     {
6722       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6723       {
6724         Bang(x, y + 1);
6725         return;
6726       }
6727     }
6728     else if (((element == EL_SP_INFOTRON ||
6729                element == EL_SP_ZONK) &&
6730               (smashed == EL_SP_SNIKSNAK ||
6731                smashed == EL_SP_ELECTRON ||
6732                smashed == EL_SP_DISK_ORANGE)) ||
6733              (element == EL_SP_INFOTRON &&
6734               smashed == EL_SP_DISK_YELLOW))
6735     {
6736       Bang(x, y + 1);
6737       return;
6738     }
6739     else if (CAN_SMASH_EVERYTHING(element))
6740     {
6741       if (IS_CLASSIC_ENEMY(smashed) ||
6742           CAN_EXPLODE_SMASHED(smashed))
6743       {
6744         Bang(x, y + 1);
6745         return;
6746       }
6747       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6748       {
6749         if (smashed == EL_LAMP ||
6750             smashed == EL_LAMP_ACTIVE)
6751         {
6752           Bang(x, y + 1);
6753           return;
6754         }
6755         else if (smashed == EL_NUT)
6756         {
6757           Tile[x][y + 1] = EL_NUT_BREAKING;
6758           PlayLevelSound(x, y, SND_NUT_BREAKING);
6759           RaiseScoreElement(EL_NUT);
6760           return;
6761         }
6762         else if (smashed == EL_PEARL)
6763         {
6764           ResetGfxAnimation(x, y);
6765
6766           Tile[x][y + 1] = EL_PEARL_BREAKING;
6767           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6768           return;
6769         }
6770         else if (smashed == EL_DIAMOND)
6771         {
6772           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6773           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6774           return;
6775         }
6776         else if (IS_BELT_SWITCH(smashed))
6777         {
6778           ToggleBeltSwitch(x, y + 1);
6779         }
6780         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6781                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6782                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6783                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6784         {
6785           ToggleSwitchgateSwitch(x, y + 1);
6786         }
6787         else if (smashed == EL_LIGHT_SWITCH ||
6788                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6789         {
6790           ToggleLightSwitch(x, y + 1);
6791         }
6792         else
6793         {
6794           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6795
6796           CheckElementChangeBySide(x, y + 1, smashed, element,
6797                                    CE_SWITCHED, CH_SIDE_TOP);
6798           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6799                                             CH_SIDE_TOP);
6800         }
6801       }
6802       else
6803       {
6804         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6805       }
6806     }
6807   }
6808
6809   // play sound of magic wall / mill
6810   if (!last_line &&
6811       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6812        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6813        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6814   {
6815     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6816       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6817     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6818       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6819     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6820       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6821
6822     return;
6823   }
6824
6825   // play sound of object that hits the ground
6826   if (last_line || object_hit)
6827     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6828 }
6829
6830 static void TurnRoundExt(int x, int y)
6831 {
6832   static struct
6833   {
6834     int dx, dy;
6835   } move_xy[] =
6836   {
6837     {  0,  0 },
6838     { -1,  0 },
6839     { +1,  0 },
6840     {  0,  0 },
6841     {  0, -1 },
6842     {  0,  0 }, { 0, 0 }, { 0, 0 },
6843     {  0, +1 }
6844   };
6845   static struct
6846   {
6847     int left, right, back;
6848   } turn[] =
6849   {
6850     { 0,        0,              0        },
6851     { MV_DOWN,  MV_UP,          MV_RIGHT },
6852     { MV_UP,    MV_DOWN,        MV_LEFT  },
6853     { 0,        0,              0        },
6854     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6855     { 0,        0,              0        },
6856     { 0,        0,              0        },
6857     { 0,        0,              0        },
6858     { MV_RIGHT, MV_LEFT,        MV_UP    }
6859   };
6860
6861   int element = Tile[x][y];
6862   int move_pattern = element_info[element].move_pattern;
6863
6864   int old_move_dir = MovDir[x][y];
6865   int left_dir  = turn[old_move_dir].left;
6866   int right_dir = turn[old_move_dir].right;
6867   int back_dir  = turn[old_move_dir].back;
6868
6869   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6870   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6871   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6872   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6873
6874   int left_x  = x + left_dx,  left_y  = y + left_dy;
6875   int right_x = x + right_dx, right_y = y + right_dy;
6876   int move_x  = x + move_dx,  move_y  = y + move_dy;
6877
6878   int xx, yy;
6879
6880   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6881   {
6882     TestIfBadThingTouchesOtherBadThing(x, y);
6883
6884     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6885       MovDir[x][y] = right_dir;
6886     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6887       MovDir[x][y] = left_dir;
6888
6889     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6890       MovDelay[x][y] = 9;
6891     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6892       MovDelay[x][y] = 1;
6893   }
6894   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6895   {
6896     TestIfBadThingTouchesOtherBadThing(x, y);
6897
6898     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6899       MovDir[x][y] = left_dir;
6900     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6901       MovDir[x][y] = right_dir;
6902
6903     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6904       MovDelay[x][y] = 9;
6905     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6906       MovDelay[x][y] = 1;
6907   }
6908   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6909   {
6910     TestIfBadThingTouchesOtherBadThing(x, y);
6911
6912     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6913       MovDir[x][y] = left_dir;
6914     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6915       MovDir[x][y] = right_dir;
6916
6917     if (MovDir[x][y] != old_move_dir)
6918       MovDelay[x][y] = 9;
6919   }
6920   else if (element == EL_YAMYAM)
6921   {
6922     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6923     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6924
6925     if (can_turn_left && can_turn_right)
6926       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6927     else if (can_turn_left)
6928       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6929     else if (can_turn_right)
6930       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6931     else
6932       MovDir[x][y] = back_dir;
6933
6934     MovDelay[x][y] = 16 + 16 * RND(3);
6935   }
6936   else if (element == EL_DARK_YAMYAM)
6937   {
6938     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6939                                                          left_x, left_y);
6940     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6941                                                          right_x, right_y);
6942
6943     if (can_turn_left && can_turn_right)
6944       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6945     else if (can_turn_left)
6946       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6947     else if (can_turn_right)
6948       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6949     else
6950       MovDir[x][y] = back_dir;
6951
6952     MovDelay[x][y] = 16 + 16 * RND(3);
6953   }
6954   else if (element == EL_PACMAN)
6955   {
6956     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6957     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6958
6959     if (can_turn_left && can_turn_right)
6960       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6961     else if (can_turn_left)
6962       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6963     else if (can_turn_right)
6964       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6965     else
6966       MovDir[x][y] = back_dir;
6967
6968     MovDelay[x][y] = 6 + RND(40);
6969   }
6970   else if (element == EL_PIG)
6971   {
6972     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6973     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6974     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6975     boolean should_turn_left, should_turn_right, should_move_on;
6976     int rnd_value = 24;
6977     int rnd = RND(rnd_value);
6978
6979     should_turn_left = (can_turn_left &&
6980                         (!can_move_on ||
6981                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6982                                                    y + back_dy + left_dy)));
6983     should_turn_right = (can_turn_right &&
6984                          (!can_move_on ||
6985                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6986                                                     y + back_dy + right_dy)));
6987     should_move_on = (can_move_on &&
6988                       (!can_turn_left ||
6989                        !can_turn_right ||
6990                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6991                                                  y + move_dy + left_dy) ||
6992                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6993                                                  y + move_dy + right_dy)));
6994
6995     if (should_turn_left || should_turn_right || should_move_on)
6996     {
6997       if (should_turn_left && should_turn_right && should_move_on)
6998         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6999                         rnd < 2 * rnd_value / 3 ? right_dir :
7000                         old_move_dir);
7001       else if (should_turn_left && should_turn_right)
7002         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7003       else if (should_turn_left && should_move_on)
7004         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7005       else if (should_turn_right && should_move_on)
7006         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7007       else if (should_turn_left)
7008         MovDir[x][y] = left_dir;
7009       else if (should_turn_right)
7010         MovDir[x][y] = right_dir;
7011       else if (should_move_on)
7012         MovDir[x][y] = old_move_dir;
7013     }
7014     else if (can_move_on && rnd > rnd_value / 8)
7015       MovDir[x][y] = old_move_dir;
7016     else if (can_turn_left && can_turn_right)
7017       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7018     else if (can_turn_left && rnd > rnd_value / 8)
7019       MovDir[x][y] = left_dir;
7020     else if (can_turn_right && rnd > rnd_value/8)
7021       MovDir[x][y] = right_dir;
7022     else
7023       MovDir[x][y] = back_dir;
7024
7025     xx = x + move_xy[MovDir[x][y]].dx;
7026     yy = y + move_xy[MovDir[x][y]].dy;
7027
7028     if (!IN_LEV_FIELD(xx, yy) ||
7029         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7030       MovDir[x][y] = old_move_dir;
7031
7032     MovDelay[x][y] = 0;
7033   }
7034   else if (element == EL_DRAGON)
7035   {
7036     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7037     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7038     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7039     int rnd_value = 24;
7040     int rnd = RND(rnd_value);
7041
7042     if (can_move_on && rnd > rnd_value / 8)
7043       MovDir[x][y] = old_move_dir;
7044     else if (can_turn_left && can_turn_right)
7045       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7046     else if (can_turn_left && rnd > rnd_value / 8)
7047       MovDir[x][y] = left_dir;
7048     else if (can_turn_right && rnd > rnd_value / 8)
7049       MovDir[x][y] = right_dir;
7050     else
7051       MovDir[x][y] = back_dir;
7052
7053     xx = x + move_xy[MovDir[x][y]].dx;
7054     yy = y + move_xy[MovDir[x][y]].dy;
7055
7056     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7057       MovDir[x][y] = old_move_dir;
7058
7059     MovDelay[x][y] = 0;
7060   }
7061   else if (element == EL_MOLE)
7062   {
7063     boolean can_move_on =
7064       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7065                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7066                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7067     if (!can_move_on)
7068     {
7069       boolean can_turn_left =
7070         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7071                               IS_AMOEBOID(Tile[left_x][left_y])));
7072
7073       boolean can_turn_right =
7074         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7075                               IS_AMOEBOID(Tile[right_x][right_y])));
7076
7077       if (can_turn_left && can_turn_right)
7078         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7079       else if (can_turn_left)
7080         MovDir[x][y] = left_dir;
7081       else
7082         MovDir[x][y] = right_dir;
7083     }
7084
7085     if (MovDir[x][y] != old_move_dir)
7086       MovDelay[x][y] = 9;
7087   }
7088   else if (element == EL_BALLOON)
7089   {
7090     MovDir[x][y] = game.wind_direction;
7091     MovDelay[x][y] = 0;
7092   }
7093   else if (element == EL_SPRING)
7094   {
7095     if (MovDir[x][y] & MV_HORIZONTAL)
7096     {
7097       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7098           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7099       {
7100         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7101         ResetGfxAnimation(move_x, move_y);
7102         TEST_DrawLevelField(move_x, move_y);
7103
7104         MovDir[x][y] = back_dir;
7105       }
7106       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7107                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7108         MovDir[x][y] = MV_NONE;
7109     }
7110
7111     MovDelay[x][y] = 0;
7112   }
7113   else if (element == EL_ROBOT ||
7114            element == EL_SATELLITE ||
7115            element == EL_PENGUIN ||
7116            element == EL_EMC_ANDROID)
7117   {
7118     int attr_x = -1, attr_y = -1;
7119
7120     if (game.all_players_gone)
7121     {
7122       attr_x = game.exit_x;
7123       attr_y = game.exit_y;
7124     }
7125     else
7126     {
7127       int i;
7128
7129       for (i = 0; i < MAX_PLAYERS; i++)
7130       {
7131         struct PlayerInfo *player = &stored_player[i];
7132         int jx = player->jx, jy = player->jy;
7133
7134         if (!player->active)
7135           continue;
7136
7137         if (attr_x == -1 ||
7138             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7139         {
7140           attr_x = jx;
7141           attr_y = jy;
7142         }
7143       }
7144     }
7145
7146     if (element == EL_ROBOT &&
7147         game.robot_wheel_x >= 0 &&
7148         game.robot_wheel_y >= 0 &&
7149         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7150          game.engine_version < VERSION_IDENT(3,1,0,0)))
7151     {
7152       attr_x = game.robot_wheel_x;
7153       attr_y = game.robot_wheel_y;
7154     }
7155
7156     if (element == EL_PENGUIN)
7157     {
7158       int i;
7159       static int xy[4][2] =
7160       {
7161         { 0, -1 },
7162         { -1, 0 },
7163         { +1, 0 },
7164         { 0, +1 }
7165       };
7166
7167       for (i = 0; i < NUM_DIRECTIONS; i++)
7168       {
7169         int ex = x + xy[i][0];
7170         int ey = y + xy[i][1];
7171
7172         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7173                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7174                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7175                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7176         {
7177           attr_x = ex;
7178           attr_y = ey;
7179           break;
7180         }
7181       }
7182     }
7183
7184     MovDir[x][y] = MV_NONE;
7185     if (attr_x < x)
7186       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7187     else if (attr_x > x)
7188       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7189     if (attr_y < y)
7190       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7191     else if (attr_y > y)
7192       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7193
7194     if (element == EL_ROBOT)
7195     {
7196       int newx, newy;
7197
7198       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7199         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7200       Moving2Blocked(x, y, &newx, &newy);
7201
7202       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7203         MovDelay[x][y] = 8 + 8 * !RND(3);
7204       else
7205         MovDelay[x][y] = 16;
7206     }
7207     else if (element == EL_PENGUIN)
7208     {
7209       int newx, newy;
7210
7211       MovDelay[x][y] = 1;
7212
7213       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7214       {
7215         boolean first_horiz = RND(2);
7216         int new_move_dir = MovDir[x][y];
7217
7218         MovDir[x][y] =
7219           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7220         Moving2Blocked(x, y, &newx, &newy);
7221
7222         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7223           return;
7224
7225         MovDir[x][y] =
7226           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7227         Moving2Blocked(x, y, &newx, &newy);
7228
7229         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7230           return;
7231
7232         MovDir[x][y] = old_move_dir;
7233         return;
7234       }
7235     }
7236     else if (element == EL_SATELLITE)
7237     {
7238       int newx, newy;
7239
7240       MovDelay[x][y] = 1;
7241
7242       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7243       {
7244         boolean first_horiz = RND(2);
7245         int new_move_dir = MovDir[x][y];
7246
7247         MovDir[x][y] =
7248           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7249         Moving2Blocked(x, y, &newx, &newy);
7250
7251         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7252           return;
7253
7254         MovDir[x][y] =
7255           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7256         Moving2Blocked(x, y, &newx, &newy);
7257
7258         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7259           return;
7260
7261         MovDir[x][y] = old_move_dir;
7262         return;
7263       }
7264     }
7265     else if (element == EL_EMC_ANDROID)
7266     {
7267       static int check_pos[16] =
7268       {
7269         -1,             //  0 => (invalid)
7270         7,              //  1 => MV_LEFT
7271         3,              //  2 => MV_RIGHT
7272         -1,             //  3 => (invalid)
7273         1,              //  4 =>            MV_UP
7274         0,              //  5 => MV_LEFT  | MV_UP
7275         2,              //  6 => MV_RIGHT | MV_UP
7276         -1,             //  7 => (invalid)
7277         5,              //  8 =>            MV_DOWN
7278         6,              //  9 => MV_LEFT  | MV_DOWN
7279         4,              // 10 => MV_RIGHT | MV_DOWN
7280         -1,             // 11 => (invalid)
7281         -1,             // 12 => (invalid)
7282         -1,             // 13 => (invalid)
7283         -1,             // 14 => (invalid)
7284         -1,             // 15 => (invalid)
7285       };
7286       static struct
7287       {
7288         int dx, dy;
7289         int dir;
7290       } check_xy[8] =
7291       {
7292         { -1, -1,       MV_LEFT  | MV_UP   },
7293         {  0, -1,                  MV_UP   },
7294         { +1, -1,       MV_RIGHT | MV_UP   },
7295         { +1,  0,       MV_RIGHT           },
7296         { +1, +1,       MV_RIGHT | MV_DOWN },
7297         {  0, +1,                  MV_DOWN },
7298         { -1, +1,       MV_LEFT  | MV_DOWN },
7299         { -1,  0,       MV_LEFT            },
7300       };
7301       int start_pos, check_order;
7302       boolean can_clone = FALSE;
7303       int i;
7304
7305       // check if there is any free field around current position
7306       for (i = 0; i < 8; i++)
7307       {
7308         int newx = x + check_xy[i].dx;
7309         int newy = y + check_xy[i].dy;
7310
7311         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7312         {
7313           can_clone = TRUE;
7314
7315           break;
7316         }
7317       }
7318
7319       if (can_clone)            // randomly find an element to clone
7320       {
7321         can_clone = FALSE;
7322
7323         start_pos = check_pos[RND(8)];
7324         check_order = (RND(2) ? -1 : +1);
7325
7326         for (i = 0; i < 8; i++)
7327         {
7328           int pos_raw = start_pos + i * check_order;
7329           int pos = (pos_raw + 8) % 8;
7330           int newx = x + check_xy[pos].dx;
7331           int newy = y + check_xy[pos].dy;
7332
7333           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7334           {
7335             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7336             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7337
7338             Store[x][y] = Tile[newx][newy];
7339
7340             can_clone = TRUE;
7341
7342             break;
7343           }
7344         }
7345       }
7346
7347       if (can_clone)            // randomly find a direction to move
7348       {
7349         can_clone = FALSE;
7350
7351         start_pos = check_pos[RND(8)];
7352         check_order = (RND(2) ? -1 : +1);
7353
7354         for (i = 0; i < 8; i++)
7355         {
7356           int pos_raw = start_pos + i * check_order;
7357           int pos = (pos_raw + 8) % 8;
7358           int newx = x + check_xy[pos].dx;
7359           int newy = y + check_xy[pos].dy;
7360           int new_move_dir = check_xy[pos].dir;
7361
7362           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7363           {
7364             MovDir[x][y] = new_move_dir;
7365             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7366
7367             can_clone = TRUE;
7368
7369             break;
7370           }
7371         }
7372       }
7373
7374       if (can_clone)            // cloning and moving successful
7375         return;
7376
7377       // cannot clone -- try to move towards player
7378
7379       start_pos = check_pos[MovDir[x][y] & 0x0f];
7380       check_order = (RND(2) ? -1 : +1);
7381
7382       for (i = 0; i < 3; i++)
7383       {
7384         // first check start_pos, then previous/next or (next/previous) pos
7385         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7386         int pos = (pos_raw + 8) % 8;
7387         int newx = x + check_xy[pos].dx;
7388         int newy = y + check_xy[pos].dy;
7389         int new_move_dir = check_xy[pos].dir;
7390
7391         if (IS_PLAYER(newx, newy))
7392           break;
7393
7394         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7395         {
7396           MovDir[x][y] = new_move_dir;
7397           MovDelay[x][y] = level.android_move_time * 8 + 1;
7398
7399           break;
7400         }
7401       }
7402     }
7403   }
7404   else if (move_pattern == MV_TURNING_LEFT ||
7405            move_pattern == MV_TURNING_RIGHT ||
7406            move_pattern == MV_TURNING_LEFT_RIGHT ||
7407            move_pattern == MV_TURNING_RIGHT_LEFT ||
7408            move_pattern == MV_TURNING_RANDOM ||
7409            move_pattern == MV_ALL_DIRECTIONS)
7410   {
7411     boolean can_turn_left =
7412       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7413     boolean can_turn_right =
7414       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7415
7416     if (element_info[element].move_stepsize == 0)       // "not moving"
7417       return;
7418
7419     if (move_pattern == MV_TURNING_LEFT)
7420       MovDir[x][y] = left_dir;
7421     else if (move_pattern == MV_TURNING_RIGHT)
7422       MovDir[x][y] = right_dir;
7423     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7424       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7425     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7426       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7427     else if (move_pattern == MV_TURNING_RANDOM)
7428       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7429                       can_turn_right && !can_turn_left ? right_dir :
7430                       RND(2) ? left_dir : right_dir);
7431     else if (can_turn_left && can_turn_right)
7432       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7433     else if (can_turn_left)
7434       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7435     else if (can_turn_right)
7436       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7437     else
7438       MovDir[x][y] = back_dir;
7439
7440     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7441   }
7442   else if (move_pattern == MV_HORIZONTAL ||
7443            move_pattern == MV_VERTICAL)
7444   {
7445     if (move_pattern & old_move_dir)
7446       MovDir[x][y] = back_dir;
7447     else if (move_pattern == MV_HORIZONTAL)
7448       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7449     else if (move_pattern == MV_VERTICAL)
7450       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7451
7452     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7453   }
7454   else if (move_pattern & MV_ANY_DIRECTION)
7455   {
7456     MovDir[x][y] = move_pattern;
7457     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7458   }
7459   else if (move_pattern & MV_WIND_DIRECTION)
7460   {
7461     MovDir[x][y] = game.wind_direction;
7462     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7463   }
7464   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7465   {
7466     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7467       MovDir[x][y] = left_dir;
7468     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7469       MovDir[x][y] = right_dir;
7470
7471     if (MovDir[x][y] != old_move_dir)
7472       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7473   }
7474   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7475   {
7476     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7477       MovDir[x][y] = right_dir;
7478     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7479       MovDir[x][y] = left_dir;
7480
7481     if (MovDir[x][y] != old_move_dir)
7482       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7483   }
7484   else if (move_pattern == MV_TOWARDS_PLAYER ||
7485            move_pattern == MV_AWAY_FROM_PLAYER)
7486   {
7487     int attr_x = -1, attr_y = -1;
7488     int newx, newy;
7489     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7490
7491     if (game.all_players_gone)
7492     {
7493       attr_x = game.exit_x;
7494       attr_y = game.exit_y;
7495     }
7496     else
7497     {
7498       int i;
7499
7500       for (i = 0; i < MAX_PLAYERS; i++)
7501       {
7502         struct PlayerInfo *player = &stored_player[i];
7503         int jx = player->jx, jy = player->jy;
7504
7505         if (!player->active)
7506           continue;
7507
7508         if (attr_x == -1 ||
7509             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7510         {
7511           attr_x = jx;
7512           attr_y = jy;
7513         }
7514       }
7515     }
7516
7517     MovDir[x][y] = MV_NONE;
7518     if (attr_x < x)
7519       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7520     else if (attr_x > x)
7521       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7522     if (attr_y < y)
7523       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7524     else if (attr_y > y)
7525       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7526
7527     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7528
7529     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7530     {
7531       boolean first_horiz = RND(2);
7532       int new_move_dir = MovDir[x][y];
7533
7534       if (element_info[element].move_stepsize == 0)     // "not moving"
7535       {
7536         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7537         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7538
7539         return;
7540       }
7541
7542       MovDir[x][y] =
7543         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7544       Moving2Blocked(x, y, &newx, &newy);
7545
7546       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7547         return;
7548
7549       MovDir[x][y] =
7550         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7551       Moving2Blocked(x, y, &newx, &newy);
7552
7553       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7554         return;
7555
7556       MovDir[x][y] = old_move_dir;
7557     }
7558   }
7559   else if (move_pattern == MV_WHEN_PUSHED ||
7560            move_pattern == MV_WHEN_DROPPED)
7561   {
7562     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7563       MovDir[x][y] = MV_NONE;
7564
7565     MovDelay[x][y] = 0;
7566   }
7567   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7568   {
7569     static int test_xy[7][2] =
7570     {
7571       { 0, -1 },
7572       { -1, 0 },
7573       { +1, 0 },
7574       { 0, +1 },
7575       { 0, -1 },
7576       { -1, 0 },
7577       { +1, 0 },
7578     };
7579     static int test_dir[7] =
7580     {
7581       MV_UP,
7582       MV_LEFT,
7583       MV_RIGHT,
7584       MV_DOWN,
7585       MV_UP,
7586       MV_LEFT,
7587       MV_RIGHT,
7588     };
7589     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7590     int move_preference = -1000000;     // start with very low preference
7591     int new_move_dir = MV_NONE;
7592     int start_test = RND(4);
7593     int i;
7594
7595     for (i = 0; i < NUM_DIRECTIONS; i++)
7596     {
7597       int move_dir = test_dir[start_test + i];
7598       int move_dir_preference;
7599
7600       xx = x + test_xy[start_test + i][0];
7601       yy = y + test_xy[start_test + i][1];
7602
7603       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7604           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7605       {
7606         new_move_dir = move_dir;
7607
7608         break;
7609       }
7610
7611       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7612         continue;
7613
7614       move_dir_preference = -1 * RunnerVisit[xx][yy];
7615       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7616         move_dir_preference = PlayerVisit[xx][yy];
7617
7618       if (move_dir_preference > move_preference)
7619       {
7620         // prefer field that has not been visited for the longest time
7621         move_preference = move_dir_preference;
7622         new_move_dir = move_dir;
7623       }
7624       else if (move_dir_preference == move_preference &&
7625                move_dir == old_move_dir)
7626       {
7627         // prefer last direction when all directions are preferred equally
7628         move_preference = move_dir_preference;
7629         new_move_dir = move_dir;
7630       }
7631     }
7632
7633     MovDir[x][y] = new_move_dir;
7634     if (old_move_dir != new_move_dir)
7635       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7636   }
7637 }
7638
7639 static void TurnRound(int x, int y)
7640 {
7641   int direction = MovDir[x][y];
7642
7643   TurnRoundExt(x, y);
7644
7645   GfxDir[x][y] = MovDir[x][y];
7646
7647   if (direction != MovDir[x][y])
7648     GfxFrame[x][y] = 0;
7649
7650   if (MovDelay[x][y])
7651     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7652
7653   ResetGfxFrame(x, y);
7654 }
7655
7656 static boolean JustBeingPushed(int x, int y)
7657 {
7658   int i;
7659
7660   for (i = 0; i < MAX_PLAYERS; i++)
7661   {
7662     struct PlayerInfo *player = &stored_player[i];
7663
7664     if (player->active && player->is_pushing && player->MovPos)
7665     {
7666       int next_jx = player->jx + (player->jx - player->last_jx);
7667       int next_jy = player->jy + (player->jy - player->last_jy);
7668
7669       if (x == next_jx && y == next_jy)
7670         return TRUE;
7671     }
7672   }
7673
7674   return FALSE;
7675 }
7676
7677 static void StartMoving(int x, int y)
7678 {
7679   boolean started_moving = FALSE;       // some elements can fall _and_ move
7680   int element = Tile[x][y];
7681
7682   if (Stop[x][y])
7683     return;
7684
7685   if (MovDelay[x][y] == 0)
7686     GfxAction[x][y] = ACTION_DEFAULT;
7687
7688   if (CAN_FALL(element) && y < lev_fieldy - 1)
7689   {
7690     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7691         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7692       if (JustBeingPushed(x, y))
7693         return;
7694
7695     if (element == EL_QUICKSAND_FULL)
7696     {
7697       if (IS_FREE(x, y + 1))
7698       {
7699         InitMovingField(x, y, MV_DOWN);
7700         started_moving = TRUE;
7701
7702         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7703 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7704         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7705           Store[x][y] = EL_ROCK;
7706 #else
7707         Store[x][y] = EL_ROCK;
7708 #endif
7709
7710         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7711       }
7712       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7713       {
7714         if (!MovDelay[x][y])
7715         {
7716           MovDelay[x][y] = TILEY + 1;
7717
7718           ResetGfxAnimation(x, y);
7719           ResetGfxAnimation(x, y + 1);
7720         }
7721
7722         if (MovDelay[x][y])
7723         {
7724           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7725           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7726
7727           MovDelay[x][y]--;
7728           if (MovDelay[x][y])
7729             return;
7730         }
7731
7732         Tile[x][y] = EL_QUICKSAND_EMPTY;
7733         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7734         Store[x][y + 1] = Store[x][y];
7735         Store[x][y] = 0;
7736
7737         PlayLevelSoundAction(x, y, ACTION_FILLING);
7738       }
7739       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7740       {
7741         if (!MovDelay[x][y])
7742         {
7743           MovDelay[x][y] = TILEY + 1;
7744
7745           ResetGfxAnimation(x, y);
7746           ResetGfxAnimation(x, y + 1);
7747         }
7748
7749         if (MovDelay[x][y])
7750         {
7751           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7752           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7753
7754           MovDelay[x][y]--;
7755           if (MovDelay[x][y])
7756             return;
7757         }
7758
7759         Tile[x][y] = EL_QUICKSAND_EMPTY;
7760         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7761         Store[x][y + 1] = Store[x][y];
7762         Store[x][y] = 0;
7763
7764         PlayLevelSoundAction(x, y, ACTION_FILLING);
7765       }
7766     }
7767     else if (element == EL_QUICKSAND_FAST_FULL)
7768     {
7769       if (IS_FREE(x, y + 1))
7770       {
7771         InitMovingField(x, y, MV_DOWN);
7772         started_moving = TRUE;
7773
7774         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7775 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7776         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7777           Store[x][y] = EL_ROCK;
7778 #else
7779         Store[x][y] = EL_ROCK;
7780 #endif
7781
7782         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7783       }
7784       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7785       {
7786         if (!MovDelay[x][y])
7787         {
7788           MovDelay[x][y] = TILEY + 1;
7789
7790           ResetGfxAnimation(x, y);
7791           ResetGfxAnimation(x, y + 1);
7792         }
7793
7794         if (MovDelay[x][y])
7795         {
7796           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7797           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7798
7799           MovDelay[x][y]--;
7800           if (MovDelay[x][y])
7801             return;
7802         }
7803
7804         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7805         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7806         Store[x][y + 1] = Store[x][y];
7807         Store[x][y] = 0;
7808
7809         PlayLevelSoundAction(x, y, ACTION_FILLING);
7810       }
7811       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7812       {
7813         if (!MovDelay[x][y])
7814         {
7815           MovDelay[x][y] = TILEY + 1;
7816
7817           ResetGfxAnimation(x, y);
7818           ResetGfxAnimation(x, y + 1);
7819         }
7820
7821         if (MovDelay[x][y])
7822         {
7823           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7824           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7825
7826           MovDelay[x][y]--;
7827           if (MovDelay[x][y])
7828             return;
7829         }
7830
7831         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7832         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7833         Store[x][y + 1] = Store[x][y];
7834         Store[x][y] = 0;
7835
7836         PlayLevelSoundAction(x, y, ACTION_FILLING);
7837       }
7838     }
7839     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7840              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7841     {
7842       InitMovingField(x, y, MV_DOWN);
7843       started_moving = TRUE;
7844
7845       Tile[x][y] = EL_QUICKSAND_FILLING;
7846       Store[x][y] = element;
7847
7848       PlayLevelSoundAction(x, y, ACTION_FILLING);
7849     }
7850     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7851              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7852     {
7853       InitMovingField(x, y, MV_DOWN);
7854       started_moving = TRUE;
7855
7856       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7857       Store[x][y] = element;
7858
7859       PlayLevelSoundAction(x, y, ACTION_FILLING);
7860     }
7861     else if (element == EL_MAGIC_WALL_FULL)
7862     {
7863       if (IS_FREE(x, y + 1))
7864       {
7865         InitMovingField(x, y, MV_DOWN);
7866         started_moving = TRUE;
7867
7868         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7869         Store[x][y] = EL_CHANGED(Store[x][y]);
7870       }
7871       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7872       {
7873         if (!MovDelay[x][y])
7874           MovDelay[x][y] = TILEY / 4 + 1;
7875
7876         if (MovDelay[x][y])
7877         {
7878           MovDelay[x][y]--;
7879           if (MovDelay[x][y])
7880             return;
7881         }
7882
7883         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7884         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7885         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7886         Store[x][y] = 0;
7887       }
7888     }
7889     else if (element == EL_BD_MAGIC_WALL_FULL)
7890     {
7891       if (IS_FREE(x, y + 1))
7892       {
7893         InitMovingField(x, y, MV_DOWN);
7894         started_moving = TRUE;
7895
7896         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7897         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7898       }
7899       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7900       {
7901         if (!MovDelay[x][y])
7902           MovDelay[x][y] = TILEY / 4 + 1;
7903
7904         if (MovDelay[x][y])
7905         {
7906           MovDelay[x][y]--;
7907           if (MovDelay[x][y])
7908             return;
7909         }
7910
7911         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7912         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7913         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7914         Store[x][y] = 0;
7915       }
7916     }
7917     else if (element == EL_DC_MAGIC_WALL_FULL)
7918     {
7919       if (IS_FREE(x, y + 1))
7920       {
7921         InitMovingField(x, y, MV_DOWN);
7922         started_moving = TRUE;
7923
7924         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7925         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7926       }
7927       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7928       {
7929         if (!MovDelay[x][y])
7930           MovDelay[x][y] = TILEY / 4 + 1;
7931
7932         if (MovDelay[x][y])
7933         {
7934           MovDelay[x][y]--;
7935           if (MovDelay[x][y])
7936             return;
7937         }
7938
7939         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7940         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7941         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7942         Store[x][y] = 0;
7943       }
7944     }
7945     else if ((CAN_PASS_MAGIC_WALL(element) &&
7946               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7947                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7948              (CAN_PASS_DC_MAGIC_WALL(element) &&
7949               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7950
7951     {
7952       InitMovingField(x, y, MV_DOWN);
7953       started_moving = TRUE;
7954
7955       Tile[x][y] =
7956         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7957          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7958          EL_DC_MAGIC_WALL_FILLING);
7959       Store[x][y] = element;
7960     }
7961     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7962     {
7963       SplashAcid(x, y + 1);
7964
7965       InitMovingField(x, y, MV_DOWN);
7966       started_moving = TRUE;
7967
7968       Store[x][y] = EL_ACID;
7969     }
7970     else if (
7971              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7972               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7973              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7974               CAN_FALL(element) && WasJustFalling[x][y] &&
7975               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7976
7977              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7978               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7979               (Tile[x][y + 1] == EL_BLOCKED)))
7980     {
7981       /* this is needed for a special case not covered by calling "Impact()"
7982          from "ContinueMoving()": if an element moves to a tile directly below
7983          another element which was just falling on that tile (which was empty
7984          in the previous frame), the falling element above would just stop
7985          instead of smashing the element below (in previous version, the above
7986          element was just checked for "moving" instead of "falling", resulting
7987          in incorrect smashes caused by horizontal movement of the above
7988          element; also, the case of the player being the element to smash was
7989          simply not covered here... :-/ ) */
7990
7991       CheckCollision[x][y] = 0;
7992       CheckImpact[x][y] = 0;
7993
7994       Impact(x, y);
7995     }
7996     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7997     {
7998       if (MovDir[x][y] == MV_NONE)
7999       {
8000         InitMovingField(x, y, MV_DOWN);
8001         started_moving = TRUE;
8002       }
8003     }
8004     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8005     {
8006       if (WasJustFalling[x][y]) // prevent animation from being restarted
8007         MovDir[x][y] = MV_DOWN;
8008
8009       InitMovingField(x, y, MV_DOWN);
8010       started_moving = TRUE;
8011     }
8012     else if (element == EL_AMOEBA_DROP)
8013     {
8014       Tile[x][y] = EL_AMOEBA_GROWING;
8015       Store[x][y] = EL_AMOEBA_WET;
8016     }
8017     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8018               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8019              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8020              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8021     {
8022       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8023                                 (IS_FREE(x - 1, y + 1) ||
8024                                  Tile[x - 1][y + 1] == EL_ACID));
8025       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8026                                 (IS_FREE(x + 1, y + 1) ||
8027                                  Tile[x + 1][y + 1] == EL_ACID));
8028       boolean can_fall_any  = (can_fall_left || can_fall_right);
8029       boolean can_fall_both = (can_fall_left && can_fall_right);
8030       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8031
8032       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8033       {
8034         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8035           can_fall_right = FALSE;
8036         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8037           can_fall_left = FALSE;
8038         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8039           can_fall_right = FALSE;
8040         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8041           can_fall_left = FALSE;
8042
8043         can_fall_any  = (can_fall_left || can_fall_right);
8044         can_fall_both = FALSE;
8045       }
8046
8047       if (can_fall_both)
8048       {
8049         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8050           can_fall_right = FALSE;       // slip down on left side
8051         else
8052           can_fall_left = !(can_fall_right = RND(2));
8053
8054         can_fall_both = FALSE;
8055       }
8056
8057       if (can_fall_any)
8058       {
8059         // if not determined otherwise, prefer left side for slipping down
8060         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8061         started_moving = TRUE;
8062       }
8063     }
8064     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8065     {
8066       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8067       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8068       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8069       int belt_dir = game.belt_dir[belt_nr];
8070
8071       if ((belt_dir == MV_LEFT  && left_is_free) ||
8072           (belt_dir == MV_RIGHT && right_is_free))
8073       {
8074         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8075
8076         InitMovingField(x, y, belt_dir);
8077         started_moving = TRUE;
8078
8079         Pushed[x][y] = TRUE;
8080         Pushed[nextx][y] = TRUE;
8081
8082         GfxAction[x][y] = ACTION_DEFAULT;
8083       }
8084       else
8085       {
8086         MovDir[x][y] = 0;       // if element was moving, stop it
8087       }
8088     }
8089   }
8090
8091   // not "else if" because of elements that can fall and move (EL_SPRING)
8092   if (CAN_MOVE(element) && !started_moving)
8093   {
8094     int move_pattern = element_info[element].move_pattern;
8095     int newx, newy;
8096
8097     Moving2Blocked(x, y, &newx, &newy);
8098
8099     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8100       return;
8101
8102     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8103         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8104     {
8105       WasJustMoving[x][y] = 0;
8106       CheckCollision[x][y] = 0;
8107
8108       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8109
8110       if (Tile[x][y] != element)        // element has changed
8111         return;
8112     }
8113
8114     if (!MovDelay[x][y])        // start new movement phase
8115     {
8116       // all objects that can change their move direction after each step
8117       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8118
8119       if (element != EL_YAMYAM &&
8120           element != EL_DARK_YAMYAM &&
8121           element != EL_PACMAN &&
8122           !(move_pattern & MV_ANY_DIRECTION) &&
8123           move_pattern != MV_TURNING_LEFT &&
8124           move_pattern != MV_TURNING_RIGHT &&
8125           move_pattern != MV_TURNING_LEFT_RIGHT &&
8126           move_pattern != MV_TURNING_RIGHT_LEFT &&
8127           move_pattern != MV_TURNING_RANDOM)
8128       {
8129         TurnRound(x, y);
8130
8131         if (MovDelay[x][y] && (element == EL_BUG ||
8132                                element == EL_SPACESHIP ||
8133                                element == EL_SP_SNIKSNAK ||
8134                                element == EL_SP_ELECTRON ||
8135                                element == EL_MOLE))
8136           TEST_DrawLevelField(x, y);
8137       }
8138     }
8139
8140     if (MovDelay[x][y])         // wait some time before next movement
8141     {
8142       MovDelay[x][y]--;
8143
8144       if (element == EL_ROBOT ||
8145           element == EL_YAMYAM ||
8146           element == EL_DARK_YAMYAM)
8147       {
8148         DrawLevelElementAnimationIfNeeded(x, y, element);
8149         PlayLevelSoundAction(x, y, ACTION_WAITING);
8150       }
8151       else if (element == EL_SP_ELECTRON)
8152         DrawLevelElementAnimationIfNeeded(x, y, element);
8153       else if (element == EL_DRAGON)
8154       {
8155         int i;
8156         int dir = MovDir[x][y];
8157         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8158         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8159         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8160                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8161                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8162                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8163         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8164
8165         GfxAction[x][y] = ACTION_ATTACKING;
8166
8167         if (IS_PLAYER(x, y))
8168           DrawPlayerField(x, y);
8169         else
8170           TEST_DrawLevelField(x, y);
8171
8172         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8173
8174         for (i = 1; i <= 3; i++)
8175         {
8176           int xx = x + i * dx;
8177           int yy = y + i * dy;
8178           int sx = SCREENX(xx);
8179           int sy = SCREENY(yy);
8180           int flame_graphic = graphic + (i - 1);
8181
8182           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8183             break;
8184
8185           if (MovDelay[x][y])
8186           {
8187             int flamed = MovingOrBlocked2Element(xx, yy);
8188
8189             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8190               Bang(xx, yy);
8191             else
8192               RemoveMovingField(xx, yy);
8193
8194             ChangeDelay[xx][yy] = 0;
8195
8196             Tile[xx][yy] = EL_FLAMES;
8197
8198             if (IN_SCR_FIELD(sx, sy))
8199             {
8200               TEST_DrawLevelFieldCrumbled(xx, yy);
8201               DrawGraphic(sx, sy, flame_graphic, frame);
8202             }
8203           }
8204           else
8205           {
8206             if (Tile[xx][yy] == EL_FLAMES)
8207               Tile[xx][yy] = EL_EMPTY;
8208             TEST_DrawLevelField(xx, yy);
8209           }
8210         }
8211       }
8212
8213       if (MovDelay[x][y])       // element still has to wait some time
8214       {
8215         PlayLevelSoundAction(x, y, ACTION_WAITING);
8216
8217         return;
8218       }
8219     }
8220
8221     // now make next step
8222
8223     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8224
8225     if (DONT_COLLIDE_WITH(element) &&
8226         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8227         !PLAYER_ENEMY_PROTECTED(newx, newy))
8228     {
8229       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8230
8231       return;
8232     }
8233
8234     else if (CAN_MOVE_INTO_ACID(element) &&
8235              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8236              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8237              (MovDir[x][y] == MV_DOWN ||
8238               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8239     {
8240       SplashAcid(newx, newy);
8241       Store[x][y] = EL_ACID;
8242     }
8243     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8244     {
8245       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8246           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8247           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8248           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8249       {
8250         RemoveField(x, y);
8251         TEST_DrawLevelField(x, y);
8252
8253         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8254         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8255           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8256
8257         game.friends_still_needed--;
8258         if (!game.friends_still_needed &&
8259             !game.GameOver &&
8260             game.all_players_gone)
8261           LevelSolved();
8262
8263         return;
8264       }
8265       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8266       {
8267         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8268           TEST_DrawLevelField(newx, newy);
8269         else
8270           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8271       }
8272       else if (!IS_FREE(newx, newy))
8273       {
8274         GfxAction[x][y] = ACTION_WAITING;
8275
8276         if (IS_PLAYER(x, y))
8277           DrawPlayerField(x, y);
8278         else
8279           TEST_DrawLevelField(x, y);
8280
8281         return;
8282       }
8283     }
8284     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8285     {
8286       if (IS_FOOD_PIG(Tile[newx][newy]))
8287       {
8288         if (IS_MOVING(newx, newy))
8289           RemoveMovingField(newx, newy);
8290         else
8291         {
8292           Tile[newx][newy] = EL_EMPTY;
8293           TEST_DrawLevelField(newx, newy);
8294         }
8295
8296         PlayLevelSound(x, y, SND_PIG_DIGGING);
8297       }
8298       else if (!IS_FREE(newx, newy))
8299       {
8300         if (IS_PLAYER(x, y))
8301           DrawPlayerField(x, y);
8302         else
8303           TEST_DrawLevelField(x, y);
8304
8305         return;
8306       }
8307     }
8308     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8309     {
8310       if (Store[x][y] != EL_EMPTY)
8311       {
8312         boolean can_clone = FALSE;
8313         int xx, yy;
8314
8315         // check if element to clone is still there
8316         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8317         {
8318           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8319           {
8320             can_clone = TRUE;
8321
8322             break;
8323           }
8324         }
8325
8326         // cannot clone or target field not free anymore -- do not clone
8327         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8328           Store[x][y] = EL_EMPTY;
8329       }
8330
8331       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8332       {
8333         if (IS_MV_DIAGONAL(MovDir[x][y]))
8334         {
8335           int diagonal_move_dir = MovDir[x][y];
8336           int stored = Store[x][y];
8337           int change_delay = 8;
8338           int graphic;
8339
8340           // android is moving diagonally
8341
8342           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8343
8344           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8345           GfxElement[x][y] = EL_EMC_ANDROID;
8346           GfxAction[x][y] = ACTION_SHRINKING;
8347           GfxDir[x][y] = diagonal_move_dir;
8348           ChangeDelay[x][y] = change_delay;
8349
8350           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8351                                    GfxDir[x][y]);
8352
8353           DrawLevelGraphicAnimation(x, y, graphic);
8354           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8355
8356           if (Tile[newx][newy] == EL_ACID)
8357           {
8358             SplashAcid(newx, newy);
8359
8360             return;
8361           }
8362
8363           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8364
8365           Store[newx][newy] = EL_EMC_ANDROID;
8366           GfxElement[newx][newy] = EL_EMC_ANDROID;
8367           GfxAction[newx][newy] = ACTION_GROWING;
8368           GfxDir[newx][newy] = diagonal_move_dir;
8369           ChangeDelay[newx][newy] = change_delay;
8370
8371           graphic = el_act_dir2img(GfxElement[newx][newy],
8372                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8373
8374           DrawLevelGraphicAnimation(newx, newy, graphic);
8375           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8376
8377           return;
8378         }
8379         else
8380         {
8381           Tile[newx][newy] = EL_EMPTY;
8382           TEST_DrawLevelField(newx, newy);
8383
8384           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8385         }
8386       }
8387       else if (!IS_FREE(newx, newy))
8388       {
8389         return;
8390       }
8391     }
8392     else if (IS_CUSTOM_ELEMENT(element) &&
8393              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8394     {
8395       if (!DigFieldByCE(newx, newy, element))
8396         return;
8397
8398       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8399       {
8400         RunnerVisit[x][y] = FrameCounter;
8401         PlayerVisit[x][y] /= 8;         // expire player visit path
8402       }
8403     }
8404     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8405     {
8406       if (!IS_FREE(newx, newy))
8407       {
8408         if (IS_PLAYER(x, y))
8409           DrawPlayerField(x, y);
8410         else
8411           TEST_DrawLevelField(x, y);
8412
8413         return;
8414       }
8415       else
8416       {
8417         boolean wanna_flame = !RND(10);
8418         int dx = newx - x, dy = newy - y;
8419         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8420         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8421         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8422                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8423         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8424                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8425
8426         if ((wanna_flame ||
8427              IS_CLASSIC_ENEMY(element1) ||
8428              IS_CLASSIC_ENEMY(element2)) &&
8429             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8430             element1 != EL_FLAMES && element2 != EL_FLAMES)
8431         {
8432           ResetGfxAnimation(x, y);
8433           GfxAction[x][y] = ACTION_ATTACKING;
8434
8435           if (IS_PLAYER(x, y))
8436             DrawPlayerField(x, y);
8437           else
8438             TEST_DrawLevelField(x, y);
8439
8440           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8441
8442           MovDelay[x][y] = 50;
8443
8444           Tile[newx][newy] = EL_FLAMES;
8445           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8446             Tile[newx1][newy1] = EL_FLAMES;
8447           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8448             Tile[newx2][newy2] = EL_FLAMES;
8449
8450           return;
8451         }
8452       }
8453     }
8454     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8455              Tile[newx][newy] == EL_DIAMOND)
8456     {
8457       if (IS_MOVING(newx, newy))
8458         RemoveMovingField(newx, newy);
8459       else
8460       {
8461         Tile[newx][newy] = EL_EMPTY;
8462         TEST_DrawLevelField(newx, newy);
8463       }
8464
8465       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8466     }
8467     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8468              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8469     {
8470       if (AmoebaNr[newx][newy])
8471       {
8472         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8473         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8474             Tile[newx][newy] == EL_BD_AMOEBA)
8475           AmoebaCnt[AmoebaNr[newx][newy]]--;
8476       }
8477
8478       if (IS_MOVING(newx, newy))
8479       {
8480         RemoveMovingField(newx, newy);
8481       }
8482       else
8483       {
8484         Tile[newx][newy] = EL_EMPTY;
8485         TEST_DrawLevelField(newx, newy);
8486       }
8487
8488       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8489     }
8490     else if ((element == EL_PACMAN || element == EL_MOLE)
8491              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8492     {
8493       if (AmoebaNr[newx][newy])
8494       {
8495         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8496         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8497             Tile[newx][newy] == EL_BD_AMOEBA)
8498           AmoebaCnt[AmoebaNr[newx][newy]]--;
8499       }
8500
8501       if (element == EL_MOLE)
8502       {
8503         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8504         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8505
8506         ResetGfxAnimation(x, y);
8507         GfxAction[x][y] = ACTION_DIGGING;
8508         TEST_DrawLevelField(x, y);
8509
8510         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8511
8512         return;                         // wait for shrinking amoeba
8513       }
8514       else      // element == EL_PACMAN
8515       {
8516         Tile[newx][newy] = EL_EMPTY;
8517         TEST_DrawLevelField(newx, newy);
8518         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8519       }
8520     }
8521     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8522              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8523               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8524     {
8525       // wait for shrinking amoeba to completely disappear
8526       return;
8527     }
8528     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8529     {
8530       // object was running against a wall
8531
8532       TurnRound(x, y);
8533
8534       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8535         DrawLevelElementAnimation(x, y, element);
8536
8537       if (DONT_TOUCH(element))
8538         TestIfBadThingTouchesPlayer(x, y);
8539
8540       return;
8541     }
8542
8543     InitMovingField(x, y, MovDir[x][y]);
8544
8545     PlayLevelSoundAction(x, y, ACTION_MOVING);
8546   }
8547
8548   if (MovDir[x][y])
8549     ContinueMoving(x, y);
8550 }
8551
8552 void ContinueMoving(int x, int y)
8553 {
8554   int element = Tile[x][y];
8555   struct ElementInfo *ei = &element_info[element];
8556   int direction = MovDir[x][y];
8557   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8558   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8559   int newx = x + dx, newy = y + dy;
8560   int stored = Store[x][y];
8561   int stored_new = Store[newx][newy];
8562   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8563   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8564   boolean last_line = (newy == lev_fieldy - 1);
8565   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8566
8567   if (pushed_by_player)         // special case: moving object pushed by player
8568   {
8569     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8570   }
8571   else if (use_step_delay)      // special case: moving object has step delay
8572   {
8573     if (!MovDelay[x][y])
8574       MovPos[x][y] += getElementMoveStepsize(x, y);
8575
8576     if (MovDelay[x][y])
8577       MovDelay[x][y]--;
8578     else
8579       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8580
8581     if (MovDelay[x][y])
8582     {
8583       TEST_DrawLevelField(x, y);
8584
8585       return;   // element is still waiting
8586     }
8587   }
8588   else                          // normal case: generically moving object
8589   {
8590     MovPos[x][y] += getElementMoveStepsize(x, y);
8591   }
8592
8593   if (ABS(MovPos[x][y]) < TILEX)
8594   {
8595     TEST_DrawLevelField(x, y);
8596
8597     return;     // element is still moving
8598   }
8599
8600   // element reached destination field
8601
8602   Tile[x][y] = EL_EMPTY;
8603   Tile[newx][newy] = element;
8604   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8605
8606   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8607   {
8608     element = Tile[newx][newy] = EL_ACID;
8609   }
8610   else if (element == EL_MOLE)
8611   {
8612     Tile[x][y] = EL_SAND;
8613
8614     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8615   }
8616   else if (element == EL_QUICKSAND_FILLING)
8617   {
8618     element = Tile[newx][newy] = get_next_element(element);
8619     Store[newx][newy] = Store[x][y];
8620   }
8621   else if (element == EL_QUICKSAND_EMPTYING)
8622   {
8623     Tile[x][y] = get_next_element(element);
8624     element = Tile[newx][newy] = Store[x][y];
8625   }
8626   else if (element == EL_QUICKSAND_FAST_FILLING)
8627   {
8628     element = Tile[newx][newy] = get_next_element(element);
8629     Store[newx][newy] = Store[x][y];
8630   }
8631   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8632   {
8633     Tile[x][y] = get_next_element(element);
8634     element = Tile[newx][newy] = Store[x][y];
8635   }
8636   else if (element == EL_MAGIC_WALL_FILLING)
8637   {
8638     element = Tile[newx][newy] = get_next_element(element);
8639     if (!game.magic_wall_active)
8640       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8641     Store[newx][newy] = Store[x][y];
8642   }
8643   else if (element == EL_MAGIC_WALL_EMPTYING)
8644   {
8645     Tile[x][y] = get_next_element(element);
8646     if (!game.magic_wall_active)
8647       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8648     element = Tile[newx][newy] = Store[x][y];
8649
8650     InitField(newx, newy, FALSE);
8651   }
8652   else if (element == EL_BD_MAGIC_WALL_FILLING)
8653   {
8654     element = Tile[newx][newy] = get_next_element(element);
8655     if (!game.magic_wall_active)
8656       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8657     Store[newx][newy] = Store[x][y];
8658   }
8659   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8660   {
8661     Tile[x][y] = get_next_element(element);
8662     if (!game.magic_wall_active)
8663       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8664     element = Tile[newx][newy] = Store[x][y];
8665
8666     InitField(newx, newy, FALSE);
8667   }
8668   else if (element == EL_DC_MAGIC_WALL_FILLING)
8669   {
8670     element = Tile[newx][newy] = get_next_element(element);
8671     if (!game.magic_wall_active)
8672       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8673     Store[newx][newy] = Store[x][y];
8674   }
8675   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8676   {
8677     Tile[x][y] = get_next_element(element);
8678     if (!game.magic_wall_active)
8679       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8680     element = Tile[newx][newy] = Store[x][y];
8681
8682     InitField(newx, newy, FALSE);
8683   }
8684   else if (element == EL_AMOEBA_DROPPING)
8685   {
8686     Tile[x][y] = get_next_element(element);
8687     element = Tile[newx][newy] = Store[x][y];
8688   }
8689   else if (element == EL_SOKOBAN_OBJECT)
8690   {
8691     if (Back[x][y])
8692       Tile[x][y] = Back[x][y];
8693
8694     if (Back[newx][newy])
8695       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8696
8697     Back[x][y] = Back[newx][newy] = 0;
8698   }
8699
8700   Store[x][y] = EL_EMPTY;
8701   MovPos[x][y] = 0;
8702   MovDir[x][y] = 0;
8703   MovDelay[x][y] = 0;
8704
8705   MovDelay[newx][newy] = 0;
8706
8707   if (CAN_CHANGE_OR_HAS_ACTION(element))
8708   {
8709     // copy element change control values to new field
8710     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8711     ChangePage[newx][newy]  = ChangePage[x][y];
8712     ChangeCount[newx][newy] = ChangeCount[x][y];
8713     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8714   }
8715
8716   CustomValue[newx][newy] = CustomValue[x][y];
8717
8718   ChangeDelay[x][y] = 0;
8719   ChangePage[x][y] = -1;
8720   ChangeCount[x][y] = 0;
8721   ChangeEvent[x][y] = -1;
8722
8723   CustomValue[x][y] = 0;
8724
8725   // copy animation control values to new field
8726   GfxFrame[newx][newy]  = GfxFrame[x][y];
8727   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8728   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8729   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8730
8731   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8732
8733   // some elements can leave other elements behind after moving
8734   if (ei->move_leave_element != EL_EMPTY &&
8735       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8736       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8737   {
8738     int move_leave_element = ei->move_leave_element;
8739
8740     // this makes it possible to leave the removed element again
8741     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8742       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8743
8744     Tile[x][y] = move_leave_element;
8745
8746     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8747       MovDir[x][y] = direction;
8748
8749     InitField(x, y, FALSE);
8750
8751     if (GFX_CRUMBLED(Tile[x][y]))
8752       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8753
8754     if (ELEM_IS_PLAYER(move_leave_element))
8755       RelocatePlayer(x, y, move_leave_element);
8756   }
8757
8758   // do this after checking for left-behind element
8759   ResetGfxAnimation(x, y);      // reset animation values for old field
8760
8761   if (!CAN_MOVE(element) ||
8762       (CAN_FALL(element) && direction == MV_DOWN &&
8763        (element == EL_SPRING ||
8764         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8765         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8766     GfxDir[x][y] = MovDir[newx][newy] = 0;
8767
8768   TEST_DrawLevelField(x, y);
8769   TEST_DrawLevelField(newx, newy);
8770
8771   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8772
8773   // prevent pushed element from moving on in pushed direction
8774   if (pushed_by_player && CAN_MOVE(element) &&
8775       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8776       !(element_info[element].move_pattern & direction))
8777     TurnRound(newx, newy);
8778
8779   // prevent elements on conveyor belt from moving on in last direction
8780   if (pushed_by_conveyor && CAN_FALL(element) &&
8781       direction & MV_HORIZONTAL)
8782     MovDir[newx][newy] = 0;
8783
8784   if (!pushed_by_player)
8785   {
8786     int nextx = newx + dx, nexty = newy + dy;
8787     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8788
8789     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8790
8791     if (CAN_FALL(element) && direction == MV_DOWN)
8792       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8793
8794     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8795       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8796
8797     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8798       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8799   }
8800
8801   if (DONT_TOUCH(element))      // object may be nasty to player or others
8802   {
8803     TestIfBadThingTouchesPlayer(newx, newy);
8804     TestIfBadThingTouchesFriend(newx, newy);
8805
8806     if (!IS_CUSTOM_ELEMENT(element))
8807       TestIfBadThingTouchesOtherBadThing(newx, newy);
8808   }
8809   else if (element == EL_PENGUIN)
8810     TestIfFriendTouchesBadThing(newx, newy);
8811
8812   if (DONT_GET_HIT_BY(element))
8813   {
8814     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8815   }
8816
8817   // give the player one last chance (one more frame) to move away
8818   if (CAN_FALL(element) && direction == MV_DOWN &&
8819       (last_line || (!IS_FREE(x, newy + 1) &&
8820                      (!IS_PLAYER(x, newy + 1) ||
8821                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8822     Impact(x, newy);
8823
8824   if (pushed_by_player && !game.use_change_when_pushing_bug)
8825   {
8826     int push_side = MV_DIR_OPPOSITE(direction);
8827     struct PlayerInfo *player = PLAYERINFO(x, y);
8828
8829     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8830                                player->index_bit, push_side);
8831     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8832                                         player->index_bit, push_side);
8833   }
8834
8835   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8836     MovDelay[newx][newy] = 1;
8837
8838   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8839
8840   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8841   TestIfElementHitsCustomElement(newx, newy, direction);
8842   TestIfPlayerTouchesCustomElement(newx, newy);
8843   TestIfElementTouchesCustomElement(newx, newy);
8844
8845   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8846       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8847     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8848                              MV_DIR_OPPOSITE(direction));
8849 }
8850
8851 int AmoebaNeighbourNr(int ax, int ay)
8852 {
8853   int i;
8854   int element = Tile[ax][ay];
8855   int group_nr = 0;
8856   static int xy[4][2] =
8857   {
8858     { 0, -1 },
8859     { -1, 0 },
8860     { +1, 0 },
8861     { 0, +1 }
8862   };
8863
8864   for (i = 0; i < NUM_DIRECTIONS; i++)
8865   {
8866     int x = ax + xy[i][0];
8867     int y = ay + xy[i][1];
8868
8869     if (!IN_LEV_FIELD(x, y))
8870       continue;
8871
8872     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8873       group_nr = AmoebaNr[x][y];
8874   }
8875
8876   return group_nr;
8877 }
8878
8879 static void AmoebaMerge(int ax, int ay)
8880 {
8881   int i, x, y, xx, yy;
8882   int new_group_nr = AmoebaNr[ax][ay];
8883   static int xy[4][2] =
8884   {
8885     { 0, -1 },
8886     { -1, 0 },
8887     { +1, 0 },
8888     { 0, +1 }
8889   };
8890
8891   if (new_group_nr == 0)
8892     return;
8893
8894   for (i = 0; i < NUM_DIRECTIONS; i++)
8895   {
8896     x = ax + xy[i][0];
8897     y = ay + xy[i][1];
8898
8899     if (!IN_LEV_FIELD(x, y))
8900       continue;
8901
8902     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8903          Tile[x][y] == EL_BD_AMOEBA ||
8904          Tile[x][y] == EL_AMOEBA_DEAD) &&
8905         AmoebaNr[x][y] != new_group_nr)
8906     {
8907       int old_group_nr = AmoebaNr[x][y];
8908
8909       if (old_group_nr == 0)
8910         return;
8911
8912       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8913       AmoebaCnt[old_group_nr] = 0;
8914       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8915       AmoebaCnt2[old_group_nr] = 0;
8916
8917       SCAN_PLAYFIELD(xx, yy)
8918       {
8919         if (AmoebaNr[xx][yy] == old_group_nr)
8920           AmoebaNr[xx][yy] = new_group_nr;
8921       }
8922     }
8923   }
8924 }
8925
8926 void AmoebaToDiamond(int ax, int ay)
8927 {
8928   int i, x, y;
8929
8930   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8931   {
8932     int group_nr = AmoebaNr[ax][ay];
8933
8934 #ifdef DEBUG
8935     if (group_nr == 0)
8936     {
8937       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8938       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8939
8940       return;
8941     }
8942 #endif
8943
8944     SCAN_PLAYFIELD(x, y)
8945     {
8946       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8947       {
8948         AmoebaNr[x][y] = 0;
8949         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8950       }
8951     }
8952
8953     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8954                             SND_AMOEBA_TURNING_TO_GEM :
8955                             SND_AMOEBA_TURNING_TO_ROCK));
8956     Bang(ax, ay);
8957   }
8958   else
8959   {
8960     static int xy[4][2] =
8961     {
8962       { 0, -1 },
8963       { -1, 0 },
8964       { +1, 0 },
8965       { 0, +1 }
8966     };
8967
8968     for (i = 0; i < NUM_DIRECTIONS; i++)
8969     {
8970       x = ax + xy[i][0];
8971       y = ay + xy[i][1];
8972
8973       if (!IN_LEV_FIELD(x, y))
8974         continue;
8975
8976       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8977       {
8978         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8979                               SND_AMOEBA_TURNING_TO_GEM :
8980                               SND_AMOEBA_TURNING_TO_ROCK));
8981         Bang(x, y);
8982       }
8983     }
8984   }
8985 }
8986
8987 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8988 {
8989   int x, y;
8990   int group_nr = AmoebaNr[ax][ay];
8991   boolean done = FALSE;
8992
8993 #ifdef DEBUG
8994   if (group_nr == 0)
8995   {
8996     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8997     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8998
8999     return;
9000   }
9001 #endif
9002
9003   SCAN_PLAYFIELD(x, y)
9004   {
9005     if (AmoebaNr[x][y] == group_nr &&
9006         (Tile[x][y] == EL_AMOEBA_DEAD ||
9007          Tile[x][y] == EL_BD_AMOEBA ||
9008          Tile[x][y] == EL_AMOEBA_GROWING))
9009     {
9010       AmoebaNr[x][y] = 0;
9011       Tile[x][y] = new_element;
9012       InitField(x, y, FALSE);
9013       TEST_DrawLevelField(x, y);
9014       done = TRUE;
9015     }
9016   }
9017
9018   if (done)
9019     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9020                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9021                             SND_BD_AMOEBA_TURNING_TO_GEM));
9022 }
9023
9024 static void AmoebaGrowing(int x, int y)
9025 {
9026   static unsigned int sound_delay = 0;
9027   static unsigned int sound_delay_value = 0;
9028
9029   if (!MovDelay[x][y])          // start new growing cycle
9030   {
9031     MovDelay[x][y] = 7;
9032
9033     if (DelayReached(&sound_delay, sound_delay_value))
9034     {
9035       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9036       sound_delay_value = 30;
9037     }
9038   }
9039
9040   if (MovDelay[x][y])           // wait some time before growing bigger
9041   {
9042     MovDelay[x][y]--;
9043     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9044     {
9045       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9046                                            6 - MovDelay[x][y]);
9047
9048       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9049     }
9050
9051     if (!MovDelay[x][y])
9052     {
9053       Tile[x][y] = Store[x][y];
9054       Store[x][y] = 0;
9055       TEST_DrawLevelField(x, y);
9056     }
9057   }
9058 }
9059
9060 static void AmoebaShrinking(int x, int y)
9061 {
9062   static unsigned int sound_delay = 0;
9063   static unsigned int sound_delay_value = 0;
9064
9065   if (!MovDelay[x][y])          // start new shrinking cycle
9066   {
9067     MovDelay[x][y] = 7;
9068
9069     if (DelayReached(&sound_delay, sound_delay_value))
9070       sound_delay_value = 30;
9071   }
9072
9073   if (MovDelay[x][y])           // wait some time before shrinking
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_SHRINKING,
9079                                            6 - MovDelay[x][y]);
9080
9081       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9082     }
9083
9084     if (!MovDelay[x][y])
9085     {
9086       Tile[x][y] = EL_EMPTY;
9087       TEST_DrawLevelField(x, y);
9088
9089       // don't let mole enter this field in this cycle;
9090       // (give priority to objects falling to this field from above)
9091       Stop[x][y] = TRUE;
9092     }
9093   }
9094 }
9095
9096 static void AmoebaReproduce(int ax, int ay)
9097 {
9098   int i;
9099   int element = Tile[ax][ay];
9100   int graphic = el2img(element);
9101   int newax = ax, neway = ay;
9102   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9103   static int xy[4][2] =
9104   {
9105     { 0, -1 },
9106     { -1, 0 },
9107     { +1, 0 },
9108     { 0, +1 }
9109   };
9110
9111   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9112   {
9113     Tile[ax][ay] = EL_AMOEBA_DEAD;
9114     TEST_DrawLevelField(ax, ay);
9115     return;
9116   }
9117
9118   if (IS_ANIMATED(graphic))
9119     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9120
9121   if (!MovDelay[ax][ay])        // start making new amoeba field
9122     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9123
9124   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9125   {
9126     MovDelay[ax][ay]--;
9127     if (MovDelay[ax][ay])
9128       return;
9129   }
9130
9131   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9132   {
9133     int start = RND(4);
9134     int x = ax + xy[start][0];
9135     int y = ay + xy[start][1];
9136
9137     if (!IN_LEV_FIELD(x, y))
9138       return;
9139
9140     if (IS_FREE(x, y) ||
9141         CAN_GROW_INTO(Tile[x][y]) ||
9142         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9143         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9144     {
9145       newax = x;
9146       neway = y;
9147     }
9148
9149     if (newax == ax && neway == ay)
9150       return;
9151   }
9152   else                          // normal or "filled" (BD style) amoeba
9153   {
9154     int start = RND(4);
9155     boolean waiting_for_player = FALSE;
9156
9157     for (i = 0; i < NUM_DIRECTIONS; i++)
9158     {
9159       int j = (start + i) % 4;
9160       int x = ax + xy[j][0];
9161       int y = ay + xy[j][1];
9162
9163       if (!IN_LEV_FIELD(x, y))
9164         continue;
9165
9166       if (IS_FREE(x, y) ||
9167           CAN_GROW_INTO(Tile[x][y]) ||
9168           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9169           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9170       {
9171         newax = x;
9172         neway = y;
9173         break;
9174       }
9175       else if (IS_PLAYER(x, y))
9176         waiting_for_player = TRUE;
9177     }
9178
9179     if (newax == ax && neway == ay)             // amoeba cannot grow
9180     {
9181       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9182       {
9183         Tile[ax][ay] = EL_AMOEBA_DEAD;
9184         TEST_DrawLevelField(ax, ay);
9185         AmoebaCnt[AmoebaNr[ax][ay]]--;
9186
9187         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9188         {
9189           if (element == EL_AMOEBA_FULL)
9190             AmoebaToDiamond(ax, ay);
9191           else if (element == EL_BD_AMOEBA)
9192             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9193         }
9194       }
9195       return;
9196     }
9197     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9198     {
9199       // amoeba gets larger by growing in some direction
9200
9201       int new_group_nr = AmoebaNr[ax][ay];
9202
9203 #ifdef DEBUG
9204   if (new_group_nr == 0)
9205   {
9206     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9207           newax, neway);
9208     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9209
9210     return;
9211   }
9212 #endif
9213
9214       AmoebaNr[newax][neway] = new_group_nr;
9215       AmoebaCnt[new_group_nr]++;
9216       AmoebaCnt2[new_group_nr]++;
9217
9218       // if amoeba touches other amoeba(s) after growing, unify them
9219       AmoebaMerge(newax, neway);
9220
9221       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9222       {
9223         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9224         return;
9225       }
9226     }
9227   }
9228
9229   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9230       (neway == lev_fieldy - 1 && newax != ax))
9231   {
9232     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9233     Store[newax][neway] = element;
9234   }
9235   else if (neway == ay || element == EL_EMC_DRIPPER)
9236   {
9237     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9238
9239     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9240   }
9241   else
9242   {
9243     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9244     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9245     Store[ax][ay] = EL_AMOEBA_DROP;
9246     ContinueMoving(ax, ay);
9247     return;
9248   }
9249
9250   TEST_DrawLevelField(newax, neway);
9251 }
9252
9253 static void Life(int ax, int ay)
9254 {
9255   int x1, y1, x2, y2;
9256   int life_time = 40;
9257   int element = Tile[ax][ay];
9258   int graphic = el2img(element);
9259   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9260                          level.biomaze);
9261   boolean changed = FALSE;
9262
9263   if (IS_ANIMATED(graphic))
9264     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9265
9266   if (Stop[ax][ay])
9267     return;
9268
9269   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9270     MovDelay[ax][ay] = life_time;
9271
9272   if (MovDelay[ax][ay])         // wait some time before next cycle
9273   {
9274     MovDelay[ax][ay]--;
9275     if (MovDelay[ax][ay])
9276       return;
9277   }
9278
9279   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9280   {
9281     int xx = ax+x1, yy = ay+y1;
9282     int old_element = Tile[xx][yy];
9283     int num_neighbours = 0;
9284
9285     if (!IN_LEV_FIELD(xx, yy))
9286       continue;
9287
9288     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9289     {
9290       int x = xx+x2, y = yy+y2;
9291
9292       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9293         continue;
9294
9295       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9296       boolean is_neighbour = FALSE;
9297
9298       if (level.use_life_bugs)
9299         is_neighbour =
9300           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9301            (IS_FREE(x, y)                             &&  Stop[x][y]));
9302       else
9303         is_neighbour =
9304           (Last[x][y] == element || is_player_cell);
9305
9306       if (is_neighbour)
9307         num_neighbours++;
9308     }
9309
9310     boolean is_free = FALSE;
9311
9312     if (level.use_life_bugs)
9313       is_free = (IS_FREE(xx, yy));
9314     else
9315       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9316
9317     if (xx == ax && yy == ay)           // field in the middle
9318     {
9319       if (num_neighbours < life_parameter[0] ||
9320           num_neighbours > life_parameter[1])
9321       {
9322         Tile[xx][yy] = EL_EMPTY;
9323         if (Tile[xx][yy] != old_element)
9324           TEST_DrawLevelField(xx, yy);
9325         Stop[xx][yy] = TRUE;
9326         changed = TRUE;
9327       }
9328     }
9329     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9330     {                                   // free border field
9331       if (num_neighbours >= life_parameter[2] &&
9332           num_neighbours <= life_parameter[3])
9333       {
9334         Tile[xx][yy] = element;
9335         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9336         if (Tile[xx][yy] != old_element)
9337           TEST_DrawLevelField(xx, yy);
9338         Stop[xx][yy] = TRUE;
9339         changed = TRUE;
9340       }
9341     }
9342   }
9343
9344   if (changed)
9345     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9346                    SND_GAME_OF_LIFE_GROWING);
9347 }
9348
9349 static void InitRobotWheel(int x, int y)
9350 {
9351   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9352 }
9353
9354 static void RunRobotWheel(int x, int y)
9355 {
9356   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9357 }
9358
9359 static void StopRobotWheel(int x, int y)
9360 {
9361   if (game.robot_wheel_x == x &&
9362       game.robot_wheel_y == y)
9363   {
9364     game.robot_wheel_x = -1;
9365     game.robot_wheel_y = -1;
9366     game.robot_wheel_active = FALSE;
9367   }
9368 }
9369
9370 static void InitTimegateWheel(int x, int y)
9371 {
9372   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9373 }
9374
9375 static void RunTimegateWheel(int x, int y)
9376 {
9377   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9378 }
9379
9380 static void InitMagicBallDelay(int x, int y)
9381 {
9382   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9383 }
9384
9385 static void ActivateMagicBall(int bx, int by)
9386 {
9387   int x, y;
9388
9389   if (level.ball_random)
9390   {
9391     int pos_border = RND(8);    // select one of the eight border elements
9392     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9393     int xx = pos_content % 3;
9394     int yy = pos_content / 3;
9395
9396     x = bx - 1 + xx;
9397     y = by - 1 + yy;
9398
9399     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9400       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9401   }
9402   else
9403   {
9404     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9405     {
9406       int xx = x - bx + 1;
9407       int yy = y - by + 1;
9408
9409       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9410         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9411     }
9412   }
9413
9414   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9415 }
9416
9417 static void CheckExit(int x, int y)
9418 {
9419   if (game.gems_still_needed > 0 ||
9420       game.sokoban_fields_still_needed > 0 ||
9421       game.sokoban_objects_still_needed > 0 ||
9422       game.lights_still_needed > 0)
9423   {
9424     int element = Tile[x][y];
9425     int graphic = el2img(element);
9426
9427     if (IS_ANIMATED(graphic))
9428       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9429
9430     return;
9431   }
9432
9433   // do not re-open exit door closed after last player
9434   if (game.all_players_gone)
9435     return;
9436
9437   Tile[x][y] = EL_EXIT_OPENING;
9438
9439   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9440 }
9441
9442 static void CheckExitEM(int x, int y)
9443 {
9444   if (game.gems_still_needed > 0 ||
9445       game.sokoban_fields_still_needed > 0 ||
9446       game.sokoban_objects_still_needed > 0 ||
9447       game.lights_still_needed > 0)
9448   {
9449     int element = Tile[x][y];
9450     int graphic = el2img(element);
9451
9452     if (IS_ANIMATED(graphic))
9453       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9454
9455     return;
9456   }
9457
9458   // do not re-open exit door closed after last player
9459   if (game.all_players_gone)
9460     return;
9461
9462   Tile[x][y] = EL_EM_EXIT_OPENING;
9463
9464   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9465 }
9466
9467 static void CheckExitSteel(int x, int y)
9468 {
9469   if (game.gems_still_needed > 0 ||
9470       game.sokoban_fields_still_needed > 0 ||
9471       game.sokoban_objects_still_needed > 0 ||
9472       game.lights_still_needed > 0)
9473   {
9474     int element = Tile[x][y];
9475     int graphic = el2img(element);
9476
9477     if (IS_ANIMATED(graphic))
9478       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9479
9480     return;
9481   }
9482
9483   // do not re-open exit door closed after last player
9484   if (game.all_players_gone)
9485     return;
9486
9487   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9488
9489   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9490 }
9491
9492 static void CheckExitSteelEM(int x, int y)
9493 {
9494   if (game.gems_still_needed > 0 ||
9495       game.sokoban_fields_still_needed > 0 ||
9496       game.sokoban_objects_still_needed > 0 ||
9497       game.lights_still_needed > 0)
9498   {
9499     int element = Tile[x][y];
9500     int graphic = el2img(element);
9501
9502     if (IS_ANIMATED(graphic))
9503       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9504
9505     return;
9506   }
9507
9508   // do not re-open exit door closed after last player
9509   if (game.all_players_gone)
9510     return;
9511
9512   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9513
9514   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9515 }
9516
9517 static void CheckExitSP(int x, int y)
9518 {
9519   if (game.gems_still_needed > 0)
9520   {
9521     int element = Tile[x][y];
9522     int graphic = el2img(element);
9523
9524     if (IS_ANIMATED(graphic))
9525       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9526
9527     return;
9528   }
9529
9530   // do not re-open exit door closed after last player
9531   if (game.all_players_gone)
9532     return;
9533
9534   Tile[x][y] = EL_SP_EXIT_OPENING;
9535
9536   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9537 }
9538
9539 static void CloseAllOpenTimegates(void)
9540 {
9541   int x, y;
9542
9543   SCAN_PLAYFIELD(x, y)
9544   {
9545     int element = Tile[x][y];
9546
9547     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9548     {
9549       Tile[x][y] = EL_TIMEGATE_CLOSING;
9550
9551       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9552     }
9553   }
9554 }
9555
9556 static void DrawTwinkleOnField(int x, int y)
9557 {
9558   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9559     return;
9560
9561   if (Tile[x][y] == EL_BD_DIAMOND)
9562     return;
9563
9564   if (MovDelay[x][y] == 0)      // next animation frame
9565     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9566
9567   if (MovDelay[x][y] != 0)      // wait some time before next frame
9568   {
9569     MovDelay[x][y]--;
9570
9571     DrawLevelElementAnimation(x, y, Tile[x][y]);
9572
9573     if (MovDelay[x][y] != 0)
9574     {
9575       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9576                                            10 - MovDelay[x][y]);
9577
9578       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9579     }
9580   }
9581 }
9582
9583 static void MauerWaechst(int x, int y)
9584 {
9585   int delay = 6;
9586
9587   if (!MovDelay[x][y])          // next animation frame
9588     MovDelay[x][y] = 3 * delay;
9589
9590   if (MovDelay[x][y])           // wait some time before next frame
9591   {
9592     MovDelay[x][y]--;
9593
9594     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9595     {
9596       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9597       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9598
9599       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9600     }
9601
9602     if (!MovDelay[x][y])
9603     {
9604       if (MovDir[x][y] == MV_LEFT)
9605       {
9606         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9607           TEST_DrawLevelField(x - 1, y);
9608       }
9609       else if (MovDir[x][y] == MV_RIGHT)
9610       {
9611         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9612           TEST_DrawLevelField(x + 1, y);
9613       }
9614       else if (MovDir[x][y] == MV_UP)
9615       {
9616         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9617           TEST_DrawLevelField(x, y - 1);
9618       }
9619       else
9620       {
9621         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9622           TEST_DrawLevelField(x, y + 1);
9623       }
9624
9625       Tile[x][y] = Store[x][y];
9626       Store[x][y] = 0;
9627       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9628       TEST_DrawLevelField(x, y);
9629     }
9630   }
9631 }
9632
9633 static void MauerAbleger(int ax, int ay)
9634 {
9635   int element = Tile[ax][ay];
9636   int graphic = el2img(element);
9637   boolean oben_frei = FALSE, unten_frei = FALSE;
9638   boolean links_frei = FALSE, rechts_frei = FALSE;
9639   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9640   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9641   boolean new_wall = FALSE;
9642
9643   if (IS_ANIMATED(graphic))
9644     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9645
9646   if (!MovDelay[ax][ay])        // start building new wall
9647     MovDelay[ax][ay] = 6;
9648
9649   if (MovDelay[ax][ay])         // wait some time before building new wall
9650   {
9651     MovDelay[ax][ay]--;
9652     if (MovDelay[ax][ay])
9653       return;
9654   }
9655
9656   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9657     oben_frei = TRUE;
9658   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9659     unten_frei = TRUE;
9660   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9661     links_frei = TRUE;
9662   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9663     rechts_frei = TRUE;
9664
9665   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9666       element == EL_EXPANDABLE_WALL_ANY)
9667   {
9668     if (oben_frei)
9669     {
9670       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9671       Store[ax][ay-1] = element;
9672       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9673       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9674         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9675                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9676       new_wall = TRUE;
9677     }
9678     if (unten_frei)
9679     {
9680       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9681       Store[ax][ay+1] = element;
9682       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9683       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9684         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9685                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9686       new_wall = TRUE;
9687     }
9688   }
9689
9690   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9691       element == EL_EXPANDABLE_WALL_ANY ||
9692       element == EL_EXPANDABLE_WALL ||
9693       element == EL_BD_EXPANDABLE_WALL)
9694   {
9695     if (links_frei)
9696     {
9697       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9698       Store[ax-1][ay] = element;
9699       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9700       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9701         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9702                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9703       new_wall = TRUE;
9704     }
9705
9706     if (rechts_frei)
9707     {
9708       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9709       Store[ax+1][ay] = element;
9710       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9711       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9712         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9713                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9714       new_wall = TRUE;
9715     }
9716   }
9717
9718   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9719     TEST_DrawLevelField(ax, ay);
9720
9721   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9722     oben_massiv = TRUE;
9723   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9724     unten_massiv = TRUE;
9725   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9726     links_massiv = TRUE;
9727   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9728     rechts_massiv = TRUE;
9729
9730   if (((oben_massiv && unten_massiv) ||
9731        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9732        element == EL_EXPANDABLE_WALL) &&
9733       ((links_massiv && rechts_massiv) ||
9734        element == EL_EXPANDABLE_WALL_VERTICAL))
9735     Tile[ax][ay] = EL_WALL;
9736
9737   if (new_wall)
9738     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9739 }
9740
9741 static void MauerAblegerStahl(int ax, int ay)
9742 {
9743   int element = Tile[ax][ay];
9744   int graphic = el2img(element);
9745   boolean oben_frei = FALSE, unten_frei = FALSE;
9746   boolean links_frei = FALSE, rechts_frei = FALSE;
9747   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9748   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9749   boolean new_wall = FALSE;
9750
9751   if (IS_ANIMATED(graphic))
9752     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9753
9754   if (!MovDelay[ax][ay])        // start building new wall
9755     MovDelay[ax][ay] = 6;
9756
9757   if (MovDelay[ax][ay])         // wait some time before building new wall
9758   {
9759     MovDelay[ax][ay]--;
9760     if (MovDelay[ax][ay])
9761       return;
9762   }
9763
9764   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9765     oben_frei = TRUE;
9766   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9767     unten_frei = TRUE;
9768   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9769     links_frei = TRUE;
9770   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9771     rechts_frei = TRUE;
9772
9773   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9774       element == EL_EXPANDABLE_STEELWALL_ANY)
9775   {
9776     if (oben_frei)
9777     {
9778       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9779       Store[ax][ay-1] = element;
9780       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9781       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9782         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9783                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9784       new_wall = TRUE;
9785     }
9786     if (unten_frei)
9787     {
9788       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9789       Store[ax][ay+1] = element;
9790       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9791       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9792         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9793                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9794       new_wall = TRUE;
9795     }
9796   }
9797
9798   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9799       element == EL_EXPANDABLE_STEELWALL_ANY)
9800   {
9801     if (links_frei)
9802     {
9803       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9804       Store[ax-1][ay] = element;
9805       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9806       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9807         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9808                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9809       new_wall = TRUE;
9810     }
9811
9812     if (rechts_frei)
9813     {
9814       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9815       Store[ax+1][ay] = element;
9816       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9817       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9818         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9819                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9820       new_wall = TRUE;
9821     }
9822   }
9823
9824   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9825     oben_massiv = TRUE;
9826   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9827     unten_massiv = TRUE;
9828   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9829     links_massiv = TRUE;
9830   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9831     rechts_massiv = TRUE;
9832
9833   if (((oben_massiv && unten_massiv) ||
9834        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9835       ((links_massiv && rechts_massiv) ||
9836        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9837     Tile[ax][ay] = EL_STEELWALL;
9838
9839   if (new_wall)
9840     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9841 }
9842
9843 static void CheckForDragon(int x, int y)
9844 {
9845   int i, j;
9846   boolean dragon_found = FALSE;
9847   static int xy[4][2] =
9848   {
9849     { 0, -1 },
9850     { -1, 0 },
9851     { +1, 0 },
9852     { 0, +1 }
9853   };
9854
9855   for (i = 0; i < NUM_DIRECTIONS; i++)
9856   {
9857     for (j = 0; j < 4; j++)
9858     {
9859       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9860
9861       if (IN_LEV_FIELD(xx, yy) &&
9862           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9863       {
9864         if (Tile[xx][yy] == EL_DRAGON)
9865           dragon_found = TRUE;
9866       }
9867       else
9868         break;
9869     }
9870   }
9871
9872   if (!dragon_found)
9873   {
9874     for (i = 0; i < NUM_DIRECTIONS; i++)
9875     {
9876       for (j = 0; j < 3; j++)
9877       {
9878         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9879   
9880         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9881         {
9882           Tile[xx][yy] = EL_EMPTY;
9883           TEST_DrawLevelField(xx, yy);
9884         }
9885         else
9886           break;
9887       }
9888     }
9889   }
9890 }
9891
9892 static void InitBuggyBase(int x, int y)
9893 {
9894   int element = Tile[x][y];
9895   int activating_delay = FRAMES_PER_SECOND / 4;
9896
9897   ChangeDelay[x][y] =
9898     (element == EL_SP_BUGGY_BASE ?
9899      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9900      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9901      activating_delay :
9902      element == EL_SP_BUGGY_BASE_ACTIVE ?
9903      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9904 }
9905
9906 static void WarnBuggyBase(int x, int y)
9907 {
9908   int i;
9909   static int xy[4][2] =
9910   {
9911     { 0, -1 },
9912     { -1, 0 },
9913     { +1, 0 },
9914     { 0, +1 }
9915   };
9916
9917   for (i = 0; i < NUM_DIRECTIONS; i++)
9918   {
9919     int xx = x + xy[i][0];
9920     int yy = y + xy[i][1];
9921
9922     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9923     {
9924       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9925
9926       break;
9927     }
9928   }
9929 }
9930
9931 static void InitTrap(int x, int y)
9932 {
9933   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9934 }
9935
9936 static void ActivateTrap(int x, int y)
9937 {
9938   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9939 }
9940
9941 static void ChangeActiveTrap(int x, int y)
9942 {
9943   int graphic = IMG_TRAP_ACTIVE;
9944
9945   // if new animation frame was drawn, correct crumbled sand border
9946   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9947     TEST_DrawLevelFieldCrumbled(x, y);
9948 }
9949
9950 static int getSpecialActionElement(int element, int number, int base_element)
9951 {
9952   return (element != EL_EMPTY ? element :
9953           number != -1 ? base_element + number - 1 :
9954           EL_EMPTY);
9955 }
9956
9957 static int getModifiedActionNumber(int value_old, int operator, int operand,
9958                                    int value_min, int value_max)
9959 {
9960   int value_new = (operator == CA_MODE_SET      ? operand :
9961                    operator == CA_MODE_ADD      ? value_old + operand :
9962                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9963                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9964                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9965                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9966                    value_old);
9967
9968   return (value_new < value_min ? value_min :
9969           value_new > value_max ? value_max :
9970           value_new);
9971 }
9972
9973 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9974 {
9975   struct ElementInfo *ei = &element_info[element];
9976   struct ElementChangeInfo *change = &ei->change_page[page];
9977   int target_element = change->target_element;
9978   int action_type = change->action_type;
9979   int action_mode = change->action_mode;
9980   int action_arg = change->action_arg;
9981   int action_element = change->action_element;
9982   int i;
9983
9984   if (!change->has_action)
9985     return;
9986
9987   // ---------- determine action paramater values -----------------------------
9988
9989   int level_time_value =
9990     (level.time > 0 ? TimeLeft :
9991      TimePlayed);
9992
9993   int action_arg_element_raw =
9994     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9995      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9996      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9997      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9998      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9999      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10000      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10001      EL_EMPTY);
10002   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10003
10004   int action_arg_direction =
10005     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10006      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10007      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10008      change->actual_trigger_side :
10009      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10010      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10011      MV_NONE);
10012
10013   int action_arg_number_min =
10014     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10015      CA_ARG_MIN);
10016
10017   int action_arg_number_max =
10018     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10019      action_type == CA_SET_LEVEL_GEMS ? 999 :
10020      action_type == CA_SET_LEVEL_TIME ? 9999 :
10021      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10022      action_type == CA_SET_CE_VALUE ? 9999 :
10023      action_type == CA_SET_CE_SCORE ? 9999 :
10024      CA_ARG_MAX);
10025
10026   int action_arg_number_reset =
10027     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10028      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10029      action_type == CA_SET_LEVEL_TIME ? level.time :
10030      action_type == CA_SET_LEVEL_SCORE ? 0 :
10031      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10032      action_type == CA_SET_CE_SCORE ? 0 :
10033      0);
10034
10035   int action_arg_number =
10036     (action_arg <= CA_ARG_MAX ? action_arg :
10037      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10038      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10039      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10040      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10041      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10042      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10043      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10044      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10045      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10046      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10047      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10048      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10049      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10050      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10051      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10052      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10053      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10054      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10055      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10056      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10057      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10058      -1);
10059
10060   int action_arg_number_old =
10061     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10062      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10063      action_type == CA_SET_LEVEL_SCORE ? game.score :
10064      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10065      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10066      0);
10067
10068   int action_arg_number_new =
10069     getModifiedActionNumber(action_arg_number_old,
10070                             action_mode, action_arg_number,
10071                             action_arg_number_min, action_arg_number_max);
10072
10073   int trigger_player_bits =
10074     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10075      change->actual_trigger_player_bits : change->trigger_player);
10076
10077   int action_arg_player_bits =
10078     (action_arg >= CA_ARG_PLAYER_1 &&
10079      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10080      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10081      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10082      PLAYER_BITS_ANY);
10083
10084   // ---------- execute action  -----------------------------------------------
10085
10086   switch (action_type)
10087   {
10088     case CA_NO_ACTION:
10089     {
10090       return;
10091     }
10092
10093     // ---------- level actions  ----------------------------------------------
10094
10095     case CA_RESTART_LEVEL:
10096     {
10097       game.restart_level = TRUE;
10098
10099       break;
10100     }
10101
10102     case CA_SHOW_ENVELOPE:
10103     {
10104       int element = getSpecialActionElement(action_arg_element,
10105                                             action_arg_number, EL_ENVELOPE_1);
10106
10107       if (IS_ENVELOPE(element))
10108         local_player->show_envelope = element;
10109
10110       break;
10111     }
10112
10113     case CA_SET_LEVEL_TIME:
10114     {
10115       if (level.time > 0)       // only modify limited time value
10116       {
10117         TimeLeft = action_arg_number_new;
10118
10119         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10120
10121         DisplayGameControlValues();
10122
10123         if (!TimeLeft && setup.time_limit)
10124           for (i = 0; i < MAX_PLAYERS; i++)
10125             KillPlayer(&stored_player[i]);
10126       }
10127
10128       break;
10129     }
10130
10131     case CA_SET_LEVEL_SCORE:
10132     {
10133       game.score = action_arg_number_new;
10134
10135       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10136
10137       DisplayGameControlValues();
10138
10139       break;
10140     }
10141
10142     case CA_SET_LEVEL_GEMS:
10143     {
10144       game.gems_still_needed = action_arg_number_new;
10145
10146       game.snapshot.collected_item = TRUE;
10147
10148       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10149
10150       DisplayGameControlValues();
10151
10152       break;
10153     }
10154
10155     case CA_SET_LEVEL_WIND:
10156     {
10157       game.wind_direction = action_arg_direction;
10158
10159       break;
10160     }
10161
10162     case CA_SET_LEVEL_RANDOM_SEED:
10163     {
10164       // ensure that setting a new random seed while playing is predictable
10165       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10166
10167       break;
10168     }
10169
10170     // ---------- player actions  ---------------------------------------------
10171
10172     case CA_MOVE_PLAYER:
10173     case CA_MOVE_PLAYER_NEW:
10174     {
10175       // automatically move to the next field in specified direction
10176       for (i = 0; i < MAX_PLAYERS; i++)
10177         if (trigger_player_bits & (1 << i))
10178           if (action_type == CA_MOVE_PLAYER ||
10179               stored_player[i].MovPos == 0)
10180             stored_player[i].programmed_action = action_arg_direction;
10181
10182       break;
10183     }
10184
10185     case CA_EXIT_PLAYER:
10186     {
10187       for (i = 0; i < MAX_PLAYERS; i++)
10188         if (action_arg_player_bits & (1 << i))
10189           ExitPlayer(&stored_player[i]);
10190
10191       if (game.players_still_needed == 0)
10192         LevelSolved();
10193
10194       break;
10195     }
10196
10197     case CA_KILL_PLAYER:
10198     {
10199       for (i = 0; i < MAX_PLAYERS; i++)
10200         if (action_arg_player_bits & (1 << i))
10201           KillPlayer(&stored_player[i]);
10202
10203       break;
10204     }
10205
10206     case CA_SET_PLAYER_KEYS:
10207     {
10208       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10209       int element = getSpecialActionElement(action_arg_element,
10210                                             action_arg_number, EL_KEY_1);
10211
10212       if (IS_KEY(element))
10213       {
10214         for (i = 0; i < MAX_PLAYERS; i++)
10215         {
10216           if (trigger_player_bits & (1 << i))
10217           {
10218             stored_player[i].key[KEY_NR(element)] = key_state;
10219
10220             DrawGameDoorValues();
10221           }
10222         }
10223       }
10224
10225       break;
10226     }
10227
10228     case CA_SET_PLAYER_SPEED:
10229     {
10230       for (i = 0; i < MAX_PLAYERS; i++)
10231       {
10232         if (trigger_player_bits & (1 << i))
10233         {
10234           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10235
10236           if (action_arg == CA_ARG_SPEED_FASTER &&
10237               stored_player[i].cannot_move)
10238           {
10239             action_arg_number = STEPSIZE_VERY_SLOW;
10240           }
10241           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10242                    action_arg == CA_ARG_SPEED_FASTER)
10243           {
10244             action_arg_number = 2;
10245             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10246                            CA_MODE_MULTIPLY);
10247           }
10248           else if (action_arg == CA_ARG_NUMBER_RESET)
10249           {
10250             action_arg_number = level.initial_player_stepsize[i];
10251           }
10252
10253           move_stepsize =
10254             getModifiedActionNumber(move_stepsize,
10255                                     action_mode,
10256                                     action_arg_number,
10257                                     action_arg_number_min,
10258                                     action_arg_number_max);
10259
10260           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10261         }
10262       }
10263
10264       break;
10265     }
10266
10267     case CA_SET_PLAYER_SHIELD:
10268     {
10269       for (i = 0; i < MAX_PLAYERS; i++)
10270       {
10271         if (trigger_player_bits & (1 << i))
10272         {
10273           if (action_arg == CA_ARG_SHIELD_OFF)
10274           {
10275             stored_player[i].shield_normal_time_left = 0;
10276             stored_player[i].shield_deadly_time_left = 0;
10277           }
10278           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10279           {
10280             stored_player[i].shield_normal_time_left = 999999;
10281           }
10282           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10283           {
10284             stored_player[i].shield_normal_time_left = 999999;
10285             stored_player[i].shield_deadly_time_left = 999999;
10286           }
10287         }
10288       }
10289
10290       break;
10291     }
10292
10293     case CA_SET_PLAYER_GRAVITY:
10294     {
10295       for (i = 0; i < MAX_PLAYERS; i++)
10296       {
10297         if (trigger_player_bits & (1 << i))
10298         {
10299           stored_player[i].gravity =
10300             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10301              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10302              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10303              stored_player[i].gravity);
10304         }
10305       }
10306
10307       break;
10308     }
10309
10310     case CA_SET_PLAYER_ARTWORK:
10311     {
10312       for (i = 0; i < MAX_PLAYERS; i++)
10313       {
10314         if (trigger_player_bits & (1 << i))
10315         {
10316           int artwork_element = action_arg_element;
10317
10318           if (action_arg == CA_ARG_ELEMENT_RESET)
10319             artwork_element =
10320               (level.use_artwork_element[i] ? level.artwork_element[i] :
10321                stored_player[i].element_nr);
10322
10323           if (stored_player[i].artwork_element != artwork_element)
10324             stored_player[i].Frame = 0;
10325
10326           stored_player[i].artwork_element = artwork_element;
10327
10328           SetPlayerWaiting(&stored_player[i], FALSE);
10329
10330           // set number of special actions for bored and sleeping animation
10331           stored_player[i].num_special_action_bored =
10332             get_num_special_action(artwork_element,
10333                                    ACTION_BORING_1, ACTION_BORING_LAST);
10334           stored_player[i].num_special_action_sleeping =
10335             get_num_special_action(artwork_element,
10336                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10337         }
10338       }
10339
10340       break;
10341     }
10342
10343     case CA_SET_PLAYER_INVENTORY:
10344     {
10345       for (i = 0; i < MAX_PLAYERS; i++)
10346       {
10347         struct PlayerInfo *player = &stored_player[i];
10348         int j, k;
10349
10350         if (trigger_player_bits & (1 << i))
10351         {
10352           int inventory_element = action_arg_element;
10353
10354           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10355               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10356               action_arg == CA_ARG_ELEMENT_ACTION)
10357           {
10358             int element = inventory_element;
10359             int collect_count = element_info[element].collect_count_initial;
10360
10361             if (!IS_CUSTOM_ELEMENT(element))
10362               collect_count = 1;
10363
10364             if (collect_count == 0)
10365               player->inventory_infinite_element = element;
10366             else
10367               for (k = 0; k < collect_count; k++)
10368                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10369                   player->inventory_element[player->inventory_size++] =
10370                     element;
10371           }
10372           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10373                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10374                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10375           {
10376             if (player->inventory_infinite_element != EL_UNDEFINED &&
10377                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10378                                      action_arg_element_raw))
10379               player->inventory_infinite_element = EL_UNDEFINED;
10380
10381             for (k = 0, j = 0; j < player->inventory_size; j++)
10382             {
10383               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10384                                         action_arg_element_raw))
10385                 player->inventory_element[k++] = player->inventory_element[j];
10386             }
10387
10388             player->inventory_size = k;
10389           }
10390           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10391           {
10392             if (player->inventory_size > 0)
10393             {
10394               for (j = 0; j < player->inventory_size - 1; j++)
10395                 player->inventory_element[j] = player->inventory_element[j + 1];
10396
10397               player->inventory_size--;
10398             }
10399           }
10400           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10401           {
10402             if (player->inventory_size > 0)
10403               player->inventory_size--;
10404           }
10405           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10406           {
10407             player->inventory_infinite_element = EL_UNDEFINED;
10408             player->inventory_size = 0;
10409           }
10410           else if (action_arg == CA_ARG_INVENTORY_RESET)
10411           {
10412             player->inventory_infinite_element = EL_UNDEFINED;
10413             player->inventory_size = 0;
10414
10415             if (level.use_initial_inventory[i])
10416             {
10417               for (j = 0; j < level.initial_inventory_size[i]; j++)
10418               {
10419                 int element = level.initial_inventory_content[i][j];
10420                 int collect_count = element_info[element].collect_count_initial;
10421
10422                 if (!IS_CUSTOM_ELEMENT(element))
10423                   collect_count = 1;
10424
10425                 if (collect_count == 0)
10426                   player->inventory_infinite_element = element;
10427                 else
10428                   for (k = 0; k < collect_count; k++)
10429                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10430                       player->inventory_element[player->inventory_size++] =
10431                         element;
10432               }
10433             }
10434           }
10435         }
10436       }
10437
10438       break;
10439     }
10440
10441     // ---------- CE actions  -------------------------------------------------
10442
10443     case CA_SET_CE_VALUE:
10444     {
10445       int last_ce_value = CustomValue[x][y];
10446
10447       CustomValue[x][y] = action_arg_number_new;
10448
10449       if (CustomValue[x][y] != last_ce_value)
10450       {
10451         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10452         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10453
10454         if (CustomValue[x][y] == 0)
10455         {
10456           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10457           ChangeCount[x][y] = 0;        // allow at least one more change
10458
10459           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10460           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10461         }
10462       }
10463
10464       break;
10465     }
10466
10467     case CA_SET_CE_SCORE:
10468     {
10469       int last_ce_score = ei->collect_score;
10470
10471       ei->collect_score = action_arg_number_new;
10472
10473       if (ei->collect_score != last_ce_score)
10474       {
10475         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10476         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10477
10478         if (ei->collect_score == 0)
10479         {
10480           int xx, yy;
10481
10482           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10483           ChangeCount[x][y] = 0;        // allow at least one more change
10484
10485           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10486           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10487
10488           /*
10489             This is a very special case that seems to be a mixture between
10490             CheckElementChange() and CheckTriggeredElementChange(): while
10491             the first one only affects single elements that are triggered
10492             directly, the second one affects multiple elements in the playfield
10493             that are triggered indirectly by another element. This is a third
10494             case: Changing the CE score always affects multiple identical CEs,
10495             so every affected CE must be checked, not only the single CE for
10496             which the CE score was changed in the first place (as every instance
10497             of that CE shares the same CE score, and therefore also can change)!
10498           */
10499           SCAN_PLAYFIELD(xx, yy)
10500           {
10501             if (Tile[xx][yy] == element)
10502               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10503                                  CE_SCORE_GETS_ZERO);
10504           }
10505         }
10506       }
10507
10508       break;
10509     }
10510
10511     case CA_SET_CE_ARTWORK:
10512     {
10513       int artwork_element = action_arg_element;
10514       boolean reset_frame = FALSE;
10515       int xx, yy;
10516
10517       if (action_arg == CA_ARG_ELEMENT_RESET)
10518         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10519                            element);
10520
10521       if (ei->gfx_element != artwork_element)
10522         reset_frame = TRUE;
10523
10524       ei->gfx_element = artwork_element;
10525
10526       SCAN_PLAYFIELD(xx, yy)
10527       {
10528         if (Tile[xx][yy] == element)
10529         {
10530           if (reset_frame)
10531           {
10532             ResetGfxAnimation(xx, yy);
10533             ResetRandomAnimationValue(xx, yy);
10534           }
10535
10536           TEST_DrawLevelField(xx, yy);
10537         }
10538       }
10539
10540       break;
10541     }
10542
10543     // ---------- engine actions  ---------------------------------------------
10544
10545     case CA_SET_ENGINE_SCAN_MODE:
10546     {
10547       InitPlayfieldScanMode(action_arg);
10548
10549       break;
10550     }
10551
10552     default:
10553       break;
10554   }
10555 }
10556
10557 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10558 {
10559   int old_element = Tile[x][y];
10560   int new_element = GetElementFromGroupElement(element);
10561   int previous_move_direction = MovDir[x][y];
10562   int last_ce_value = CustomValue[x][y];
10563   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10564   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10565   boolean add_player_onto_element = (new_element_is_player &&
10566                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10567                                      IS_WALKABLE(old_element));
10568
10569   if (!add_player_onto_element)
10570   {
10571     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10572       RemoveMovingField(x, y);
10573     else
10574       RemoveField(x, y);
10575
10576     Tile[x][y] = new_element;
10577
10578     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10579       MovDir[x][y] = previous_move_direction;
10580
10581     if (element_info[new_element].use_last_ce_value)
10582       CustomValue[x][y] = last_ce_value;
10583
10584     InitField_WithBug1(x, y, FALSE);
10585
10586     new_element = Tile[x][y];   // element may have changed
10587
10588     ResetGfxAnimation(x, y);
10589     ResetRandomAnimationValue(x, y);
10590
10591     TEST_DrawLevelField(x, y);
10592
10593     if (GFX_CRUMBLED(new_element))
10594       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10595   }
10596
10597   // check if element under the player changes from accessible to unaccessible
10598   // (needed for special case of dropping element which then changes)
10599   // (must be checked after creating new element for walkable group elements)
10600   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10601       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10602   {
10603     Bang(x, y);
10604
10605     return;
10606   }
10607
10608   // "ChangeCount" not set yet to allow "entered by player" change one time
10609   if (new_element_is_player)
10610     RelocatePlayer(x, y, new_element);
10611
10612   if (is_change)
10613     ChangeCount[x][y]++;        // count number of changes in the same frame
10614
10615   TestIfBadThingTouchesPlayer(x, y);
10616   TestIfPlayerTouchesCustomElement(x, y);
10617   TestIfElementTouchesCustomElement(x, y);
10618 }
10619
10620 static void CreateField(int x, int y, int element)
10621 {
10622   CreateFieldExt(x, y, element, FALSE);
10623 }
10624
10625 static void CreateElementFromChange(int x, int y, int element)
10626 {
10627   element = GET_VALID_RUNTIME_ELEMENT(element);
10628
10629   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10630   {
10631     int old_element = Tile[x][y];
10632
10633     // prevent changed element from moving in same engine frame
10634     // unless both old and new element can either fall or move
10635     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10636         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10637       Stop[x][y] = TRUE;
10638   }
10639
10640   CreateFieldExt(x, y, element, TRUE);
10641 }
10642
10643 static boolean ChangeElement(int x, int y, int element, int page)
10644 {
10645   struct ElementInfo *ei = &element_info[element];
10646   struct ElementChangeInfo *change = &ei->change_page[page];
10647   int ce_value = CustomValue[x][y];
10648   int ce_score = ei->collect_score;
10649   int target_element;
10650   int old_element = Tile[x][y];
10651
10652   // always use default change event to prevent running into a loop
10653   if (ChangeEvent[x][y] == -1)
10654     ChangeEvent[x][y] = CE_DELAY;
10655
10656   if (ChangeEvent[x][y] == CE_DELAY)
10657   {
10658     // reset actual trigger element, trigger player and action element
10659     change->actual_trigger_element = EL_EMPTY;
10660     change->actual_trigger_player = EL_EMPTY;
10661     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10662     change->actual_trigger_side = CH_SIDE_NONE;
10663     change->actual_trigger_ce_value = 0;
10664     change->actual_trigger_ce_score = 0;
10665   }
10666
10667   // do not change elements more than a specified maximum number of changes
10668   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10669     return FALSE;
10670
10671   ChangeCount[x][y]++;          // count number of changes in the same frame
10672
10673   if (change->explode)
10674   {
10675     Bang(x, y);
10676
10677     return TRUE;
10678   }
10679
10680   if (change->use_target_content)
10681   {
10682     boolean complete_replace = TRUE;
10683     boolean can_replace[3][3];
10684     int xx, yy;
10685
10686     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10687     {
10688       boolean is_empty;
10689       boolean is_walkable;
10690       boolean is_diggable;
10691       boolean is_collectible;
10692       boolean is_removable;
10693       boolean is_destructible;
10694       int ex = x + xx - 1;
10695       int ey = y + yy - 1;
10696       int content_element = change->target_content.e[xx][yy];
10697       int e;
10698
10699       can_replace[xx][yy] = TRUE;
10700
10701       if (ex == x && ey == y)   // do not check changing element itself
10702         continue;
10703
10704       if (content_element == EL_EMPTY_SPACE)
10705       {
10706         can_replace[xx][yy] = FALSE;    // do not replace border with space
10707
10708         continue;
10709       }
10710
10711       if (!IN_LEV_FIELD(ex, ey))
10712       {
10713         can_replace[xx][yy] = FALSE;
10714         complete_replace = FALSE;
10715
10716         continue;
10717       }
10718
10719       e = Tile[ex][ey];
10720
10721       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10722         e = MovingOrBlocked2Element(ex, ey);
10723
10724       is_empty = (IS_FREE(ex, ey) ||
10725                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10726
10727       is_walkable     = (is_empty || IS_WALKABLE(e));
10728       is_diggable     = (is_empty || IS_DIGGABLE(e));
10729       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10730       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10731       is_removable    = (is_diggable || is_collectible);
10732
10733       can_replace[xx][yy] =
10734         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10735           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10736           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10737           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10738           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10739           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10740          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10741
10742       if (!can_replace[xx][yy])
10743         complete_replace = FALSE;
10744     }
10745
10746     if (!change->only_if_complete || complete_replace)
10747     {
10748       boolean something_has_changed = FALSE;
10749
10750       if (change->only_if_complete && change->use_random_replace &&
10751           RND(100) < change->random_percentage)
10752         return FALSE;
10753
10754       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10755       {
10756         int ex = x + xx - 1;
10757         int ey = y + yy - 1;
10758         int content_element;
10759
10760         if (can_replace[xx][yy] && (!change->use_random_replace ||
10761                                     RND(100) < change->random_percentage))
10762         {
10763           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10764             RemoveMovingField(ex, ey);
10765
10766           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10767
10768           content_element = change->target_content.e[xx][yy];
10769           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10770                                               ce_value, ce_score);
10771
10772           CreateElementFromChange(ex, ey, target_element);
10773
10774           something_has_changed = TRUE;
10775
10776           // for symmetry reasons, freeze newly created border elements
10777           if (ex != x || ey != y)
10778             Stop[ex][ey] = TRUE;        // no more moving in this frame
10779         }
10780       }
10781
10782       if (something_has_changed)
10783       {
10784         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10785         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10786       }
10787     }
10788   }
10789   else
10790   {
10791     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10792                                         ce_value, ce_score);
10793
10794     if (element == EL_DIAGONAL_GROWING ||
10795         element == EL_DIAGONAL_SHRINKING)
10796     {
10797       target_element = Store[x][y];
10798
10799       Store[x][y] = EL_EMPTY;
10800     }
10801
10802     CreateElementFromChange(x, y, target_element);
10803
10804     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10805     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10806   }
10807
10808   // this uses direct change before indirect change
10809   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10810
10811   return TRUE;
10812 }
10813
10814 static void HandleElementChange(int x, int y, int page)
10815 {
10816   int element = MovingOrBlocked2Element(x, y);
10817   struct ElementInfo *ei = &element_info[element];
10818   struct ElementChangeInfo *change = &ei->change_page[page];
10819   boolean handle_action_before_change = FALSE;
10820
10821 #ifdef DEBUG
10822   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10823       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10824   {
10825     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10826           x, y, element, element_info[element].token_name);
10827     Debug("game:playing:HandleElementChange", "This should never happen!");
10828   }
10829 #endif
10830
10831   // this can happen with classic bombs on walkable, changing elements
10832   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10833   {
10834     return;
10835   }
10836
10837   if (ChangeDelay[x][y] == 0)           // initialize element change
10838   {
10839     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10840
10841     if (change->can_change)
10842     {
10843       // !!! not clear why graphic animation should be reset at all here !!!
10844       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10845       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10846
10847       /*
10848         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10849
10850         When using an animation frame delay of 1 (this only happens with
10851         "sp_zonk.moving.left/right" in the classic graphics), the default
10852         (non-moving) animation shows wrong animation frames (while the
10853         moving animation, like "sp_zonk.moving.left/right", is correct,
10854         so this graphical bug never shows up with the classic graphics).
10855         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10856         be drawn instead of the correct frames 0,1,2,3. This is caused by
10857         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10858         an element change: First when the change delay ("ChangeDelay[][]")
10859         counter has reached zero after decrementing, then a second time in
10860         the next frame (after "GfxFrame[][]" was already incremented) when
10861         "ChangeDelay[][]" is reset to the initial delay value again.
10862
10863         This causes frame 0 to be drawn twice, while the last frame won't
10864         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10865
10866         As some animations may already be cleverly designed around this bug
10867         (at least the "Snake Bite" snake tail animation does this), it cannot
10868         simply be fixed here without breaking such existing animations.
10869         Unfortunately, it cannot easily be detected if a graphics set was
10870         designed "before" or "after" the bug was fixed. As a workaround,
10871         a new graphics set option "game.graphics_engine_version" was added
10872         to be able to specify the game's major release version for which the
10873         graphics set was designed, which can then be used to decide if the
10874         bugfix should be used (version 4 and above) or not (version 3 or
10875         below, or if no version was specified at all, as with old sets).
10876
10877         (The wrong/fixed animation frames can be tested with the test level set
10878         "test_gfxframe" and level "000", which contains a specially prepared
10879         custom element at level position (x/y) == (11/9) which uses the zonk
10880         animation mentioned above. Using "game.graphics_engine_version: 4"
10881         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10882         This can also be seen from the debug output for this test element.)
10883       */
10884
10885       // when a custom element is about to change (for example by change delay),
10886       // do not reset graphic animation when the custom element is moving
10887       if (game.graphics_engine_version < 4 &&
10888           !IS_MOVING(x, y))
10889       {
10890         ResetGfxAnimation(x, y);
10891         ResetRandomAnimationValue(x, y);
10892       }
10893
10894       if (change->pre_change_function)
10895         change->pre_change_function(x, y);
10896     }
10897   }
10898
10899   ChangeDelay[x][y]--;
10900
10901   if (ChangeDelay[x][y] != 0)           // continue element change
10902   {
10903     if (change->can_change)
10904     {
10905       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10906
10907       if (IS_ANIMATED(graphic))
10908         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10909
10910       if (change->change_function)
10911         change->change_function(x, y);
10912     }
10913   }
10914   else                                  // finish element change
10915   {
10916     if (ChangePage[x][y] != -1)         // remember page from delayed change
10917     {
10918       page = ChangePage[x][y];
10919       ChangePage[x][y] = -1;
10920
10921       change = &ei->change_page[page];
10922     }
10923
10924     if (IS_MOVING(x, y))                // never change a running system ;-)
10925     {
10926       ChangeDelay[x][y] = 1;            // try change after next move step
10927       ChangePage[x][y] = page;          // remember page to use for change
10928
10929       return;
10930     }
10931
10932     // special case: set new level random seed before changing element
10933     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10934       handle_action_before_change = TRUE;
10935
10936     if (change->has_action && handle_action_before_change)
10937       ExecuteCustomElementAction(x, y, element, page);
10938
10939     if (change->can_change)
10940     {
10941       if (ChangeElement(x, y, element, page))
10942       {
10943         if (change->post_change_function)
10944           change->post_change_function(x, y);
10945       }
10946     }
10947
10948     if (change->has_action && !handle_action_before_change)
10949       ExecuteCustomElementAction(x, y, element, page);
10950   }
10951 }
10952
10953 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10954                                               int trigger_element,
10955                                               int trigger_event,
10956                                               int trigger_player,
10957                                               int trigger_side,
10958                                               int trigger_page)
10959 {
10960   boolean change_done_any = FALSE;
10961   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10962   int i;
10963
10964   if (!(trigger_events[trigger_element][trigger_event]))
10965     return FALSE;
10966
10967   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10968
10969   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10970   {
10971     int element = EL_CUSTOM_START + i;
10972     boolean change_done = FALSE;
10973     int p;
10974
10975     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10976         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10977       continue;
10978
10979     for (p = 0; p < element_info[element].num_change_pages; p++)
10980     {
10981       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10982
10983       if (change->can_change_or_has_action &&
10984           change->has_event[trigger_event] &&
10985           change->trigger_side & trigger_side &&
10986           change->trigger_player & trigger_player &&
10987           change->trigger_page & trigger_page_bits &&
10988           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10989       {
10990         change->actual_trigger_element = trigger_element;
10991         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10992         change->actual_trigger_player_bits = trigger_player;
10993         change->actual_trigger_side = trigger_side;
10994         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10995         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10996
10997         if ((change->can_change && !change_done) || change->has_action)
10998         {
10999           int x, y;
11000
11001           SCAN_PLAYFIELD(x, y)
11002           {
11003             if (Tile[x][y] == element)
11004             {
11005               if (change->can_change && !change_done)
11006               {
11007                 // if element already changed in this frame, not only prevent
11008                 // another element change (checked in ChangeElement()), but
11009                 // also prevent additional element actions for this element
11010
11011                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11012                     !level.use_action_after_change_bug)
11013                   continue;
11014
11015                 ChangeDelay[x][y] = 1;
11016                 ChangeEvent[x][y] = trigger_event;
11017
11018                 HandleElementChange(x, y, p);
11019               }
11020               else if (change->has_action)
11021               {
11022                 // if element already changed in this frame, not only prevent
11023                 // another element change (checked in ChangeElement()), but
11024                 // also prevent additional element actions for this element
11025
11026                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11027                     !level.use_action_after_change_bug)
11028                   continue;
11029
11030                 ExecuteCustomElementAction(x, y, element, p);
11031                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11032               }
11033             }
11034           }
11035
11036           if (change->can_change)
11037           {
11038             change_done = TRUE;
11039             change_done_any = TRUE;
11040           }
11041         }
11042       }
11043     }
11044   }
11045
11046   RECURSION_LOOP_DETECTION_END();
11047
11048   return change_done_any;
11049 }
11050
11051 static boolean CheckElementChangeExt(int x, int y,
11052                                      int element,
11053                                      int trigger_element,
11054                                      int trigger_event,
11055                                      int trigger_player,
11056                                      int trigger_side)
11057 {
11058   boolean change_done = FALSE;
11059   int p;
11060
11061   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11062       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11063     return FALSE;
11064
11065   if (Tile[x][y] == EL_BLOCKED)
11066   {
11067     Blocked2Moving(x, y, &x, &y);
11068     element = Tile[x][y];
11069   }
11070
11071   // check if element has already changed or is about to change after moving
11072   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11073        Tile[x][y] != element) ||
11074
11075       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11076        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11077         ChangePage[x][y] != -1)))
11078     return FALSE;
11079
11080   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11081
11082   for (p = 0; p < element_info[element].num_change_pages; p++)
11083   {
11084     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11085
11086     /* check trigger element for all events where the element that is checked
11087        for changing interacts with a directly adjacent element -- this is
11088        different to element changes that affect other elements to change on the
11089        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11090     boolean check_trigger_element =
11091       (trigger_event == CE_TOUCHING_X ||
11092        trigger_event == CE_HITTING_X ||
11093        trigger_event == CE_HIT_BY_X ||
11094        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11095
11096     if (change->can_change_or_has_action &&
11097         change->has_event[trigger_event] &&
11098         change->trigger_side & trigger_side &&
11099         change->trigger_player & trigger_player &&
11100         (!check_trigger_element ||
11101          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11102     {
11103       change->actual_trigger_element = trigger_element;
11104       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11105       change->actual_trigger_player_bits = trigger_player;
11106       change->actual_trigger_side = trigger_side;
11107       change->actual_trigger_ce_value = CustomValue[x][y];
11108       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11109
11110       // special case: trigger element not at (x,y) position for some events
11111       if (check_trigger_element)
11112       {
11113         static struct
11114         {
11115           int dx, dy;
11116         } move_xy[] =
11117           {
11118             {  0,  0 },
11119             { -1,  0 },
11120             { +1,  0 },
11121             {  0,  0 },
11122             {  0, -1 },
11123             {  0,  0 }, { 0, 0 }, { 0, 0 },
11124             {  0, +1 }
11125           };
11126
11127         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11128         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11129
11130         change->actual_trigger_ce_value = CustomValue[xx][yy];
11131         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11132       }
11133
11134       if (change->can_change && !change_done)
11135       {
11136         ChangeDelay[x][y] = 1;
11137         ChangeEvent[x][y] = trigger_event;
11138
11139         HandleElementChange(x, y, p);
11140
11141         change_done = TRUE;
11142       }
11143       else if (change->has_action)
11144       {
11145         ExecuteCustomElementAction(x, y, element, p);
11146         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11147       }
11148     }
11149   }
11150
11151   RECURSION_LOOP_DETECTION_END();
11152
11153   return change_done;
11154 }
11155
11156 static void PlayPlayerSound(struct PlayerInfo *player)
11157 {
11158   int jx = player->jx, jy = player->jy;
11159   int sound_element = player->artwork_element;
11160   int last_action = player->last_action_waiting;
11161   int action = player->action_waiting;
11162
11163   if (player->is_waiting)
11164   {
11165     if (action != last_action)
11166       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11167     else
11168       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11169   }
11170   else
11171   {
11172     if (action != last_action)
11173       StopSound(element_info[sound_element].sound[last_action]);
11174
11175     if (last_action == ACTION_SLEEPING)
11176       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11177   }
11178 }
11179
11180 static void PlayAllPlayersSound(void)
11181 {
11182   int i;
11183
11184   for (i = 0; i < MAX_PLAYERS; i++)
11185     if (stored_player[i].active)
11186       PlayPlayerSound(&stored_player[i]);
11187 }
11188
11189 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11190 {
11191   boolean last_waiting = player->is_waiting;
11192   int move_dir = player->MovDir;
11193
11194   player->dir_waiting = move_dir;
11195   player->last_action_waiting = player->action_waiting;
11196
11197   if (is_waiting)
11198   {
11199     if (!last_waiting)          // not waiting -> waiting
11200     {
11201       player->is_waiting = TRUE;
11202
11203       player->frame_counter_bored =
11204         FrameCounter +
11205         game.player_boring_delay_fixed +
11206         GetSimpleRandom(game.player_boring_delay_random);
11207       player->frame_counter_sleeping =
11208         FrameCounter +
11209         game.player_sleeping_delay_fixed +
11210         GetSimpleRandom(game.player_sleeping_delay_random);
11211
11212       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11213     }
11214
11215     if (game.player_sleeping_delay_fixed +
11216         game.player_sleeping_delay_random > 0 &&
11217         player->anim_delay_counter == 0 &&
11218         player->post_delay_counter == 0 &&
11219         FrameCounter >= player->frame_counter_sleeping)
11220       player->is_sleeping = TRUE;
11221     else if (game.player_boring_delay_fixed +
11222              game.player_boring_delay_random > 0 &&
11223              FrameCounter >= player->frame_counter_bored)
11224       player->is_bored = TRUE;
11225
11226     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11227                               player->is_bored ? ACTION_BORING :
11228                               ACTION_WAITING);
11229
11230     if (player->is_sleeping && player->use_murphy)
11231     {
11232       // special case for sleeping Murphy when leaning against non-free tile
11233
11234       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11235           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11236            !IS_MOVING(player->jx - 1, player->jy)))
11237         move_dir = MV_LEFT;
11238       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11239                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11240                 !IS_MOVING(player->jx + 1, player->jy)))
11241         move_dir = MV_RIGHT;
11242       else
11243         player->is_sleeping = FALSE;
11244
11245       player->dir_waiting = move_dir;
11246     }
11247
11248     if (player->is_sleeping)
11249     {
11250       if (player->num_special_action_sleeping > 0)
11251       {
11252         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11253         {
11254           int last_special_action = player->special_action_sleeping;
11255           int num_special_action = player->num_special_action_sleeping;
11256           int special_action =
11257             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11258              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11259              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11260              last_special_action + 1 : ACTION_SLEEPING);
11261           int special_graphic =
11262             el_act_dir2img(player->artwork_element, special_action, move_dir);
11263
11264           player->anim_delay_counter =
11265             graphic_info[special_graphic].anim_delay_fixed +
11266             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11267           player->post_delay_counter =
11268             graphic_info[special_graphic].post_delay_fixed +
11269             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11270
11271           player->special_action_sleeping = special_action;
11272         }
11273
11274         if (player->anim_delay_counter > 0)
11275         {
11276           player->action_waiting = player->special_action_sleeping;
11277           player->anim_delay_counter--;
11278         }
11279         else if (player->post_delay_counter > 0)
11280         {
11281           player->post_delay_counter--;
11282         }
11283       }
11284     }
11285     else if (player->is_bored)
11286     {
11287       if (player->num_special_action_bored > 0)
11288       {
11289         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11290         {
11291           int special_action =
11292             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11293           int special_graphic =
11294             el_act_dir2img(player->artwork_element, special_action, move_dir);
11295
11296           player->anim_delay_counter =
11297             graphic_info[special_graphic].anim_delay_fixed +
11298             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11299           player->post_delay_counter =
11300             graphic_info[special_graphic].post_delay_fixed +
11301             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11302
11303           player->special_action_bored = special_action;
11304         }
11305
11306         if (player->anim_delay_counter > 0)
11307         {
11308           player->action_waiting = player->special_action_bored;
11309           player->anim_delay_counter--;
11310         }
11311         else if (player->post_delay_counter > 0)
11312         {
11313           player->post_delay_counter--;
11314         }
11315       }
11316     }
11317   }
11318   else if (last_waiting)        // waiting -> not waiting
11319   {
11320     player->is_waiting = FALSE;
11321     player->is_bored = FALSE;
11322     player->is_sleeping = FALSE;
11323
11324     player->frame_counter_bored = -1;
11325     player->frame_counter_sleeping = -1;
11326
11327     player->anim_delay_counter = 0;
11328     player->post_delay_counter = 0;
11329
11330     player->dir_waiting = player->MovDir;
11331     player->action_waiting = ACTION_DEFAULT;
11332
11333     player->special_action_bored = ACTION_DEFAULT;
11334     player->special_action_sleeping = ACTION_DEFAULT;
11335   }
11336 }
11337
11338 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11339 {
11340   if ((!player->is_moving  && player->was_moving) ||
11341       (player->MovPos == 0 && player->was_moving) ||
11342       (player->is_snapping && !player->was_snapping) ||
11343       (player->is_dropping && !player->was_dropping))
11344   {
11345     if (!CheckSaveEngineSnapshotToList())
11346       return;
11347
11348     player->was_moving = FALSE;
11349     player->was_snapping = TRUE;
11350     player->was_dropping = TRUE;
11351   }
11352   else
11353   {
11354     if (player->is_moving)
11355       player->was_moving = TRUE;
11356
11357     if (!player->is_snapping)
11358       player->was_snapping = FALSE;
11359
11360     if (!player->is_dropping)
11361       player->was_dropping = FALSE;
11362   }
11363
11364   static struct MouseActionInfo mouse_action_last = { 0 };
11365   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11366   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11367
11368   if (new_released)
11369     CheckSaveEngineSnapshotToList();
11370
11371   mouse_action_last = mouse_action;
11372 }
11373
11374 static void CheckSingleStepMode(struct PlayerInfo *player)
11375 {
11376   if (tape.single_step && tape.recording && !tape.pausing)
11377   {
11378     // as it is called "single step mode", just return to pause mode when the
11379     // player stopped moving after one tile (or never starts moving at all)
11380     // (reverse logic needed here in case single step mode used in team mode)
11381     if (player->is_moving ||
11382         player->is_pushing ||
11383         player->is_dropping_pressed ||
11384         player->effective_mouse_action.button)
11385       game.enter_single_step_mode = FALSE;
11386   }
11387
11388   CheckSaveEngineSnapshot(player);
11389 }
11390
11391 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11392 {
11393   int left      = player_action & JOY_LEFT;
11394   int right     = player_action & JOY_RIGHT;
11395   int up        = player_action & JOY_UP;
11396   int down      = player_action & JOY_DOWN;
11397   int button1   = player_action & JOY_BUTTON_1;
11398   int button2   = player_action & JOY_BUTTON_2;
11399   int dx        = (left ? -1 : right ? 1 : 0);
11400   int dy        = (up   ? -1 : down  ? 1 : 0);
11401
11402   if (!player->active || tape.pausing)
11403     return 0;
11404
11405   if (player_action)
11406   {
11407     if (button1)
11408       SnapField(player, dx, dy);
11409     else
11410     {
11411       if (button2)
11412         DropElement(player);
11413
11414       MovePlayer(player, dx, dy);
11415     }
11416
11417     CheckSingleStepMode(player);
11418
11419     SetPlayerWaiting(player, FALSE);
11420
11421     return player_action;
11422   }
11423   else
11424   {
11425     // no actions for this player (no input at player's configured device)
11426
11427     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11428     SnapField(player, 0, 0);
11429     CheckGravityMovementWhenNotMoving(player);
11430
11431     if (player->MovPos == 0)
11432       SetPlayerWaiting(player, TRUE);
11433
11434     if (player->MovPos == 0)    // needed for tape.playing
11435       player->is_moving = FALSE;
11436
11437     player->is_dropping = FALSE;
11438     player->is_dropping_pressed = FALSE;
11439     player->drop_pressed_delay = 0;
11440
11441     CheckSingleStepMode(player);
11442
11443     return 0;
11444   }
11445 }
11446
11447 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11448                                          byte *tape_action)
11449 {
11450   if (!tape.use_mouse_actions)
11451     return;
11452
11453   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11454   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11455   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11456 }
11457
11458 static void SetTapeActionFromMouseAction(byte *tape_action,
11459                                          struct MouseActionInfo *mouse_action)
11460 {
11461   if (!tape.use_mouse_actions)
11462     return;
11463
11464   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11465   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11466   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11467 }
11468
11469 static void CheckLevelSolved(void)
11470 {
11471   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11472   {
11473     if (game_em.level_solved &&
11474         !game_em.game_over)                             // game won
11475     {
11476       LevelSolved();
11477
11478       game_em.game_over = TRUE;
11479
11480       game.all_players_gone = TRUE;
11481     }
11482
11483     if (game_em.game_over)                              // game lost
11484       game.all_players_gone = TRUE;
11485   }
11486   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11487   {
11488     if (game_sp.level_solved &&
11489         !game_sp.game_over)                             // game won
11490     {
11491       LevelSolved();
11492
11493       game_sp.game_over = TRUE;
11494
11495       game.all_players_gone = TRUE;
11496     }
11497
11498     if (game_sp.game_over)                              // game lost
11499       game.all_players_gone = TRUE;
11500   }
11501   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11502   {
11503     if (game_mm.level_solved &&
11504         !game_mm.game_over)                             // game won
11505     {
11506       LevelSolved();
11507
11508       game_mm.game_over = TRUE;
11509
11510       game.all_players_gone = TRUE;
11511     }
11512
11513     if (game_mm.game_over)                              // game lost
11514       game.all_players_gone = TRUE;
11515   }
11516 }
11517
11518 static void CheckLevelTime(void)
11519 {
11520   int i;
11521
11522   if (TimeFrames >= FRAMES_PER_SECOND)
11523   {
11524     TimeFrames = 0;
11525     TapeTime++;
11526
11527     for (i = 0; i < MAX_PLAYERS; i++)
11528     {
11529       struct PlayerInfo *player = &stored_player[i];
11530
11531       if (SHIELD_ON(player))
11532       {
11533         player->shield_normal_time_left--;
11534
11535         if (player->shield_deadly_time_left > 0)
11536           player->shield_deadly_time_left--;
11537       }
11538     }
11539
11540     if (!game.LevelSolved && !level.use_step_counter)
11541     {
11542       TimePlayed++;
11543
11544       if (TimeLeft > 0)
11545       {
11546         TimeLeft--;
11547
11548         if (TimeLeft <= 10 && setup.time_limit)
11549           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11550
11551         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11552            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11553
11554         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11555
11556         if (!TimeLeft && setup.time_limit)
11557         {
11558           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11559             game_em.lev->killed_out_of_time = TRUE;
11560           else
11561             for (i = 0; i < MAX_PLAYERS; i++)
11562               KillPlayer(&stored_player[i]);
11563         }
11564       }
11565       else if (game.no_time_limit && !game.all_players_gone)
11566       {
11567         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11568       }
11569
11570       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11571     }
11572
11573     if (tape.recording || tape.playing)
11574       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11575   }
11576
11577   if (tape.recording || tape.playing)
11578     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11579
11580   UpdateAndDisplayGameControlValues();
11581 }
11582
11583 void AdvanceFrameAndPlayerCounters(int player_nr)
11584 {
11585   int i;
11586
11587   // advance frame counters (global frame counter and time frame counter)
11588   FrameCounter++;
11589   TimeFrames++;
11590
11591   // advance player counters (counters for move delay, move animation etc.)
11592   for (i = 0; i < MAX_PLAYERS; i++)
11593   {
11594     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11595     int move_delay_value = stored_player[i].move_delay_value;
11596     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11597
11598     if (!advance_player_counters)       // not all players may be affected
11599       continue;
11600
11601     if (move_frames == 0)       // less than one move per game frame
11602     {
11603       int stepsize = TILEX / move_delay_value;
11604       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11605       int count = (stored_player[i].is_moving ?
11606                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11607
11608       if (count % delay == 0)
11609         move_frames = 1;
11610     }
11611
11612     stored_player[i].Frame += move_frames;
11613
11614     if (stored_player[i].MovPos != 0)
11615       stored_player[i].StepFrame += move_frames;
11616
11617     if (stored_player[i].move_delay > 0)
11618       stored_player[i].move_delay--;
11619
11620     // due to bugs in previous versions, counter must count up, not down
11621     if (stored_player[i].push_delay != -1)
11622       stored_player[i].push_delay++;
11623
11624     if (stored_player[i].drop_delay > 0)
11625       stored_player[i].drop_delay--;
11626
11627     if (stored_player[i].is_dropping_pressed)
11628       stored_player[i].drop_pressed_delay++;
11629   }
11630 }
11631
11632 void StartGameActions(boolean init_network_game, boolean record_tape,
11633                       int random_seed)
11634 {
11635   unsigned int new_random_seed = InitRND(random_seed);
11636
11637   if (record_tape)
11638     TapeStartRecording(new_random_seed);
11639
11640   if (init_network_game)
11641   {
11642     SendToServer_LevelFile();
11643     SendToServer_StartPlaying();
11644
11645     return;
11646   }
11647
11648   InitGame();
11649 }
11650
11651 static void GameActionsExt(void)
11652 {
11653 #if 0
11654   static unsigned int game_frame_delay = 0;
11655 #endif
11656   unsigned int game_frame_delay_value;
11657   byte *recorded_player_action;
11658   byte summarized_player_action = 0;
11659   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11660   int i;
11661
11662   // detect endless loops, caused by custom element programming
11663   if (recursion_loop_detected && recursion_loop_depth == 0)
11664   {
11665     char *message = getStringCat3("Internal Error! Element ",
11666                                   EL_NAME(recursion_loop_element),
11667                                   " caused endless loop! Quit the game?");
11668
11669     Warn("element '%s' caused endless loop in game engine",
11670          EL_NAME(recursion_loop_element));
11671
11672     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11673
11674     recursion_loop_detected = FALSE;    // if game should be continued
11675
11676     free(message);
11677
11678     return;
11679   }
11680
11681   if (game.restart_level)
11682     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11683
11684   CheckLevelSolved();
11685
11686   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11687     GameWon();
11688
11689   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11690     TapeStop();
11691
11692   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11693     return;
11694
11695   game_frame_delay_value =
11696     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11697
11698   if (tape.playing && tape.warp_forward && !tape.pausing)
11699     game_frame_delay_value = 0;
11700
11701   SetVideoFrameDelay(game_frame_delay_value);
11702
11703   // (de)activate virtual buttons depending on current game status
11704   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11705   {
11706     if (game.all_players_gone)  // if no players there to be controlled anymore
11707       SetOverlayActive(FALSE);
11708     else if (!tape.playing)     // if game continues after tape stopped playing
11709       SetOverlayActive(TRUE);
11710   }
11711
11712 #if 0
11713 #if 0
11714   // ---------- main game synchronization point ----------
11715
11716   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11717
11718   Debug("game:playing:skip", "skip == %d", skip);
11719
11720 #else
11721   // ---------- main game synchronization point ----------
11722
11723   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11724 #endif
11725 #endif
11726
11727   if (network_playing && !network_player_action_received)
11728   {
11729     // try to get network player actions in time
11730
11731     // last chance to get network player actions without main loop delay
11732     HandleNetworking();
11733
11734     // game was quit by network peer
11735     if (game_status != GAME_MODE_PLAYING)
11736       return;
11737
11738     // check if network player actions still missing and game still running
11739     if (!network_player_action_received && !checkGameEnded())
11740       return;           // failed to get network player actions in time
11741
11742     // do not yet reset "network_player_action_received" (for tape.pausing)
11743   }
11744
11745   if (tape.pausing)
11746     return;
11747
11748   // at this point we know that we really continue executing the game
11749
11750   network_player_action_received = FALSE;
11751
11752   // when playing tape, read previously recorded player input from tape data
11753   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11754
11755   local_player->effective_mouse_action = local_player->mouse_action;
11756
11757   if (recorded_player_action != NULL)
11758     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11759                                  recorded_player_action);
11760
11761   // TapePlayAction() may return NULL when toggling to "pause before death"
11762   if (tape.pausing)
11763     return;
11764
11765   if (tape.set_centered_player)
11766   {
11767     game.centered_player_nr_next = tape.centered_player_nr_next;
11768     game.set_centered_player = TRUE;
11769   }
11770
11771   for (i = 0; i < MAX_PLAYERS; i++)
11772   {
11773     summarized_player_action |= stored_player[i].action;
11774
11775     if (!network_playing && (game.team_mode || tape.playing))
11776       stored_player[i].effective_action = stored_player[i].action;
11777   }
11778
11779   if (network_playing && !checkGameEnded())
11780     SendToServer_MovePlayer(summarized_player_action);
11781
11782   // summarize all actions at local players mapped input device position
11783   // (this allows using different input devices in single player mode)
11784   if (!network.enabled && !game.team_mode)
11785     stored_player[map_player_action[local_player->index_nr]].effective_action =
11786       summarized_player_action;
11787
11788   // summarize all actions at centered player in local team mode
11789   if (tape.recording &&
11790       setup.team_mode && !network.enabled &&
11791       setup.input_on_focus &&
11792       game.centered_player_nr != -1)
11793   {
11794     for (i = 0; i < MAX_PLAYERS; i++)
11795       stored_player[map_player_action[i]].effective_action =
11796         (i == game.centered_player_nr ? summarized_player_action : 0);
11797   }
11798
11799   if (recorded_player_action != NULL)
11800     for (i = 0; i < MAX_PLAYERS; i++)
11801       stored_player[i].effective_action = recorded_player_action[i];
11802
11803   for (i = 0; i < MAX_PLAYERS; i++)
11804   {
11805     tape_action[i] = stored_player[i].effective_action;
11806
11807     /* (this may happen in the RND game engine if a player was not present on
11808        the playfield on level start, but appeared later from a custom element */
11809     if (setup.team_mode &&
11810         tape.recording &&
11811         tape_action[i] &&
11812         !tape.player_participates[i])
11813       tape.player_participates[i] = TRUE;
11814   }
11815
11816   SetTapeActionFromMouseAction(tape_action,
11817                                &local_player->effective_mouse_action);
11818
11819   // only record actions from input devices, but not programmed actions
11820   if (tape.recording)
11821     TapeRecordAction(tape_action);
11822
11823   // remember if game was played (especially after tape stopped playing)
11824   if (!tape.playing && summarized_player_action)
11825     game.GamePlayed = TRUE;
11826
11827 #if USE_NEW_PLAYER_ASSIGNMENTS
11828   // !!! also map player actions in single player mode !!!
11829   // if (game.team_mode)
11830   if (1)
11831   {
11832     byte mapped_action[MAX_PLAYERS];
11833
11834 #if DEBUG_PLAYER_ACTIONS
11835     for (i = 0; i < MAX_PLAYERS; i++)
11836       DebugContinued("", "%d, ", stored_player[i].effective_action);
11837 #endif
11838
11839     for (i = 0; i < MAX_PLAYERS; i++)
11840       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11841
11842     for (i = 0; i < MAX_PLAYERS; i++)
11843       stored_player[i].effective_action = mapped_action[i];
11844
11845 #if DEBUG_PLAYER_ACTIONS
11846     DebugContinued("", "=> ");
11847     for (i = 0; i < MAX_PLAYERS; i++)
11848       DebugContinued("", "%d, ", stored_player[i].effective_action);
11849     DebugContinued("game:playing:player", "\n");
11850 #endif
11851   }
11852 #if DEBUG_PLAYER_ACTIONS
11853   else
11854   {
11855     for (i = 0; i < MAX_PLAYERS; i++)
11856       DebugContinued("", "%d, ", stored_player[i].effective_action);
11857     DebugContinued("game:playing:player", "\n");
11858   }
11859 #endif
11860 #endif
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863   {
11864     // allow engine snapshot in case of changed movement attempt
11865     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11866         (stored_player[i].effective_action & KEY_MOTION))
11867       game.snapshot.changed_action = TRUE;
11868
11869     // allow engine snapshot in case of snapping/dropping attempt
11870     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11871         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11872       game.snapshot.changed_action = TRUE;
11873
11874     game.snapshot.last_action[i] = stored_player[i].effective_action;
11875   }
11876
11877   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11878   {
11879     GameActions_EM_Main();
11880   }
11881   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11882   {
11883     GameActions_SP_Main();
11884   }
11885   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11886   {
11887     GameActions_MM_Main();
11888   }
11889   else
11890   {
11891     GameActions_RND_Main();
11892   }
11893
11894   BlitScreenToBitmap(backbuffer);
11895
11896   CheckLevelSolved();
11897   CheckLevelTime();
11898
11899   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11900
11901   if (global.show_frames_per_second)
11902   {
11903     static unsigned int fps_counter = 0;
11904     static int fps_frames = 0;
11905     unsigned int fps_delay_ms = Counter() - fps_counter;
11906
11907     fps_frames++;
11908
11909     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11910     {
11911       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11912
11913       fps_frames = 0;
11914       fps_counter = Counter();
11915
11916       // always draw FPS to screen after FPS value was updated
11917       redraw_mask |= REDRAW_FPS;
11918     }
11919
11920     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11921     if (GetDrawDeactivationMask() == REDRAW_NONE)
11922       redraw_mask |= REDRAW_FPS;
11923   }
11924 }
11925
11926 static void GameActions_CheckSaveEngineSnapshot(void)
11927 {
11928   if (!game.snapshot.save_snapshot)
11929     return;
11930
11931   // clear flag for saving snapshot _before_ saving snapshot
11932   game.snapshot.save_snapshot = FALSE;
11933
11934   SaveEngineSnapshotToList();
11935 }
11936
11937 void GameActions(void)
11938 {
11939   GameActionsExt();
11940
11941   GameActions_CheckSaveEngineSnapshot();
11942 }
11943
11944 void GameActions_EM_Main(void)
11945 {
11946   byte effective_action[MAX_PLAYERS];
11947   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11948   int i;
11949
11950   for (i = 0; i < MAX_PLAYERS; i++)
11951     effective_action[i] = stored_player[i].effective_action;
11952
11953   GameActions_EM(effective_action, warp_mode);
11954 }
11955
11956 void GameActions_SP_Main(void)
11957 {
11958   byte effective_action[MAX_PLAYERS];
11959   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11960   int i;
11961
11962   for (i = 0; i < MAX_PLAYERS; i++)
11963     effective_action[i] = stored_player[i].effective_action;
11964
11965   GameActions_SP(effective_action, warp_mode);
11966
11967   for (i = 0; i < MAX_PLAYERS; i++)
11968   {
11969     if (stored_player[i].force_dropping)
11970       stored_player[i].action |= KEY_BUTTON_DROP;
11971
11972     stored_player[i].force_dropping = FALSE;
11973   }
11974 }
11975
11976 void GameActions_MM_Main(void)
11977 {
11978   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11979
11980   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11981 }
11982
11983 void GameActions_RND_Main(void)
11984 {
11985   GameActions_RND();
11986 }
11987
11988 void GameActions_RND(void)
11989 {
11990   static struct MouseActionInfo mouse_action_last = { 0 };
11991   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11992   int magic_wall_x = 0, magic_wall_y = 0;
11993   int i, x, y, element, graphic, last_gfx_frame;
11994
11995   InitPlayfieldScanModeVars();
11996
11997   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11998   {
11999     SCAN_PLAYFIELD(x, y)
12000     {
12001       ChangeCount[x][y] = 0;
12002       ChangeEvent[x][y] = -1;
12003     }
12004   }
12005
12006   if (game.set_centered_player)
12007   {
12008     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12009
12010     // switching to "all players" only possible if all players fit to screen
12011     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12012     {
12013       game.centered_player_nr_next = game.centered_player_nr;
12014       game.set_centered_player = FALSE;
12015     }
12016
12017     // do not switch focus to non-existing (or non-active) player
12018     if (game.centered_player_nr_next >= 0 &&
12019         !stored_player[game.centered_player_nr_next].active)
12020     {
12021       game.centered_player_nr_next = game.centered_player_nr;
12022       game.set_centered_player = FALSE;
12023     }
12024   }
12025
12026   if (game.set_centered_player &&
12027       ScreenMovPos == 0)        // screen currently aligned at tile position
12028   {
12029     int sx, sy;
12030
12031     if (game.centered_player_nr_next == -1)
12032     {
12033       setScreenCenteredToAllPlayers(&sx, &sy);
12034     }
12035     else
12036     {
12037       sx = stored_player[game.centered_player_nr_next].jx;
12038       sy = stored_player[game.centered_player_nr_next].jy;
12039     }
12040
12041     game.centered_player_nr = game.centered_player_nr_next;
12042     game.set_centered_player = FALSE;
12043
12044     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12045     DrawGameDoorValues();
12046   }
12047
12048   // check single step mode (set flag and clear again if any player is active)
12049   game.enter_single_step_mode =
12050     (tape.single_step && tape.recording && !tape.pausing);
12051
12052   for (i = 0; i < MAX_PLAYERS; i++)
12053   {
12054     int actual_player_action = stored_player[i].effective_action;
12055
12056 #if 1
12057     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12058        - rnd_equinox_tetrachloride 048
12059        - rnd_equinox_tetrachloride_ii 096
12060        - rnd_emanuel_schmieg 002
12061        - doctor_sloan_ww 001, 020
12062     */
12063     if (stored_player[i].MovPos == 0)
12064       CheckGravityMovement(&stored_player[i]);
12065 #endif
12066
12067     // overwrite programmed action with tape action
12068     if (stored_player[i].programmed_action)
12069       actual_player_action = stored_player[i].programmed_action;
12070
12071     PlayerActions(&stored_player[i], actual_player_action);
12072
12073     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12074   }
12075
12076   // single step pause mode may already have been toggled by "ScrollPlayer()"
12077   if (game.enter_single_step_mode && !tape.pausing)
12078     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12079
12080   ScrollScreen(NULL, SCROLL_GO_ON);
12081
12082   /* for backwards compatibility, the following code emulates a fixed bug that
12083      occured when pushing elements (causing elements that just made their last
12084      pushing step to already (if possible) make their first falling step in the
12085      same game frame, which is bad); this code is also needed to use the famous
12086      "spring push bug" which is used in older levels and might be wanted to be
12087      used also in newer levels, but in this case the buggy pushing code is only
12088      affecting the "spring" element and no other elements */
12089
12090   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12091   {
12092     for (i = 0; i < MAX_PLAYERS; i++)
12093     {
12094       struct PlayerInfo *player = &stored_player[i];
12095       int x = player->jx;
12096       int y = player->jy;
12097
12098       if (player->active && player->is_pushing && player->is_moving &&
12099           IS_MOVING(x, y) &&
12100           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12101            Tile[x][y] == EL_SPRING))
12102       {
12103         ContinueMoving(x, y);
12104
12105         // continue moving after pushing (this is actually a bug)
12106         if (!IS_MOVING(x, y))
12107           Stop[x][y] = FALSE;
12108       }
12109     }
12110   }
12111
12112   SCAN_PLAYFIELD(x, y)
12113   {
12114     Last[x][y] = Tile[x][y];
12115
12116     ChangeCount[x][y] = 0;
12117     ChangeEvent[x][y] = -1;
12118
12119     // this must be handled before main playfield loop
12120     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12121     {
12122       MovDelay[x][y]--;
12123       if (MovDelay[x][y] <= 0)
12124         RemoveField(x, y);
12125     }
12126
12127     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12128     {
12129       MovDelay[x][y]--;
12130       if (MovDelay[x][y] <= 0)
12131       {
12132         int element = Store[x][y];
12133         int move_direction = MovDir[x][y];
12134         int player_index_bit = Store2[x][y];
12135
12136         Store[x][y] = 0;
12137         Store2[x][y] = 0;
12138
12139         RemoveField(x, y);
12140         TEST_DrawLevelField(x, y);
12141
12142         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12143       }
12144     }
12145
12146 #if DEBUG
12147     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12148     {
12149       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12150             x, y);
12151       Debug("game:playing:GameActions_RND", "This should never happen!");
12152
12153       ChangePage[x][y] = -1;
12154     }
12155 #endif
12156
12157     Stop[x][y] = FALSE;
12158     if (WasJustMoving[x][y] > 0)
12159       WasJustMoving[x][y]--;
12160     if (WasJustFalling[x][y] > 0)
12161       WasJustFalling[x][y]--;
12162     if (CheckCollision[x][y] > 0)
12163       CheckCollision[x][y]--;
12164     if (CheckImpact[x][y] > 0)
12165       CheckImpact[x][y]--;
12166
12167     GfxFrame[x][y]++;
12168
12169     /* reset finished pushing action (not done in ContinueMoving() to allow
12170        continuous pushing animation for elements with zero push delay) */
12171     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12172     {
12173       ResetGfxAnimation(x, y);
12174       TEST_DrawLevelField(x, y);
12175     }
12176
12177 #if DEBUG
12178     if (IS_BLOCKED(x, y))
12179     {
12180       int oldx, oldy;
12181
12182       Blocked2Moving(x, y, &oldx, &oldy);
12183       if (!IS_MOVING(oldx, oldy))
12184       {
12185         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12186         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12187         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12188         Debug("game:playing:GameActions_RND", "This should never happen!");
12189       }
12190     }
12191 #endif
12192   }
12193
12194   if (mouse_action.button)
12195   {
12196     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12197     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12198
12199     x = mouse_action.lx;
12200     y = mouse_action.ly;
12201     element = Tile[x][y];
12202
12203     if (new_button)
12204     {
12205       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12206       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12207                                          ch_button);
12208     }
12209
12210     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12211     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12212                                        ch_button);
12213   }
12214
12215   SCAN_PLAYFIELD(x, y)
12216   {
12217     element = Tile[x][y];
12218     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12219     last_gfx_frame = GfxFrame[x][y];
12220
12221     ResetGfxFrame(x, y);
12222
12223     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12224       DrawLevelGraphicAnimation(x, y, graphic);
12225
12226     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12227         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12228       ResetRandomAnimationValue(x, y);
12229
12230     SetRandomAnimationValue(x, y);
12231
12232     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12233
12234     if (IS_INACTIVE(element))
12235     {
12236       if (IS_ANIMATED(graphic))
12237         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12238
12239       continue;
12240     }
12241
12242     // this may take place after moving, so 'element' may have changed
12243     if (IS_CHANGING(x, y) &&
12244         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12245     {
12246       int page = element_info[element].event_page_nr[CE_DELAY];
12247
12248       HandleElementChange(x, y, page);
12249
12250       element = Tile[x][y];
12251       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12252     }
12253
12254     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12255     {
12256       StartMoving(x, y);
12257
12258       element = Tile[x][y];
12259       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12260
12261       if (IS_ANIMATED(graphic) &&
12262           !IS_MOVING(x, y) &&
12263           !Stop[x][y])
12264         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12265
12266       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12267         TEST_DrawTwinkleOnField(x, y);
12268     }
12269     else if (element == EL_ACID)
12270     {
12271       if (!Stop[x][y])
12272         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12273     }
12274     else if ((element == EL_EXIT_OPEN ||
12275               element == EL_EM_EXIT_OPEN ||
12276               element == EL_SP_EXIT_OPEN ||
12277               element == EL_STEEL_EXIT_OPEN ||
12278               element == EL_EM_STEEL_EXIT_OPEN ||
12279               element == EL_SP_TERMINAL ||
12280               element == EL_SP_TERMINAL_ACTIVE ||
12281               element == EL_EXTRA_TIME ||
12282               element == EL_SHIELD_NORMAL ||
12283               element == EL_SHIELD_DEADLY) &&
12284              IS_ANIMATED(graphic))
12285       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12286     else if (IS_MOVING(x, y))
12287       ContinueMoving(x, y);
12288     else if (IS_ACTIVE_BOMB(element))
12289       CheckDynamite(x, y);
12290     else if (element == EL_AMOEBA_GROWING)
12291       AmoebaGrowing(x, y);
12292     else if (element == EL_AMOEBA_SHRINKING)
12293       AmoebaShrinking(x, y);
12294
12295 #if !USE_NEW_AMOEBA_CODE
12296     else if (IS_AMOEBALIVE(element))
12297       AmoebaReproduce(x, y);
12298 #endif
12299
12300     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12301       Life(x, y);
12302     else if (element == EL_EXIT_CLOSED)
12303       CheckExit(x, y);
12304     else if (element == EL_EM_EXIT_CLOSED)
12305       CheckExitEM(x, y);
12306     else if (element == EL_STEEL_EXIT_CLOSED)
12307       CheckExitSteel(x, y);
12308     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12309       CheckExitSteelEM(x, y);
12310     else if (element == EL_SP_EXIT_CLOSED)
12311       CheckExitSP(x, y);
12312     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12313              element == EL_EXPANDABLE_STEELWALL_GROWING)
12314       MauerWaechst(x, y);
12315     else if (element == EL_EXPANDABLE_WALL ||
12316              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12317              element == EL_EXPANDABLE_WALL_VERTICAL ||
12318              element == EL_EXPANDABLE_WALL_ANY ||
12319              element == EL_BD_EXPANDABLE_WALL)
12320       MauerAbleger(x, y);
12321     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12322              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12323              element == EL_EXPANDABLE_STEELWALL_ANY)
12324       MauerAblegerStahl(x, y);
12325     else if (element == EL_FLAMES)
12326       CheckForDragon(x, y);
12327     else if (element == EL_EXPLOSION)
12328       ; // drawing of correct explosion animation is handled separately
12329     else if (element == EL_ELEMENT_SNAPPING ||
12330              element == EL_DIAGONAL_SHRINKING ||
12331              element == EL_DIAGONAL_GROWING)
12332     {
12333       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12334
12335       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12336     }
12337     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12338       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12339
12340     if (IS_BELT_ACTIVE(element))
12341       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12342
12343     if (game.magic_wall_active)
12344     {
12345       int jx = local_player->jx, jy = local_player->jy;
12346
12347       // play the element sound at the position nearest to the player
12348       if ((element == EL_MAGIC_WALL_FULL ||
12349            element == EL_MAGIC_WALL_ACTIVE ||
12350            element == EL_MAGIC_WALL_EMPTYING ||
12351            element == EL_BD_MAGIC_WALL_FULL ||
12352            element == EL_BD_MAGIC_WALL_ACTIVE ||
12353            element == EL_BD_MAGIC_WALL_EMPTYING ||
12354            element == EL_DC_MAGIC_WALL_FULL ||
12355            element == EL_DC_MAGIC_WALL_ACTIVE ||
12356            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12357           ABS(x - jx) + ABS(y - jy) <
12358           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12359       {
12360         magic_wall_x = x;
12361         magic_wall_y = y;
12362       }
12363     }
12364   }
12365
12366 #if USE_NEW_AMOEBA_CODE
12367   // new experimental amoeba growth stuff
12368   if (!(FrameCounter % 8))
12369   {
12370     static unsigned int random = 1684108901;
12371
12372     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12373     {
12374       x = RND(lev_fieldx);
12375       y = RND(lev_fieldy);
12376       element = Tile[x][y];
12377
12378       if (!IS_PLAYER(x,y) &&
12379           (element == EL_EMPTY ||
12380            CAN_GROW_INTO(element) ||
12381            element == EL_QUICKSAND_EMPTY ||
12382            element == EL_QUICKSAND_FAST_EMPTY ||
12383            element == EL_ACID_SPLASH_LEFT ||
12384            element == EL_ACID_SPLASH_RIGHT))
12385       {
12386         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12387             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12388             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12389             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12390           Tile[x][y] = EL_AMOEBA_DROP;
12391       }
12392
12393       random = random * 129 + 1;
12394     }
12395   }
12396 #endif
12397
12398   game.explosions_delayed = FALSE;
12399
12400   SCAN_PLAYFIELD(x, y)
12401   {
12402     element = Tile[x][y];
12403
12404     if (ExplodeField[x][y])
12405       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12406     else if (element == EL_EXPLOSION)
12407       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12408
12409     ExplodeField[x][y] = EX_TYPE_NONE;
12410   }
12411
12412   game.explosions_delayed = TRUE;
12413
12414   if (game.magic_wall_active)
12415   {
12416     if (!(game.magic_wall_time_left % 4))
12417     {
12418       int element = Tile[magic_wall_x][magic_wall_y];
12419
12420       if (element == EL_BD_MAGIC_WALL_FULL ||
12421           element == EL_BD_MAGIC_WALL_ACTIVE ||
12422           element == EL_BD_MAGIC_WALL_EMPTYING)
12423         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12424       else if (element == EL_DC_MAGIC_WALL_FULL ||
12425                element == EL_DC_MAGIC_WALL_ACTIVE ||
12426                element == EL_DC_MAGIC_WALL_EMPTYING)
12427         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12428       else
12429         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12430     }
12431
12432     if (game.magic_wall_time_left > 0)
12433     {
12434       game.magic_wall_time_left--;
12435
12436       if (!game.magic_wall_time_left)
12437       {
12438         SCAN_PLAYFIELD(x, y)
12439         {
12440           element = Tile[x][y];
12441
12442           if (element == EL_MAGIC_WALL_ACTIVE ||
12443               element == EL_MAGIC_WALL_FULL)
12444           {
12445             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12446             TEST_DrawLevelField(x, y);
12447           }
12448           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12449                    element == EL_BD_MAGIC_WALL_FULL)
12450           {
12451             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12452             TEST_DrawLevelField(x, y);
12453           }
12454           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12455                    element == EL_DC_MAGIC_WALL_FULL)
12456           {
12457             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12458             TEST_DrawLevelField(x, y);
12459           }
12460         }
12461
12462         game.magic_wall_active = FALSE;
12463       }
12464     }
12465   }
12466
12467   if (game.light_time_left > 0)
12468   {
12469     game.light_time_left--;
12470
12471     if (game.light_time_left == 0)
12472       RedrawAllLightSwitchesAndInvisibleElements();
12473   }
12474
12475   if (game.timegate_time_left > 0)
12476   {
12477     game.timegate_time_left--;
12478
12479     if (game.timegate_time_left == 0)
12480       CloseAllOpenTimegates();
12481   }
12482
12483   if (game.lenses_time_left > 0)
12484   {
12485     game.lenses_time_left--;
12486
12487     if (game.lenses_time_left == 0)
12488       RedrawAllInvisibleElementsForLenses();
12489   }
12490
12491   if (game.magnify_time_left > 0)
12492   {
12493     game.magnify_time_left--;
12494
12495     if (game.magnify_time_left == 0)
12496       RedrawAllInvisibleElementsForMagnifier();
12497   }
12498
12499   for (i = 0; i < MAX_PLAYERS; i++)
12500   {
12501     struct PlayerInfo *player = &stored_player[i];
12502
12503     if (SHIELD_ON(player))
12504     {
12505       if (player->shield_deadly_time_left)
12506         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12507       else if (player->shield_normal_time_left)
12508         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12509     }
12510   }
12511
12512 #if USE_DELAYED_GFX_REDRAW
12513   SCAN_PLAYFIELD(x, y)
12514   {
12515     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12516     {
12517       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12518          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12519
12520       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12521         DrawLevelField(x, y);
12522
12523       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12524         DrawLevelFieldCrumbled(x, y);
12525
12526       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12527         DrawLevelFieldCrumbledNeighbours(x, y);
12528
12529       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12530         DrawTwinkleOnField(x, y);
12531     }
12532
12533     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12534   }
12535 #endif
12536
12537   DrawAllPlayers();
12538   PlayAllPlayersSound();
12539
12540   for (i = 0; i < MAX_PLAYERS; i++)
12541   {
12542     struct PlayerInfo *player = &stored_player[i];
12543
12544     if (player->show_envelope != 0 && (!player->active ||
12545                                        player->MovPos == 0))
12546     {
12547       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12548
12549       player->show_envelope = 0;
12550     }
12551   }
12552
12553   // use random number generator in every frame to make it less predictable
12554   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12555     RND(1);
12556
12557   mouse_action_last = mouse_action;
12558 }
12559
12560 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12561 {
12562   int min_x = x, min_y = y, max_x = x, max_y = y;
12563   int scr_fieldx = getScreenFieldSizeX();
12564   int scr_fieldy = getScreenFieldSizeY();
12565   int i;
12566
12567   for (i = 0; i < MAX_PLAYERS; i++)
12568   {
12569     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12570
12571     if (!stored_player[i].active || &stored_player[i] == player)
12572       continue;
12573
12574     min_x = MIN(min_x, jx);
12575     min_y = MIN(min_y, jy);
12576     max_x = MAX(max_x, jx);
12577     max_y = MAX(max_y, jy);
12578   }
12579
12580   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12581 }
12582
12583 static boolean AllPlayersInVisibleScreen(void)
12584 {
12585   int i;
12586
12587   for (i = 0; i < MAX_PLAYERS; i++)
12588   {
12589     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12590
12591     if (!stored_player[i].active)
12592       continue;
12593
12594     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12595       return FALSE;
12596   }
12597
12598   return TRUE;
12599 }
12600
12601 void ScrollLevel(int dx, int dy)
12602 {
12603   int scroll_offset = 2 * TILEX_VAR;
12604   int x, y;
12605
12606   BlitBitmap(drawto_field, drawto_field,
12607              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12608              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12609              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12610              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12611              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12612              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12613
12614   if (dx != 0)
12615   {
12616     x = (dx == 1 ? BX1 : BX2);
12617     for (y = BY1; y <= BY2; y++)
12618       DrawScreenField(x, y);
12619   }
12620
12621   if (dy != 0)
12622   {
12623     y = (dy == 1 ? BY1 : BY2);
12624     for (x = BX1; x <= BX2; x++)
12625       DrawScreenField(x, y);
12626   }
12627
12628   redraw_mask |= REDRAW_FIELD;
12629 }
12630
12631 static boolean canFallDown(struct PlayerInfo *player)
12632 {
12633   int jx = player->jx, jy = player->jy;
12634
12635   return (IN_LEV_FIELD(jx, jy + 1) &&
12636           (IS_FREE(jx, jy + 1) ||
12637            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12638           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12639           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12640 }
12641
12642 static boolean canPassField(int x, int y, int move_dir)
12643 {
12644   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12645   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12646   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12647   int nextx = x + dx;
12648   int nexty = y + dy;
12649   int element = Tile[x][y];
12650
12651   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12652           !CAN_MOVE(element) &&
12653           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12654           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12655           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12656 }
12657
12658 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12659 {
12660   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12661   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12662   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12663   int newx = x + dx;
12664   int newy = y + dy;
12665
12666   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12667           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12668           (IS_DIGGABLE(Tile[newx][newy]) ||
12669            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12670            canPassField(newx, newy, move_dir)));
12671 }
12672
12673 static void CheckGravityMovement(struct PlayerInfo *player)
12674 {
12675   if (player->gravity && !player->programmed_action)
12676   {
12677     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12678     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12679     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12680     int jx = player->jx, jy = player->jy;
12681     boolean player_is_moving_to_valid_field =
12682       (!player_is_snapping &&
12683        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12684         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12685     boolean player_can_fall_down = canFallDown(player);
12686
12687     if (player_can_fall_down &&
12688         !player_is_moving_to_valid_field)
12689       player->programmed_action = MV_DOWN;
12690   }
12691 }
12692
12693 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12694 {
12695   return CheckGravityMovement(player);
12696
12697   if (player->gravity && !player->programmed_action)
12698   {
12699     int jx = player->jx, jy = player->jy;
12700     boolean field_under_player_is_free =
12701       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12702     boolean player_is_standing_on_valid_field =
12703       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12704        (IS_WALKABLE(Tile[jx][jy]) &&
12705         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12706
12707     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12708       player->programmed_action = MV_DOWN;
12709   }
12710 }
12711
12712 /*
12713   MovePlayerOneStep()
12714   -----------------------------------------------------------------------------
12715   dx, dy:               direction (non-diagonal) to try to move the player to
12716   real_dx, real_dy:     direction as read from input device (can be diagonal)
12717 */
12718
12719 boolean MovePlayerOneStep(struct PlayerInfo *player,
12720                           int dx, int dy, int real_dx, int real_dy)
12721 {
12722   int jx = player->jx, jy = player->jy;
12723   int new_jx = jx + dx, new_jy = jy + dy;
12724   int can_move;
12725   boolean player_can_move = !player->cannot_move;
12726
12727   if (!player->active || (!dx && !dy))
12728     return MP_NO_ACTION;
12729
12730   player->MovDir = (dx < 0 ? MV_LEFT :
12731                     dx > 0 ? MV_RIGHT :
12732                     dy < 0 ? MV_UP :
12733                     dy > 0 ? MV_DOWN :  MV_NONE);
12734
12735   if (!IN_LEV_FIELD(new_jx, new_jy))
12736     return MP_NO_ACTION;
12737
12738   if (!player_can_move)
12739   {
12740     if (player->MovPos == 0)
12741     {
12742       player->is_moving = FALSE;
12743       player->is_digging = FALSE;
12744       player->is_collecting = FALSE;
12745       player->is_snapping = FALSE;
12746       player->is_pushing = FALSE;
12747     }
12748   }
12749
12750   if (!network.enabled && game.centered_player_nr == -1 &&
12751       !AllPlayersInSight(player, new_jx, new_jy))
12752     return MP_NO_ACTION;
12753
12754   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12755   if (can_move != MP_MOVING)
12756     return can_move;
12757
12758   // check if DigField() has caused relocation of the player
12759   if (player->jx != jx || player->jy != jy)
12760     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12761
12762   StorePlayer[jx][jy] = 0;
12763   player->last_jx = jx;
12764   player->last_jy = jy;
12765   player->jx = new_jx;
12766   player->jy = new_jy;
12767   StorePlayer[new_jx][new_jy] = player->element_nr;
12768
12769   if (player->move_delay_value_next != -1)
12770   {
12771     player->move_delay_value = player->move_delay_value_next;
12772     player->move_delay_value_next = -1;
12773   }
12774
12775   player->MovPos =
12776     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12777
12778   player->step_counter++;
12779
12780   PlayerVisit[jx][jy] = FrameCounter;
12781
12782   player->is_moving = TRUE;
12783
12784 #if 1
12785   // should better be called in MovePlayer(), but this breaks some tapes
12786   ScrollPlayer(player, SCROLL_INIT);
12787 #endif
12788
12789   return MP_MOVING;
12790 }
12791
12792 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12793 {
12794   int jx = player->jx, jy = player->jy;
12795   int old_jx = jx, old_jy = jy;
12796   int moved = MP_NO_ACTION;
12797
12798   if (!player->active)
12799     return FALSE;
12800
12801   if (!dx && !dy)
12802   {
12803     if (player->MovPos == 0)
12804     {
12805       player->is_moving = FALSE;
12806       player->is_digging = FALSE;
12807       player->is_collecting = FALSE;
12808       player->is_snapping = FALSE;
12809       player->is_pushing = FALSE;
12810     }
12811
12812     return FALSE;
12813   }
12814
12815   if (player->move_delay > 0)
12816     return FALSE;
12817
12818   player->move_delay = -1;              // set to "uninitialized" value
12819
12820   // store if player is automatically moved to next field
12821   player->is_auto_moving = (player->programmed_action != MV_NONE);
12822
12823   // remove the last programmed player action
12824   player->programmed_action = 0;
12825
12826   if (player->MovPos)
12827   {
12828     // should only happen if pre-1.2 tape recordings are played
12829     // this is only for backward compatibility
12830
12831     int original_move_delay_value = player->move_delay_value;
12832
12833 #if DEBUG
12834     Debug("game:playing:MovePlayer",
12835           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12836           tape.counter);
12837 #endif
12838
12839     // scroll remaining steps with finest movement resolution
12840     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12841
12842     while (player->MovPos)
12843     {
12844       ScrollPlayer(player, SCROLL_GO_ON);
12845       ScrollScreen(NULL, SCROLL_GO_ON);
12846
12847       AdvanceFrameAndPlayerCounters(player->index_nr);
12848
12849       DrawAllPlayers();
12850       BackToFront_WithFrameDelay(0);
12851     }
12852
12853     player->move_delay_value = original_move_delay_value;
12854   }
12855
12856   player->is_active = FALSE;
12857
12858   if (player->last_move_dir & MV_HORIZONTAL)
12859   {
12860     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12861       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12862   }
12863   else
12864   {
12865     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12866       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12867   }
12868
12869   if (!moved && !player->is_active)
12870   {
12871     player->is_moving = FALSE;
12872     player->is_digging = FALSE;
12873     player->is_collecting = FALSE;
12874     player->is_snapping = FALSE;
12875     player->is_pushing = FALSE;
12876   }
12877
12878   jx = player->jx;
12879   jy = player->jy;
12880
12881   if (moved & MP_MOVING && !ScreenMovPos &&
12882       (player->index_nr == game.centered_player_nr ||
12883        game.centered_player_nr == -1))
12884   {
12885     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12886
12887     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12888     {
12889       // actual player has left the screen -- scroll in that direction
12890       if (jx != old_jx)         // player has moved horizontally
12891         scroll_x += (jx - old_jx);
12892       else                      // player has moved vertically
12893         scroll_y += (jy - old_jy);
12894     }
12895     else
12896     {
12897       int offset_raw = game.scroll_delay_value;
12898
12899       if (jx != old_jx)         // player has moved horizontally
12900       {
12901         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12902         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12903         int new_scroll_x = jx - MIDPOSX + offset_x;
12904
12905         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12906             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12907           scroll_x = new_scroll_x;
12908
12909         // don't scroll over playfield boundaries
12910         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12911
12912         // don't scroll more than one field at a time
12913         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12914
12915         // don't scroll against the player's moving direction
12916         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12917             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12918           scroll_x = old_scroll_x;
12919       }
12920       else                      // player has moved vertically
12921       {
12922         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12923         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12924         int new_scroll_y = jy - MIDPOSY + offset_y;
12925
12926         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12927             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12928           scroll_y = new_scroll_y;
12929
12930         // don't scroll over playfield boundaries
12931         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12932
12933         // don't scroll more than one field at a time
12934         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12935
12936         // don't scroll against the player's moving direction
12937         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12938             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12939           scroll_y = old_scroll_y;
12940       }
12941     }
12942
12943     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12944     {
12945       if (!network.enabled && game.centered_player_nr == -1 &&
12946           !AllPlayersInVisibleScreen())
12947       {
12948         scroll_x = old_scroll_x;
12949         scroll_y = old_scroll_y;
12950       }
12951       else
12952       {
12953         ScrollScreen(player, SCROLL_INIT);
12954         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12955       }
12956     }
12957   }
12958
12959   player->StepFrame = 0;
12960
12961   if (moved & MP_MOVING)
12962   {
12963     if (old_jx != jx && old_jy == jy)
12964       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12965     else if (old_jx == jx && old_jy != jy)
12966       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12967
12968     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12969
12970     player->last_move_dir = player->MovDir;
12971     player->is_moving = TRUE;
12972     player->is_snapping = FALSE;
12973     player->is_switching = FALSE;
12974     player->is_dropping = FALSE;
12975     player->is_dropping_pressed = FALSE;
12976     player->drop_pressed_delay = 0;
12977
12978 #if 0
12979     // should better be called here than above, but this breaks some tapes
12980     ScrollPlayer(player, SCROLL_INIT);
12981 #endif
12982   }
12983   else
12984   {
12985     CheckGravityMovementWhenNotMoving(player);
12986
12987     player->is_moving = FALSE;
12988
12989     /* at this point, the player is allowed to move, but cannot move right now
12990        (e.g. because of something blocking the way) -- ensure that the player
12991        is also allowed to move in the next frame (in old versions before 3.1.1,
12992        the player was forced to wait again for eight frames before next try) */
12993
12994     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12995       player->move_delay = 0;   // allow direct movement in the next frame
12996   }
12997
12998   if (player->move_delay == -1)         // not yet initialized by DigField()
12999     player->move_delay = player->move_delay_value;
13000
13001   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13002   {
13003     TestIfPlayerTouchesBadThing(jx, jy);
13004     TestIfPlayerTouchesCustomElement(jx, jy);
13005   }
13006
13007   if (!player->active)
13008     RemovePlayer(player);
13009
13010   return moved;
13011 }
13012
13013 void ScrollPlayer(struct PlayerInfo *player, int mode)
13014 {
13015   int jx = player->jx, jy = player->jy;
13016   int last_jx = player->last_jx, last_jy = player->last_jy;
13017   int move_stepsize = TILEX / player->move_delay_value;
13018
13019   if (!player->active)
13020     return;
13021
13022   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13023     return;
13024
13025   if (mode == SCROLL_INIT)
13026   {
13027     player->actual_frame_counter = FrameCounter;
13028     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13029
13030     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13031         Tile[last_jx][last_jy] == EL_EMPTY)
13032     {
13033       int last_field_block_delay = 0;   // start with no blocking at all
13034       int block_delay_adjustment = player->block_delay_adjustment;
13035
13036       // if player blocks last field, add delay for exactly one move
13037       if (player->block_last_field)
13038       {
13039         last_field_block_delay += player->move_delay_value;
13040
13041         // when blocking enabled, prevent moving up despite gravity
13042         if (player->gravity && player->MovDir == MV_UP)
13043           block_delay_adjustment = -1;
13044       }
13045
13046       // add block delay adjustment (also possible when not blocking)
13047       last_field_block_delay += block_delay_adjustment;
13048
13049       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13050       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13051     }
13052
13053     if (player->MovPos != 0)    // player has not yet reached destination
13054       return;
13055   }
13056   else if (!FrameReached(&player->actual_frame_counter, 1))
13057     return;
13058
13059   if (player->MovPos != 0)
13060   {
13061     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13062     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13063
13064     // before DrawPlayer() to draw correct player graphic for this case
13065     if (player->MovPos == 0)
13066       CheckGravityMovement(player);
13067   }
13068
13069   if (player->MovPos == 0)      // player reached destination field
13070   {
13071     if (player->move_delay_reset_counter > 0)
13072     {
13073       player->move_delay_reset_counter--;
13074
13075       if (player->move_delay_reset_counter == 0)
13076       {
13077         // continue with normal speed after quickly moving through gate
13078         HALVE_PLAYER_SPEED(player);
13079
13080         // be able to make the next move without delay
13081         player->move_delay = 0;
13082       }
13083     }
13084
13085     player->last_jx = jx;
13086     player->last_jy = jy;
13087
13088     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13089         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13090         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13091         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13092         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13093         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13094         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13095         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13096     {
13097       ExitPlayer(player);
13098
13099       if (game.players_still_needed == 0 &&
13100           (game.friends_still_needed == 0 ||
13101            IS_SP_ELEMENT(Tile[jx][jy])))
13102         LevelSolved();
13103     }
13104
13105     // this breaks one level: "machine", level 000
13106     {
13107       int move_direction = player->MovDir;
13108       int enter_side = MV_DIR_OPPOSITE(move_direction);
13109       int leave_side = move_direction;
13110       int old_jx = last_jx;
13111       int old_jy = last_jy;
13112       int old_element = Tile[old_jx][old_jy];
13113       int new_element = Tile[jx][jy];
13114
13115       if (IS_CUSTOM_ELEMENT(old_element))
13116         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13117                                    CE_LEFT_BY_PLAYER,
13118                                    player->index_bit, leave_side);
13119
13120       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13121                                           CE_PLAYER_LEAVES_X,
13122                                           player->index_bit, leave_side);
13123
13124       if (IS_CUSTOM_ELEMENT(new_element))
13125         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13126                                    player->index_bit, enter_side);
13127
13128       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13129                                           CE_PLAYER_ENTERS_X,
13130                                           player->index_bit, enter_side);
13131
13132       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13133                                         CE_MOVE_OF_X, move_direction);
13134     }
13135
13136     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13137     {
13138       TestIfPlayerTouchesBadThing(jx, jy);
13139       TestIfPlayerTouchesCustomElement(jx, jy);
13140
13141       /* needed because pushed element has not yet reached its destination,
13142          so it would trigger a change event at its previous field location */
13143       if (!player->is_pushing)
13144         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13145
13146       if (level.finish_dig_collect &&
13147           (player->is_digging || player->is_collecting))
13148       {
13149         int last_element = player->last_removed_element;
13150         int move_direction = player->MovDir;
13151         int enter_side = MV_DIR_OPPOSITE(move_direction);
13152         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13153                             CE_PLAYER_COLLECTS_X);
13154
13155         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13156                                             player->index_bit, enter_side);
13157
13158         player->last_removed_element = EL_UNDEFINED;
13159       }
13160
13161       if (!player->active)
13162         RemovePlayer(player);
13163     }
13164
13165     if (level.use_step_counter)
13166     {
13167       int i;
13168
13169       TimePlayed++;
13170
13171       if (TimeLeft > 0)
13172       {
13173         TimeLeft--;
13174
13175         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13176           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13177
13178         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13179
13180         DisplayGameControlValues();
13181
13182         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13183           for (i = 0; i < MAX_PLAYERS; i++)
13184             KillPlayer(&stored_player[i]);
13185       }
13186       else if (game.no_time_limit && !game.all_players_gone)
13187       {
13188         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13189
13190         DisplayGameControlValues();
13191       }
13192     }
13193
13194     if (tape.single_step && tape.recording && !tape.pausing &&
13195         !player->programmed_action)
13196       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13197
13198     if (!player->programmed_action)
13199       CheckSaveEngineSnapshot(player);
13200   }
13201 }
13202
13203 void ScrollScreen(struct PlayerInfo *player, int mode)
13204 {
13205   static unsigned int screen_frame_counter = 0;
13206
13207   if (mode == SCROLL_INIT)
13208   {
13209     // set scrolling step size according to actual player's moving speed
13210     ScrollStepSize = TILEX / player->move_delay_value;
13211
13212     screen_frame_counter = FrameCounter;
13213     ScreenMovDir = player->MovDir;
13214     ScreenMovPos = player->MovPos;
13215     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13216     return;
13217   }
13218   else if (!FrameReached(&screen_frame_counter, 1))
13219     return;
13220
13221   if (ScreenMovPos)
13222   {
13223     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13224     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13225     redraw_mask |= REDRAW_FIELD;
13226   }
13227   else
13228     ScreenMovDir = MV_NONE;
13229 }
13230
13231 void TestIfPlayerTouchesCustomElement(int x, int y)
13232 {
13233   static int xy[4][2] =
13234   {
13235     { 0, -1 },
13236     { -1, 0 },
13237     { +1, 0 },
13238     { 0, +1 }
13239   };
13240   static int trigger_sides[4][2] =
13241   {
13242     // center side       border side
13243     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13244     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13245     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13246     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13247   };
13248   static int touch_dir[4] =
13249   {
13250     MV_LEFT | MV_RIGHT,
13251     MV_UP   | MV_DOWN,
13252     MV_UP   | MV_DOWN,
13253     MV_LEFT | MV_RIGHT
13254   };
13255   int center_element = Tile[x][y];      // should always be non-moving!
13256   int i;
13257
13258   for (i = 0; i < NUM_DIRECTIONS; i++)
13259   {
13260     int xx = x + xy[i][0];
13261     int yy = y + xy[i][1];
13262     int center_side = trigger_sides[i][0];
13263     int border_side = trigger_sides[i][1];
13264     int border_element;
13265
13266     if (!IN_LEV_FIELD(xx, yy))
13267       continue;
13268
13269     if (IS_PLAYER(x, y))                // player found at center element
13270     {
13271       struct PlayerInfo *player = PLAYERINFO(x, y);
13272
13273       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13274         border_element = Tile[xx][yy];          // may be moving!
13275       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13276         border_element = Tile[xx][yy];
13277       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13278         border_element = MovingOrBlocked2Element(xx, yy);
13279       else
13280         continue;               // center and border element do not touch
13281
13282       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13283                                  player->index_bit, border_side);
13284       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13285                                           CE_PLAYER_TOUCHES_X,
13286                                           player->index_bit, border_side);
13287
13288       {
13289         /* use player element that is initially defined in the level playfield,
13290            not the player element that corresponds to the runtime player number
13291            (example: a level that contains EL_PLAYER_3 as the only player would
13292            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13293         int player_element = PLAYERINFO(x, y)->initial_element;
13294
13295         CheckElementChangeBySide(xx, yy, border_element, player_element,
13296                                  CE_TOUCHING_X, border_side);
13297       }
13298     }
13299     else if (IS_PLAYER(xx, yy))         // player found at border element
13300     {
13301       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13302
13303       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13304       {
13305         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13306           continue;             // center and border element do not touch
13307       }
13308
13309       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13310                                  player->index_bit, center_side);
13311       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13312                                           CE_PLAYER_TOUCHES_X,
13313                                           player->index_bit, center_side);
13314
13315       {
13316         /* use player element that is initially defined in the level playfield,
13317            not the player element that corresponds to the runtime player number
13318            (example: a level that contains EL_PLAYER_3 as the only player would
13319            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13320         int player_element = PLAYERINFO(xx, yy)->initial_element;
13321
13322         CheckElementChangeBySide(x, y, center_element, player_element,
13323                                  CE_TOUCHING_X, center_side);
13324       }
13325
13326       break;
13327     }
13328   }
13329 }
13330
13331 void TestIfElementTouchesCustomElement(int x, int y)
13332 {
13333   static int xy[4][2] =
13334   {
13335     { 0, -1 },
13336     { -1, 0 },
13337     { +1, 0 },
13338     { 0, +1 }
13339   };
13340   static int trigger_sides[4][2] =
13341   {
13342     // center side      border side
13343     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13344     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13345     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13346     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13347   };
13348   static int touch_dir[4] =
13349   {
13350     MV_LEFT | MV_RIGHT,
13351     MV_UP   | MV_DOWN,
13352     MV_UP   | MV_DOWN,
13353     MV_LEFT | MV_RIGHT
13354   };
13355   boolean change_center_element = FALSE;
13356   int center_element = Tile[x][y];      // should always be non-moving!
13357   int border_element_old[NUM_DIRECTIONS];
13358   int i;
13359
13360   for (i = 0; i < NUM_DIRECTIONS; i++)
13361   {
13362     int xx = x + xy[i][0];
13363     int yy = y + xy[i][1];
13364     int border_element;
13365
13366     border_element_old[i] = -1;
13367
13368     if (!IN_LEV_FIELD(xx, yy))
13369       continue;
13370
13371     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13372       border_element = Tile[xx][yy];    // may be moving!
13373     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13374       border_element = Tile[xx][yy];
13375     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13376       border_element = MovingOrBlocked2Element(xx, yy);
13377     else
13378       continue;                 // center and border element do not touch
13379
13380     border_element_old[i] = border_element;
13381   }
13382
13383   for (i = 0; i < NUM_DIRECTIONS; i++)
13384   {
13385     int xx = x + xy[i][0];
13386     int yy = y + xy[i][1];
13387     int center_side = trigger_sides[i][0];
13388     int border_element = border_element_old[i];
13389
13390     if (border_element == -1)
13391       continue;
13392
13393     // check for change of border element
13394     CheckElementChangeBySide(xx, yy, border_element, center_element,
13395                              CE_TOUCHING_X, center_side);
13396
13397     // (center element cannot be player, so we dont have to check this here)
13398   }
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_side = trigger_sides[i][1];
13405     int border_element = border_element_old[i];
13406
13407     if (border_element == -1)
13408       continue;
13409
13410     // check for change of center element (but change it only once)
13411     if (!change_center_element)
13412       change_center_element =
13413         CheckElementChangeBySide(x, y, center_element, border_element,
13414                                  CE_TOUCHING_X, border_side);
13415
13416     if (IS_PLAYER(xx, yy))
13417     {
13418       /* use player element that is initially defined in the level playfield,
13419          not the player element that corresponds to the runtime player number
13420          (example: a level that contains EL_PLAYER_3 as the only player would
13421          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13422       int player_element = PLAYERINFO(xx, yy)->initial_element;
13423
13424       CheckElementChangeBySide(x, y, center_element, player_element,
13425                                CE_TOUCHING_X, border_side);
13426     }
13427   }
13428 }
13429
13430 void TestIfElementHitsCustomElement(int x, int y, int direction)
13431 {
13432   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13433   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13434   int hitx = x + dx, hity = y + dy;
13435   int hitting_element = Tile[x][y];
13436   int touched_element;
13437
13438   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13439     return;
13440
13441   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13442                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13443
13444   if (IN_LEV_FIELD(hitx, hity))
13445   {
13446     int opposite_direction = MV_DIR_OPPOSITE(direction);
13447     int hitting_side = direction;
13448     int touched_side = opposite_direction;
13449     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13450                           MovDir[hitx][hity] != direction ||
13451                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13452
13453     object_hit = TRUE;
13454
13455     if (object_hit)
13456     {
13457       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13458                                CE_HITTING_X, touched_side);
13459
13460       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13461                                CE_HIT_BY_X, hitting_side);
13462
13463       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13464                                CE_HIT_BY_SOMETHING, opposite_direction);
13465
13466       if (IS_PLAYER(hitx, hity))
13467       {
13468         /* use player element that is initially defined in the level playfield,
13469            not the player element that corresponds to the runtime player number
13470            (example: a level that contains EL_PLAYER_3 as the only player would
13471            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13472         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13473
13474         CheckElementChangeBySide(x, y, hitting_element, player_element,
13475                                  CE_HITTING_X, touched_side);
13476       }
13477     }
13478   }
13479
13480   // "hitting something" is also true when hitting the playfield border
13481   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13482                            CE_HITTING_SOMETHING, direction);
13483 }
13484
13485 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13486 {
13487   int i, kill_x = -1, kill_y = -1;
13488
13489   int bad_element = -1;
13490   static int test_xy[4][2] =
13491   {
13492     { 0, -1 },
13493     { -1, 0 },
13494     { +1, 0 },
13495     { 0, +1 }
13496   };
13497   static int test_dir[4] =
13498   {
13499     MV_UP,
13500     MV_LEFT,
13501     MV_RIGHT,
13502     MV_DOWN
13503   };
13504
13505   for (i = 0; i < NUM_DIRECTIONS; i++)
13506   {
13507     int test_x, test_y, test_move_dir, test_element;
13508
13509     test_x = good_x + test_xy[i][0];
13510     test_y = good_y + test_xy[i][1];
13511
13512     if (!IN_LEV_FIELD(test_x, test_y))
13513       continue;
13514
13515     test_move_dir =
13516       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13517
13518     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13519
13520     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13521        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13522     */
13523     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13524         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13525     {
13526       kill_x = test_x;
13527       kill_y = test_y;
13528       bad_element = test_element;
13529
13530       break;
13531     }
13532   }
13533
13534   if (kill_x != -1 || kill_y != -1)
13535   {
13536     if (IS_PLAYER(good_x, good_y))
13537     {
13538       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13539
13540       if (player->shield_deadly_time_left > 0 &&
13541           !IS_INDESTRUCTIBLE(bad_element))
13542         Bang(kill_x, kill_y);
13543       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13544         KillPlayer(player);
13545     }
13546     else
13547       Bang(good_x, good_y);
13548   }
13549 }
13550
13551 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13552 {
13553   int i, kill_x = -1, kill_y = -1;
13554   int bad_element = Tile[bad_x][bad_y];
13555   static int test_xy[4][2] =
13556   {
13557     { 0, -1 },
13558     { -1, 0 },
13559     { +1, 0 },
13560     { 0, +1 }
13561   };
13562   static int touch_dir[4] =
13563   {
13564     MV_LEFT | MV_RIGHT,
13565     MV_UP   | MV_DOWN,
13566     MV_UP   | MV_DOWN,
13567     MV_LEFT | MV_RIGHT
13568   };
13569   static int test_dir[4] =
13570   {
13571     MV_UP,
13572     MV_LEFT,
13573     MV_RIGHT,
13574     MV_DOWN
13575   };
13576
13577   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13578     return;
13579
13580   for (i = 0; i < NUM_DIRECTIONS; i++)
13581   {
13582     int test_x, test_y, test_move_dir, test_element;
13583
13584     test_x = bad_x + test_xy[i][0];
13585     test_y = bad_y + test_xy[i][1];
13586
13587     if (!IN_LEV_FIELD(test_x, test_y))
13588       continue;
13589
13590     test_move_dir =
13591       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13592
13593     test_element = Tile[test_x][test_y];
13594
13595     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13596        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13597     */
13598     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13599         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13600     {
13601       // good thing is player or penguin that does not move away
13602       if (IS_PLAYER(test_x, test_y))
13603       {
13604         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13605
13606         if (bad_element == EL_ROBOT && player->is_moving)
13607           continue;     // robot does not kill player if he is moving
13608
13609         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13610         {
13611           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13612             continue;           // center and border element do not touch
13613         }
13614
13615         kill_x = test_x;
13616         kill_y = test_y;
13617
13618         break;
13619       }
13620       else if (test_element == EL_PENGUIN)
13621       {
13622         kill_x = test_x;
13623         kill_y = test_y;
13624
13625         break;
13626       }
13627     }
13628   }
13629
13630   if (kill_x != -1 || kill_y != -1)
13631   {
13632     if (IS_PLAYER(kill_x, kill_y))
13633     {
13634       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13635
13636       if (player->shield_deadly_time_left > 0 &&
13637           !IS_INDESTRUCTIBLE(bad_element))
13638         Bang(bad_x, bad_y);
13639       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13640         KillPlayer(player);
13641     }
13642     else
13643       Bang(kill_x, kill_y);
13644   }
13645 }
13646
13647 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13648 {
13649   int bad_element = Tile[bad_x][bad_y];
13650   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13651   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13652   int test_x = bad_x + dx, test_y = bad_y + dy;
13653   int test_move_dir, test_element;
13654   int kill_x = -1, kill_y = -1;
13655
13656   if (!IN_LEV_FIELD(test_x, test_y))
13657     return;
13658
13659   test_move_dir =
13660     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13661
13662   test_element = Tile[test_x][test_y];
13663
13664   if (test_move_dir != bad_move_dir)
13665   {
13666     // good thing can be player or penguin that does not move away
13667     if (IS_PLAYER(test_x, test_y))
13668     {
13669       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13670
13671       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13672          player as being hit when he is moving towards the bad thing, because
13673          the "get hit by" condition would be lost after the player stops) */
13674       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13675         return;         // player moves away from bad thing
13676
13677       kill_x = test_x;
13678       kill_y = test_y;
13679     }
13680     else if (test_element == EL_PENGUIN)
13681     {
13682       kill_x = test_x;
13683       kill_y = test_y;
13684     }
13685   }
13686
13687   if (kill_x != -1 || kill_y != -1)
13688   {
13689     if (IS_PLAYER(kill_x, kill_y))
13690     {
13691       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13692
13693       if (player->shield_deadly_time_left > 0 &&
13694           !IS_INDESTRUCTIBLE(bad_element))
13695         Bang(bad_x, bad_y);
13696       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13697         KillPlayer(player);
13698     }
13699     else
13700       Bang(kill_x, kill_y);
13701   }
13702 }
13703
13704 void TestIfPlayerTouchesBadThing(int x, int y)
13705 {
13706   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13707 }
13708
13709 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13710 {
13711   TestIfGoodThingHitsBadThing(x, y, move_dir);
13712 }
13713
13714 void TestIfBadThingTouchesPlayer(int x, int y)
13715 {
13716   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13717 }
13718
13719 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13720 {
13721   TestIfBadThingHitsGoodThing(x, y, move_dir);
13722 }
13723
13724 void TestIfFriendTouchesBadThing(int x, int y)
13725 {
13726   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13727 }
13728
13729 void TestIfBadThingTouchesFriend(int x, int y)
13730 {
13731   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13732 }
13733
13734 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13735 {
13736   int i, kill_x = bad_x, kill_y = bad_y;
13737   static int xy[4][2] =
13738   {
13739     { 0, -1 },
13740     { -1, 0 },
13741     { +1, 0 },
13742     { 0, +1 }
13743   };
13744
13745   for (i = 0; i < NUM_DIRECTIONS; i++)
13746   {
13747     int x, y, element;
13748
13749     x = bad_x + xy[i][0];
13750     y = bad_y + xy[i][1];
13751     if (!IN_LEV_FIELD(x, y))
13752       continue;
13753
13754     element = Tile[x][y];
13755     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13756         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13757     {
13758       kill_x = x;
13759       kill_y = y;
13760       break;
13761     }
13762   }
13763
13764   if (kill_x != bad_x || kill_y != bad_y)
13765     Bang(bad_x, bad_y);
13766 }
13767
13768 void KillPlayer(struct PlayerInfo *player)
13769 {
13770   int jx = player->jx, jy = player->jy;
13771
13772   if (!player->active)
13773     return;
13774
13775 #if 0
13776   Debug("game:playing:KillPlayer",
13777         "0: killed == %d, active == %d, reanimated == %d",
13778         player->killed, player->active, player->reanimated);
13779 #endif
13780
13781   /* the following code was introduced to prevent an infinite loop when calling
13782      -> Bang()
13783      -> CheckTriggeredElementChangeExt()
13784      -> ExecuteCustomElementAction()
13785      -> KillPlayer()
13786      -> (infinitely repeating the above sequence of function calls)
13787      which occurs when killing the player while having a CE with the setting
13788      "kill player X when explosion of <player X>"; the solution using a new
13789      field "player->killed" was chosen for backwards compatibility, although
13790      clever use of the fields "player->active" etc. would probably also work */
13791 #if 1
13792   if (player->killed)
13793     return;
13794 #endif
13795
13796   player->killed = TRUE;
13797
13798   // remove accessible field at the player's position
13799   Tile[jx][jy] = EL_EMPTY;
13800
13801   // deactivate shield (else Bang()/Explode() would not work right)
13802   player->shield_normal_time_left = 0;
13803   player->shield_deadly_time_left = 0;
13804
13805 #if 0
13806   Debug("game:playing:KillPlayer",
13807         "1: killed == %d, active == %d, reanimated == %d",
13808         player->killed, player->active, player->reanimated);
13809 #endif
13810
13811   Bang(jx, jy);
13812
13813 #if 0
13814   Debug("game:playing:KillPlayer",
13815         "2: killed == %d, active == %d, reanimated == %d",
13816         player->killed, player->active, player->reanimated);
13817 #endif
13818
13819   if (player->reanimated)       // killed player may have been reanimated
13820     player->killed = player->reanimated = FALSE;
13821   else
13822     BuryPlayer(player);
13823 }
13824
13825 static void KillPlayerUnlessEnemyProtected(int x, int y)
13826 {
13827   if (!PLAYER_ENEMY_PROTECTED(x, y))
13828     KillPlayer(PLAYERINFO(x, y));
13829 }
13830
13831 static void KillPlayerUnlessExplosionProtected(int x, int y)
13832 {
13833   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13834     KillPlayer(PLAYERINFO(x, y));
13835 }
13836
13837 void BuryPlayer(struct PlayerInfo *player)
13838 {
13839   int jx = player->jx, jy = player->jy;
13840
13841   if (!player->active)
13842     return;
13843
13844   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13845   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13846
13847   RemovePlayer(player);
13848
13849   player->buried = TRUE;
13850
13851   if (game.all_players_gone)
13852     game.GameOver = TRUE;
13853 }
13854
13855 void RemovePlayer(struct PlayerInfo *player)
13856 {
13857   int jx = player->jx, jy = player->jy;
13858   int i, found = FALSE;
13859
13860   player->present = FALSE;
13861   player->active = FALSE;
13862
13863   // required for some CE actions (even if the player is not active anymore)
13864   player->MovPos = 0;
13865
13866   if (!ExplodeField[jx][jy])
13867     StorePlayer[jx][jy] = 0;
13868
13869   if (player->is_moving)
13870     TEST_DrawLevelField(player->last_jx, player->last_jy);
13871
13872   for (i = 0; i < MAX_PLAYERS; i++)
13873     if (stored_player[i].active)
13874       found = TRUE;
13875
13876   if (!found)
13877   {
13878     game.all_players_gone = TRUE;
13879     game.GameOver = TRUE;
13880   }
13881
13882   game.exit_x = game.robot_wheel_x = jx;
13883   game.exit_y = game.robot_wheel_y = jy;
13884 }
13885
13886 void ExitPlayer(struct PlayerInfo *player)
13887 {
13888   DrawPlayer(player);   // needed here only to cleanup last field
13889   RemovePlayer(player);
13890
13891   if (game.players_still_needed > 0)
13892     game.players_still_needed--;
13893 }
13894
13895 static void SetFieldForSnapping(int x, int y, int element, int direction,
13896                                 int player_index_bit)
13897 {
13898   struct ElementInfo *ei = &element_info[element];
13899   int direction_bit = MV_DIR_TO_BIT(direction);
13900   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13901   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13902                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13903
13904   Tile[x][y] = EL_ELEMENT_SNAPPING;
13905   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13906   MovDir[x][y] = direction;
13907   Store[x][y] = element;
13908   Store2[x][y] = player_index_bit;
13909
13910   ResetGfxAnimation(x, y);
13911
13912   GfxElement[x][y] = element;
13913   GfxAction[x][y] = action;
13914   GfxDir[x][y] = direction;
13915   GfxFrame[x][y] = -1;
13916 }
13917
13918 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13919                                    int player_index_bit)
13920 {
13921   TestIfElementTouchesCustomElement(x, y);      // for empty space
13922
13923   if (level.finish_dig_collect)
13924   {
13925     int dig_side = MV_DIR_OPPOSITE(direction);
13926
13927     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13928                                         player_index_bit, dig_side);
13929   }
13930 }
13931
13932 /*
13933   =============================================================================
13934   checkDiagonalPushing()
13935   -----------------------------------------------------------------------------
13936   check if diagonal input device direction results in pushing of object
13937   (by checking if the alternative direction is walkable, diggable, ...)
13938   =============================================================================
13939 */
13940
13941 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13942                                     int x, int y, int real_dx, int real_dy)
13943 {
13944   int jx, jy, dx, dy, xx, yy;
13945
13946   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13947     return TRUE;
13948
13949   // diagonal direction: check alternative direction
13950   jx = player->jx;
13951   jy = player->jy;
13952   dx = x - jx;
13953   dy = y - jy;
13954   xx = jx + (dx == 0 ? real_dx : 0);
13955   yy = jy + (dy == 0 ? real_dy : 0);
13956
13957   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13958 }
13959
13960 /*
13961   =============================================================================
13962   DigField()
13963   -----------------------------------------------------------------------------
13964   x, y:                 field next to player (non-diagonal) to try to dig to
13965   real_dx, real_dy:     direction as read from input device (can be diagonal)
13966   =============================================================================
13967 */
13968
13969 static int DigField(struct PlayerInfo *player,
13970                     int oldx, int oldy, int x, int y,
13971                     int real_dx, int real_dy, int mode)
13972 {
13973   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13974   boolean player_was_pushing = player->is_pushing;
13975   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13976   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13977   int jx = oldx, jy = oldy;
13978   int dx = x - jx, dy = y - jy;
13979   int nextx = x + dx, nexty = y + dy;
13980   int move_direction = (dx == -1 ? MV_LEFT  :
13981                         dx == +1 ? MV_RIGHT :
13982                         dy == -1 ? MV_UP    :
13983                         dy == +1 ? MV_DOWN  : MV_NONE);
13984   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13985   int dig_side = MV_DIR_OPPOSITE(move_direction);
13986   int old_element = Tile[jx][jy];
13987   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13988   int collect_count;
13989
13990   if (is_player)                // function can also be called by EL_PENGUIN
13991   {
13992     if (player->MovPos == 0)
13993     {
13994       player->is_digging = FALSE;
13995       player->is_collecting = FALSE;
13996     }
13997
13998     if (player->MovPos == 0)    // last pushing move finished
13999       player->is_pushing = FALSE;
14000
14001     if (mode == DF_NO_PUSH)     // player just stopped pushing
14002     {
14003       player->is_switching = FALSE;
14004       player->push_delay = -1;
14005
14006       return MP_NO_ACTION;
14007     }
14008   }
14009
14010   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14011     old_element = Back[jx][jy];
14012
14013   // in case of element dropped at player position, check background
14014   else if (Back[jx][jy] != EL_EMPTY &&
14015            game.engine_version >= VERSION_IDENT(2,2,0,0))
14016     old_element = Back[jx][jy];
14017
14018   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14019     return MP_NO_ACTION;        // field has no opening in this direction
14020
14021   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14022     return MP_NO_ACTION;        // field has no opening in this direction
14023
14024   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14025   {
14026     SplashAcid(x, y);
14027
14028     Tile[jx][jy] = player->artwork_element;
14029     InitMovingField(jx, jy, MV_DOWN);
14030     Store[jx][jy] = EL_ACID;
14031     ContinueMoving(jx, jy);
14032     BuryPlayer(player);
14033
14034     return MP_DONT_RUN_INTO;
14035   }
14036
14037   if (player_can_move && DONT_RUN_INTO(element))
14038   {
14039     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14040
14041     return MP_DONT_RUN_INTO;
14042   }
14043
14044   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14045     return MP_NO_ACTION;
14046
14047   collect_count = element_info[element].collect_count_initial;
14048
14049   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14050     return MP_NO_ACTION;
14051
14052   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14053     player_can_move = player_can_move_or_snap;
14054
14055   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14056       game.engine_version >= VERSION_IDENT(2,2,0,0))
14057   {
14058     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14059                                player->index_bit, dig_side);
14060     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14061                                         player->index_bit, dig_side);
14062
14063     if (element == EL_DC_LANDMINE)
14064       Bang(x, y);
14065
14066     if (Tile[x][y] != element)          // field changed by snapping
14067       return MP_ACTION;
14068
14069     return MP_NO_ACTION;
14070   }
14071
14072   if (player->gravity && is_player && !player->is_auto_moving &&
14073       canFallDown(player) && move_direction != MV_DOWN &&
14074       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14075     return MP_NO_ACTION;        // player cannot walk here due to gravity
14076
14077   if (player_can_move &&
14078       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14079   {
14080     int sound_element = SND_ELEMENT(element);
14081     int sound_action = ACTION_WALKING;
14082
14083     if (IS_RND_GATE(element))
14084     {
14085       if (!player->key[RND_GATE_NR(element)])
14086         return MP_NO_ACTION;
14087     }
14088     else if (IS_RND_GATE_GRAY(element))
14089     {
14090       if (!player->key[RND_GATE_GRAY_NR(element)])
14091         return MP_NO_ACTION;
14092     }
14093     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14094     {
14095       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14096         return MP_NO_ACTION;
14097     }
14098     else if (element == EL_EXIT_OPEN ||
14099              element == EL_EM_EXIT_OPEN ||
14100              element == EL_EM_EXIT_OPENING ||
14101              element == EL_STEEL_EXIT_OPEN ||
14102              element == EL_EM_STEEL_EXIT_OPEN ||
14103              element == EL_EM_STEEL_EXIT_OPENING ||
14104              element == EL_SP_EXIT_OPEN ||
14105              element == EL_SP_EXIT_OPENING)
14106     {
14107       sound_action = ACTION_PASSING;    // player is passing exit
14108     }
14109     else if (element == EL_EMPTY)
14110     {
14111       sound_action = ACTION_MOVING;             // nothing to walk on
14112     }
14113
14114     // play sound from background or player, whatever is available
14115     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14116       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14117     else
14118       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14119   }
14120   else if (player_can_move &&
14121            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14122   {
14123     if (!ACCESS_FROM(element, opposite_direction))
14124       return MP_NO_ACTION;      // field not accessible from this direction
14125
14126     if (CAN_MOVE(element))      // only fixed elements can be passed!
14127       return MP_NO_ACTION;
14128
14129     if (IS_EM_GATE(element))
14130     {
14131       if (!player->key[EM_GATE_NR(element)])
14132         return MP_NO_ACTION;
14133     }
14134     else if (IS_EM_GATE_GRAY(element))
14135     {
14136       if (!player->key[EM_GATE_GRAY_NR(element)])
14137         return MP_NO_ACTION;
14138     }
14139     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14140     {
14141       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14142         return MP_NO_ACTION;
14143     }
14144     else if (IS_EMC_GATE(element))
14145     {
14146       if (!player->key[EMC_GATE_NR(element)])
14147         return MP_NO_ACTION;
14148     }
14149     else if (IS_EMC_GATE_GRAY(element))
14150     {
14151       if (!player->key[EMC_GATE_GRAY_NR(element)])
14152         return MP_NO_ACTION;
14153     }
14154     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14155     {
14156       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14157         return MP_NO_ACTION;
14158     }
14159     else if (element == EL_DC_GATE_WHITE ||
14160              element == EL_DC_GATE_WHITE_GRAY ||
14161              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14162     {
14163       if (player->num_white_keys == 0)
14164         return MP_NO_ACTION;
14165
14166       player->num_white_keys--;
14167     }
14168     else if (IS_SP_PORT(element))
14169     {
14170       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14171           element == EL_SP_GRAVITY_PORT_RIGHT ||
14172           element == EL_SP_GRAVITY_PORT_UP ||
14173           element == EL_SP_GRAVITY_PORT_DOWN)
14174         player->gravity = !player->gravity;
14175       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14176                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14177                element == EL_SP_GRAVITY_ON_PORT_UP ||
14178                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14179         player->gravity = TRUE;
14180       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14181                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14182                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14183                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14184         player->gravity = FALSE;
14185     }
14186
14187     // automatically move to the next field with double speed
14188     player->programmed_action = move_direction;
14189
14190     if (player->move_delay_reset_counter == 0)
14191     {
14192       player->move_delay_reset_counter = 2;     // two double speed steps
14193
14194       DOUBLE_PLAYER_SPEED(player);
14195     }
14196
14197     PlayLevelSoundAction(x, y, ACTION_PASSING);
14198   }
14199   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14200   {
14201     RemoveField(x, y);
14202
14203     if (mode != DF_SNAP)
14204     {
14205       GfxElement[x][y] = GFX_ELEMENT(element);
14206       player->is_digging = TRUE;
14207     }
14208
14209     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14210
14211     // use old behaviour for old levels (digging)
14212     if (!level.finish_dig_collect)
14213     {
14214       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14215                                           player->index_bit, dig_side);
14216
14217       // if digging triggered player relocation, finish digging tile
14218       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14219         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14220     }
14221
14222     if (mode == DF_SNAP)
14223     {
14224       if (level.block_snap_field)
14225         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14226       else
14227         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14228
14229       // use old behaviour for old levels (snapping)
14230       if (!level.finish_dig_collect)
14231         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14232                                             player->index_bit, dig_side);
14233     }
14234   }
14235   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14236   {
14237     RemoveField(x, y);
14238
14239     if (is_player && mode != DF_SNAP)
14240     {
14241       GfxElement[x][y] = element;
14242       player->is_collecting = TRUE;
14243     }
14244
14245     if (element == EL_SPEED_PILL)
14246     {
14247       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14248     }
14249     else if (element == EL_EXTRA_TIME && level.time > 0)
14250     {
14251       TimeLeft += level.extra_time;
14252
14253       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14254
14255       DisplayGameControlValues();
14256     }
14257     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14258     {
14259       player->shield_normal_time_left += level.shield_normal_time;
14260       if (element == EL_SHIELD_DEADLY)
14261         player->shield_deadly_time_left += level.shield_deadly_time;
14262     }
14263     else if (element == EL_DYNAMITE ||
14264              element == EL_EM_DYNAMITE ||
14265              element == EL_SP_DISK_RED)
14266     {
14267       if (player->inventory_size < MAX_INVENTORY_SIZE)
14268         player->inventory_element[player->inventory_size++] = element;
14269
14270       DrawGameDoorValues();
14271     }
14272     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14273     {
14274       player->dynabomb_count++;
14275       player->dynabombs_left++;
14276     }
14277     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14278     {
14279       player->dynabomb_size++;
14280     }
14281     else if (element == EL_DYNABOMB_INCREASE_POWER)
14282     {
14283       player->dynabomb_xl = TRUE;
14284     }
14285     else if (IS_KEY(element))
14286     {
14287       player->key[KEY_NR(element)] = TRUE;
14288
14289       DrawGameDoorValues();
14290     }
14291     else if (element == EL_DC_KEY_WHITE)
14292     {
14293       player->num_white_keys++;
14294
14295       // display white keys?
14296       // DrawGameDoorValues();
14297     }
14298     else if (IS_ENVELOPE(element))
14299     {
14300       player->show_envelope = element;
14301     }
14302     else if (element == EL_EMC_LENSES)
14303     {
14304       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14305
14306       RedrawAllInvisibleElementsForLenses();
14307     }
14308     else if (element == EL_EMC_MAGNIFIER)
14309     {
14310       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14311
14312       RedrawAllInvisibleElementsForMagnifier();
14313     }
14314     else if (IS_DROPPABLE(element) ||
14315              IS_THROWABLE(element))     // can be collected and dropped
14316     {
14317       int i;
14318
14319       if (collect_count == 0)
14320         player->inventory_infinite_element = element;
14321       else
14322         for (i = 0; i < collect_count; i++)
14323           if (player->inventory_size < MAX_INVENTORY_SIZE)
14324             player->inventory_element[player->inventory_size++] = element;
14325
14326       DrawGameDoorValues();
14327     }
14328     else if (collect_count > 0)
14329     {
14330       game.gems_still_needed -= collect_count;
14331       if (game.gems_still_needed < 0)
14332         game.gems_still_needed = 0;
14333
14334       game.snapshot.collected_item = TRUE;
14335
14336       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14337
14338       DisplayGameControlValues();
14339     }
14340
14341     RaiseScoreElement(element);
14342     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14343
14344     // use old behaviour for old levels (collecting)
14345     if (!level.finish_dig_collect && is_player)
14346     {
14347       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14348                                           player->index_bit, dig_side);
14349
14350       // if collecting triggered player relocation, finish collecting tile
14351       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14352         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14353     }
14354
14355     if (mode == DF_SNAP)
14356     {
14357       if (level.block_snap_field)
14358         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14359       else
14360         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14361
14362       // use old behaviour for old levels (snapping)
14363       if (!level.finish_dig_collect)
14364         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14365                                             player->index_bit, dig_side);
14366     }
14367   }
14368   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14369   {
14370     if (mode == DF_SNAP && element != EL_BD_ROCK)
14371       return MP_NO_ACTION;
14372
14373     if (CAN_FALL(element) && dy)
14374       return MP_NO_ACTION;
14375
14376     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14377         !(element == EL_SPRING && level.use_spring_bug))
14378       return MP_NO_ACTION;
14379
14380     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14381         ((move_direction & MV_VERTICAL &&
14382           ((element_info[element].move_pattern & MV_LEFT &&
14383             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14384            (element_info[element].move_pattern & MV_RIGHT &&
14385             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14386          (move_direction & MV_HORIZONTAL &&
14387           ((element_info[element].move_pattern & MV_UP &&
14388             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14389            (element_info[element].move_pattern & MV_DOWN &&
14390             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14391       return MP_NO_ACTION;
14392
14393     // do not push elements already moving away faster than player
14394     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14395         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14396       return MP_NO_ACTION;
14397
14398     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14399     {
14400       if (player->push_delay_value == -1 || !player_was_pushing)
14401         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14402     }
14403     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14404     {
14405       if (player->push_delay_value == -1)
14406         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14407     }
14408     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14409     {
14410       if (!player->is_pushing)
14411         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14412     }
14413
14414     player->is_pushing = TRUE;
14415     player->is_active = TRUE;
14416
14417     if (!(IN_LEV_FIELD(nextx, nexty) &&
14418           (IS_FREE(nextx, nexty) ||
14419            (IS_SB_ELEMENT(element) &&
14420             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14421            (IS_CUSTOM_ELEMENT(element) &&
14422             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14423       return MP_NO_ACTION;
14424
14425     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14426       return MP_NO_ACTION;
14427
14428     if (player->push_delay == -1)       // new pushing; restart delay
14429       player->push_delay = 0;
14430
14431     if (player->push_delay < player->push_delay_value &&
14432         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14433         element != EL_SPRING && element != EL_BALLOON)
14434     {
14435       // make sure that there is no move delay before next try to push
14436       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14437         player->move_delay = 0;
14438
14439       return MP_NO_ACTION;
14440     }
14441
14442     if (IS_CUSTOM_ELEMENT(element) &&
14443         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14444     {
14445       if (!DigFieldByCE(nextx, nexty, element))
14446         return MP_NO_ACTION;
14447     }
14448
14449     if (IS_SB_ELEMENT(element))
14450     {
14451       boolean sokoban_task_solved = FALSE;
14452
14453       if (element == EL_SOKOBAN_FIELD_FULL)
14454       {
14455         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14456
14457         IncrementSokobanFieldsNeeded();
14458         IncrementSokobanObjectsNeeded();
14459       }
14460
14461       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14462       {
14463         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14464
14465         DecrementSokobanFieldsNeeded();
14466         DecrementSokobanObjectsNeeded();
14467
14468         // sokoban object was pushed from empty field to sokoban field
14469         if (Back[x][y] == EL_EMPTY)
14470           sokoban_task_solved = TRUE;
14471       }
14472
14473       Tile[x][y] = EL_SOKOBAN_OBJECT;
14474
14475       if (Back[x][y] == Back[nextx][nexty])
14476         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14477       else if (Back[x][y] != 0)
14478         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14479                                     ACTION_EMPTYING);
14480       else
14481         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14482                                     ACTION_FILLING);
14483
14484       if (sokoban_task_solved &&
14485           game.sokoban_fields_still_needed == 0 &&
14486           game.sokoban_objects_still_needed == 0 &&
14487           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14488       {
14489         game.players_still_needed = 0;
14490
14491         LevelSolved();
14492
14493         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14494       }
14495     }
14496     else
14497       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14498
14499     InitMovingField(x, y, move_direction);
14500     GfxAction[x][y] = ACTION_PUSHING;
14501
14502     if (mode == DF_SNAP)
14503       ContinueMoving(x, y);
14504     else
14505       MovPos[x][y] = (dx != 0 ? dx : dy);
14506
14507     Pushed[x][y] = TRUE;
14508     Pushed[nextx][nexty] = TRUE;
14509
14510     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14511       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14512     else
14513       player->push_delay_value = -1;    // get new value later
14514
14515     // check for element change _after_ element has been pushed
14516     if (game.use_change_when_pushing_bug)
14517     {
14518       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14519                                  player->index_bit, dig_side);
14520       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14521                                           player->index_bit, dig_side);
14522     }
14523   }
14524   else if (IS_SWITCHABLE(element))
14525   {
14526     if (PLAYER_SWITCHING(player, x, y))
14527     {
14528       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14529                                           player->index_bit, dig_side);
14530
14531       return MP_ACTION;
14532     }
14533
14534     player->is_switching = TRUE;
14535     player->switch_x = x;
14536     player->switch_y = y;
14537
14538     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14539
14540     if (element == EL_ROBOT_WHEEL)
14541     {
14542       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14543
14544       game.robot_wheel_x = x;
14545       game.robot_wheel_y = y;
14546       game.robot_wheel_active = TRUE;
14547
14548       TEST_DrawLevelField(x, y);
14549     }
14550     else if (element == EL_SP_TERMINAL)
14551     {
14552       int xx, yy;
14553
14554       SCAN_PLAYFIELD(xx, yy)
14555       {
14556         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14557         {
14558           Bang(xx, yy);
14559         }
14560         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14561         {
14562           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14563
14564           ResetGfxAnimation(xx, yy);
14565           TEST_DrawLevelField(xx, yy);
14566         }
14567       }
14568     }
14569     else if (IS_BELT_SWITCH(element))
14570     {
14571       ToggleBeltSwitch(x, y);
14572     }
14573     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14574              element == EL_SWITCHGATE_SWITCH_DOWN ||
14575              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14576              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14577     {
14578       ToggleSwitchgateSwitch(x, y);
14579     }
14580     else if (element == EL_LIGHT_SWITCH ||
14581              element == EL_LIGHT_SWITCH_ACTIVE)
14582     {
14583       ToggleLightSwitch(x, y);
14584     }
14585     else if (element == EL_TIMEGATE_SWITCH ||
14586              element == EL_DC_TIMEGATE_SWITCH)
14587     {
14588       ActivateTimegateSwitch(x, y);
14589     }
14590     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14591              element == EL_BALLOON_SWITCH_RIGHT ||
14592              element == EL_BALLOON_SWITCH_UP    ||
14593              element == EL_BALLOON_SWITCH_DOWN  ||
14594              element == EL_BALLOON_SWITCH_NONE  ||
14595              element == EL_BALLOON_SWITCH_ANY)
14596     {
14597       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14598                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14599                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14600                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14601                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14602                              move_direction);
14603     }
14604     else if (element == EL_LAMP)
14605     {
14606       Tile[x][y] = EL_LAMP_ACTIVE;
14607       game.lights_still_needed--;
14608
14609       ResetGfxAnimation(x, y);
14610       TEST_DrawLevelField(x, y);
14611     }
14612     else if (element == EL_TIME_ORB_FULL)
14613     {
14614       Tile[x][y] = EL_TIME_ORB_EMPTY;
14615
14616       if (level.time > 0 || level.use_time_orb_bug)
14617       {
14618         TimeLeft += level.time_orb_time;
14619         game.no_time_limit = FALSE;
14620
14621         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14622
14623         DisplayGameControlValues();
14624       }
14625
14626       ResetGfxAnimation(x, y);
14627       TEST_DrawLevelField(x, y);
14628     }
14629     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14630              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14631     {
14632       int xx, yy;
14633
14634       game.ball_active = !game.ball_active;
14635
14636       SCAN_PLAYFIELD(xx, yy)
14637       {
14638         int e = Tile[xx][yy];
14639
14640         if (game.ball_active)
14641         {
14642           if (e == EL_EMC_MAGIC_BALL)
14643             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14644           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14645             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14646         }
14647         else
14648         {
14649           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14650             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14651           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14652             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14653         }
14654       }
14655     }
14656
14657     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14658                                         player->index_bit, dig_side);
14659
14660     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14661                                         player->index_bit, dig_side);
14662
14663     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14664                                         player->index_bit, dig_side);
14665
14666     return MP_ACTION;
14667   }
14668   else
14669   {
14670     if (!PLAYER_SWITCHING(player, x, y))
14671     {
14672       player->is_switching = TRUE;
14673       player->switch_x = x;
14674       player->switch_y = y;
14675
14676       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14677                                  player->index_bit, dig_side);
14678       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14679                                           player->index_bit, dig_side);
14680
14681       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14682                                  player->index_bit, dig_side);
14683       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14684                                           player->index_bit, dig_side);
14685     }
14686
14687     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14688                                player->index_bit, dig_side);
14689     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14690                                         player->index_bit, dig_side);
14691
14692     return MP_NO_ACTION;
14693   }
14694
14695   player->push_delay = -1;
14696
14697   if (is_player)                // function can also be called by EL_PENGUIN
14698   {
14699     if (Tile[x][y] != element)          // really digged/collected something
14700     {
14701       player->is_collecting = !player->is_digging;
14702       player->is_active = TRUE;
14703
14704       player->last_removed_element = element;
14705     }
14706   }
14707
14708   return MP_MOVING;
14709 }
14710
14711 static boolean DigFieldByCE(int x, int y, int digging_element)
14712 {
14713   int element = Tile[x][y];
14714
14715   if (!IS_FREE(x, y))
14716   {
14717     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14718                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14719                   ACTION_BREAKING);
14720
14721     // no element can dig solid indestructible elements
14722     if (IS_INDESTRUCTIBLE(element) &&
14723         !IS_DIGGABLE(element) &&
14724         !IS_COLLECTIBLE(element))
14725       return FALSE;
14726
14727     if (AmoebaNr[x][y] &&
14728         (element == EL_AMOEBA_FULL ||
14729          element == EL_BD_AMOEBA ||
14730          element == EL_AMOEBA_GROWING))
14731     {
14732       AmoebaCnt[AmoebaNr[x][y]]--;
14733       AmoebaCnt2[AmoebaNr[x][y]]--;
14734     }
14735
14736     if (IS_MOVING(x, y))
14737       RemoveMovingField(x, y);
14738     else
14739     {
14740       RemoveField(x, y);
14741       TEST_DrawLevelField(x, y);
14742     }
14743
14744     // if digged element was about to explode, prevent the explosion
14745     ExplodeField[x][y] = EX_TYPE_NONE;
14746
14747     PlayLevelSoundAction(x, y, action);
14748   }
14749
14750   Store[x][y] = EL_EMPTY;
14751
14752   // this makes it possible to leave the removed element again
14753   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14754     Store[x][y] = element;
14755
14756   return TRUE;
14757 }
14758
14759 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14760 {
14761   int jx = player->jx, jy = player->jy;
14762   int x = jx + dx, y = jy + dy;
14763   int snap_direction = (dx == -1 ? MV_LEFT  :
14764                         dx == +1 ? MV_RIGHT :
14765                         dy == -1 ? MV_UP    :
14766                         dy == +1 ? MV_DOWN  : MV_NONE);
14767   boolean can_continue_snapping = (level.continuous_snapping &&
14768                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14769
14770   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14771     return FALSE;
14772
14773   if (!player->active || !IN_LEV_FIELD(x, y))
14774     return FALSE;
14775
14776   if (dx && dy)
14777     return FALSE;
14778
14779   if (!dx && !dy)
14780   {
14781     if (player->MovPos == 0)
14782       player->is_pushing = FALSE;
14783
14784     player->is_snapping = FALSE;
14785
14786     if (player->MovPos == 0)
14787     {
14788       player->is_moving = FALSE;
14789       player->is_digging = FALSE;
14790       player->is_collecting = FALSE;
14791     }
14792
14793     return FALSE;
14794   }
14795
14796   // prevent snapping with already pressed snap key when not allowed
14797   if (player->is_snapping && !can_continue_snapping)
14798     return FALSE;
14799
14800   player->MovDir = snap_direction;
14801
14802   if (player->MovPos == 0)
14803   {
14804     player->is_moving = FALSE;
14805     player->is_digging = FALSE;
14806     player->is_collecting = FALSE;
14807   }
14808
14809   player->is_dropping = FALSE;
14810   player->is_dropping_pressed = FALSE;
14811   player->drop_pressed_delay = 0;
14812
14813   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14814     return FALSE;
14815
14816   player->is_snapping = TRUE;
14817   player->is_active = TRUE;
14818
14819   if (player->MovPos == 0)
14820   {
14821     player->is_moving = FALSE;
14822     player->is_digging = FALSE;
14823     player->is_collecting = FALSE;
14824   }
14825
14826   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14827     TEST_DrawLevelField(player->last_jx, player->last_jy);
14828
14829   TEST_DrawLevelField(x, y);
14830
14831   return TRUE;
14832 }
14833
14834 static boolean DropElement(struct PlayerInfo *player)
14835 {
14836   int old_element, new_element;
14837   int dropx = player->jx, dropy = player->jy;
14838   int drop_direction = player->MovDir;
14839   int drop_side = drop_direction;
14840   int drop_element = get_next_dropped_element(player);
14841
14842   /* do not drop an element on top of another element; when holding drop key
14843      pressed without moving, dropped element must move away before the next
14844      element can be dropped (this is especially important if the next element
14845      is dynamite, which can be placed on background for historical reasons) */
14846   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14847     return MP_ACTION;
14848
14849   if (IS_THROWABLE(drop_element))
14850   {
14851     dropx += GET_DX_FROM_DIR(drop_direction);
14852     dropy += GET_DY_FROM_DIR(drop_direction);
14853
14854     if (!IN_LEV_FIELD(dropx, dropy))
14855       return FALSE;
14856   }
14857
14858   old_element = Tile[dropx][dropy];     // old element at dropping position
14859   new_element = drop_element;           // default: no change when dropping
14860
14861   // check if player is active, not moving and ready to drop
14862   if (!player->active || player->MovPos || player->drop_delay > 0)
14863     return FALSE;
14864
14865   // check if player has anything that can be dropped
14866   if (new_element == EL_UNDEFINED)
14867     return FALSE;
14868
14869   // only set if player has anything that can be dropped
14870   player->is_dropping_pressed = TRUE;
14871
14872   // check if drop key was pressed long enough for EM style dynamite
14873   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14874     return FALSE;
14875
14876   // check if anything can be dropped at the current position
14877   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14878     return FALSE;
14879
14880   // collected custom elements can only be dropped on empty fields
14881   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14882     return FALSE;
14883
14884   if (old_element != EL_EMPTY)
14885     Back[dropx][dropy] = old_element;   // store old element on this field
14886
14887   ResetGfxAnimation(dropx, dropy);
14888   ResetRandomAnimationValue(dropx, dropy);
14889
14890   if (player->inventory_size > 0 ||
14891       player->inventory_infinite_element != EL_UNDEFINED)
14892   {
14893     if (player->inventory_size > 0)
14894     {
14895       player->inventory_size--;
14896
14897       DrawGameDoorValues();
14898
14899       if (new_element == EL_DYNAMITE)
14900         new_element = EL_DYNAMITE_ACTIVE;
14901       else if (new_element == EL_EM_DYNAMITE)
14902         new_element = EL_EM_DYNAMITE_ACTIVE;
14903       else if (new_element == EL_SP_DISK_RED)
14904         new_element = EL_SP_DISK_RED_ACTIVE;
14905     }
14906
14907     Tile[dropx][dropy] = new_element;
14908
14909     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14910       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14911                           el2img(Tile[dropx][dropy]), 0);
14912
14913     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14914
14915     // needed if previous element just changed to "empty" in the last frame
14916     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14917
14918     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14919                                player->index_bit, drop_side);
14920     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14921                                         CE_PLAYER_DROPS_X,
14922                                         player->index_bit, drop_side);
14923
14924     TestIfElementTouchesCustomElement(dropx, dropy);
14925   }
14926   else          // player is dropping a dyna bomb
14927   {
14928     player->dynabombs_left--;
14929
14930     Tile[dropx][dropy] = new_element;
14931
14932     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14933       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14934                           el2img(Tile[dropx][dropy]), 0);
14935
14936     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14937   }
14938
14939   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14940     InitField_WithBug1(dropx, dropy, FALSE);
14941
14942   new_element = Tile[dropx][dropy];     // element might have changed
14943
14944   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14945       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14946   {
14947     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14948       MovDir[dropx][dropy] = drop_direction;
14949
14950     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14951
14952     // do not cause impact style collision by dropping elements that can fall
14953     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14954   }
14955
14956   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14957   player->is_dropping = TRUE;
14958
14959   player->drop_pressed_delay = 0;
14960   player->is_dropping_pressed = FALSE;
14961
14962   player->drop_x = dropx;
14963   player->drop_y = dropy;
14964
14965   return TRUE;
14966 }
14967
14968 // ----------------------------------------------------------------------------
14969 // game sound playing functions
14970 // ----------------------------------------------------------------------------
14971
14972 static int *loop_sound_frame = NULL;
14973 static int *loop_sound_volume = NULL;
14974
14975 void InitPlayLevelSound(void)
14976 {
14977   int num_sounds = getSoundListSize();
14978
14979   checked_free(loop_sound_frame);
14980   checked_free(loop_sound_volume);
14981
14982   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14983   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14984 }
14985
14986 static void PlayLevelSound(int x, int y, int nr)
14987 {
14988   int sx = SCREENX(x), sy = SCREENY(y);
14989   int volume, stereo_position;
14990   int max_distance = 8;
14991   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14992
14993   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14994       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14995     return;
14996
14997   if (!IN_LEV_FIELD(x, y) ||
14998       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14999       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15000     return;
15001
15002   volume = SOUND_MAX_VOLUME;
15003
15004   if (!IN_SCR_FIELD(sx, sy))
15005   {
15006     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15007     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15008
15009     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15010   }
15011
15012   stereo_position = (SOUND_MAX_LEFT +
15013                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15014                      (SCR_FIELDX + 2 * max_distance));
15015
15016   if (IS_LOOP_SOUND(nr))
15017   {
15018     /* This assures that quieter loop sounds do not overwrite louder ones,
15019        while restarting sound volume comparison with each new game frame. */
15020
15021     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15022       return;
15023
15024     loop_sound_volume[nr] = volume;
15025     loop_sound_frame[nr] = FrameCounter;
15026   }
15027
15028   PlaySoundExt(nr, volume, stereo_position, type);
15029 }
15030
15031 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15032 {
15033   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15034                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15035                  y < LEVELY(BY1) ? LEVELY(BY1) :
15036                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15037                  sound_action);
15038 }
15039
15040 static void PlayLevelSoundAction(int x, int y, int action)
15041 {
15042   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15043 }
15044
15045 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15046 {
15047   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15048
15049   if (sound_effect != SND_UNDEFINED)
15050     PlayLevelSound(x, y, sound_effect);
15051 }
15052
15053 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15054                                               int action)
15055 {
15056   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15057
15058   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15059     PlayLevelSound(x, y, sound_effect);
15060 }
15061
15062 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15063 {
15064   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15065
15066   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15067     PlayLevelSound(x, y, sound_effect);
15068 }
15069
15070 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15071 {
15072   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15073
15074   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15075     StopSound(sound_effect);
15076 }
15077
15078 static int getLevelMusicNr(void)
15079 {
15080   if (levelset.music[level_nr] != MUS_UNDEFINED)
15081     return levelset.music[level_nr];            // from config file
15082   else
15083     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15084 }
15085
15086 static void FadeLevelSounds(void)
15087 {
15088   FadeSounds();
15089 }
15090
15091 static void FadeLevelMusic(void)
15092 {
15093   int music_nr = getLevelMusicNr();
15094   char *curr_music = getCurrentlyPlayingMusicFilename();
15095   char *next_music = getMusicInfoEntryFilename(music_nr);
15096
15097   if (!strEqual(curr_music, next_music))
15098     FadeMusic();
15099 }
15100
15101 void FadeLevelSoundsAndMusic(void)
15102 {
15103   FadeLevelSounds();
15104   FadeLevelMusic();
15105 }
15106
15107 static void PlayLevelMusic(void)
15108 {
15109   int music_nr = getLevelMusicNr();
15110   char *curr_music = getCurrentlyPlayingMusicFilename();
15111   char *next_music = getMusicInfoEntryFilename(music_nr);
15112
15113   if (!strEqual(curr_music, next_music))
15114     PlayMusicLoop(music_nr);
15115 }
15116
15117 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15118 {
15119   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15120   int offset = 0;
15121   int x = xx - offset;
15122   int y = yy - offset;
15123
15124   switch (sample)
15125   {
15126     case SOUND_blank:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15128       break;
15129
15130     case SOUND_roll:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15132       break;
15133
15134     case SOUND_stone:
15135       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15136       break;
15137
15138     case SOUND_nut:
15139       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15140       break;
15141
15142     case SOUND_crack:
15143       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15144       break;
15145
15146     case SOUND_bug:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15148       break;
15149
15150     case SOUND_tank:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15152       break;
15153
15154     case SOUND_android_clone:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15156       break;
15157
15158     case SOUND_android_move:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15160       break;
15161
15162     case SOUND_spring:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15164       break;
15165
15166     case SOUND_slurp:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15168       break;
15169
15170     case SOUND_eater:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15172       break;
15173
15174     case SOUND_eater_eat:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15176       break;
15177
15178     case SOUND_alien:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15180       break;
15181
15182     case SOUND_collect:
15183       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15184       break;
15185
15186     case SOUND_diamond:
15187       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15188       break;
15189
15190     case SOUND_squash:
15191       // !!! CHECK THIS !!!
15192 #if 1
15193       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15194 #else
15195       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15196 #endif
15197       break;
15198
15199     case SOUND_wonderfall:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15201       break;
15202
15203     case SOUND_drip:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15205       break;
15206
15207     case SOUND_push:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15209       break;
15210
15211     case SOUND_dirt:
15212       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15213       break;
15214
15215     case SOUND_acid:
15216       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15217       break;
15218
15219     case SOUND_ball:
15220       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15221       break;
15222
15223     case SOUND_slide:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15225       break;
15226
15227     case SOUND_wonder:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15229       break;
15230
15231     case SOUND_door:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15233       break;
15234
15235     case SOUND_exit_open:
15236       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15237       break;
15238
15239     case SOUND_exit_leave:
15240       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15241       break;
15242
15243     case SOUND_dynamite:
15244       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15245       break;
15246
15247     case SOUND_tick:
15248       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15249       break;
15250
15251     case SOUND_press:
15252       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15253       break;
15254
15255     case SOUND_wheel:
15256       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15257       break;
15258
15259     case SOUND_boom:
15260       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15261       break;
15262
15263     case SOUND_die:
15264       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15265       break;
15266
15267     case SOUND_time:
15268       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15269       break;
15270
15271     default:
15272       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15273       break;
15274   }
15275 }
15276
15277 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15278 {
15279   int element = map_element_SP_to_RND(element_sp);
15280   int action = map_action_SP_to_RND(action_sp);
15281   int offset = (setup.sp_show_border_elements ? 0 : 1);
15282   int x = xx - offset;
15283   int y = yy - offset;
15284
15285   PlayLevelSoundElementAction(x, y, element, action);
15286 }
15287
15288 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15289 {
15290   int element = map_element_MM_to_RND(element_mm);
15291   int action = map_action_MM_to_RND(action_mm);
15292   int offset = 0;
15293   int x = xx - offset;
15294   int y = yy - offset;
15295
15296   if (!IS_MM_ELEMENT(element))
15297     element = EL_MM_DEFAULT;
15298
15299   PlayLevelSoundElementAction(x, y, element, action);
15300 }
15301
15302 void PlaySound_MM(int sound_mm)
15303 {
15304   int sound = map_sound_MM_to_RND(sound_mm);
15305
15306   if (sound == SND_UNDEFINED)
15307     return;
15308
15309   PlaySound(sound);
15310 }
15311
15312 void PlaySoundLoop_MM(int sound_mm)
15313 {
15314   int sound = map_sound_MM_to_RND(sound_mm);
15315
15316   if (sound == SND_UNDEFINED)
15317     return;
15318
15319   PlaySoundLoop(sound);
15320 }
15321
15322 void StopSound_MM(int sound_mm)
15323 {
15324   int sound = map_sound_MM_to_RND(sound_mm);
15325
15326   if (sound == SND_UNDEFINED)
15327     return;
15328
15329   StopSound(sound);
15330 }
15331
15332 void RaiseScore(int value)
15333 {
15334   game.score += value;
15335
15336   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15337
15338   DisplayGameControlValues();
15339 }
15340
15341 void RaiseScoreElement(int element)
15342 {
15343   switch (element)
15344   {
15345     case EL_EMERALD:
15346     case EL_BD_DIAMOND:
15347     case EL_EMERALD_YELLOW:
15348     case EL_EMERALD_RED:
15349     case EL_EMERALD_PURPLE:
15350     case EL_SP_INFOTRON:
15351       RaiseScore(level.score[SC_EMERALD]);
15352       break;
15353     case EL_DIAMOND:
15354       RaiseScore(level.score[SC_DIAMOND]);
15355       break;
15356     case EL_CRYSTAL:
15357       RaiseScore(level.score[SC_CRYSTAL]);
15358       break;
15359     case EL_PEARL:
15360       RaiseScore(level.score[SC_PEARL]);
15361       break;
15362     case EL_BUG:
15363     case EL_BD_BUTTERFLY:
15364     case EL_SP_ELECTRON:
15365       RaiseScore(level.score[SC_BUG]);
15366       break;
15367     case EL_SPACESHIP:
15368     case EL_BD_FIREFLY:
15369     case EL_SP_SNIKSNAK:
15370       RaiseScore(level.score[SC_SPACESHIP]);
15371       break;
15372     case EL_YAMYAM:
15373     case EL_DARK_YAMYAM:
15374       RaiseScore(level.score[SC_YAMYAM]);
15375       break;
15376     case EL_ROBOT:
15377       RaiseScore(level.score[SC_ROBOT]);
15378       break;
15379     case EL_PACMAN:
15380       RaiseScore(level.score[SC_PACMAN]);
15381       break;
15382     case EL_NUT:
15383       RaiseScore(level.score[SC_NUT]);
15384       break;
15385     case EL_DYNAMITE:
15386     case EL_EM_DYNAMITE:
15387     case EL_SP_DISK_RED:
15388     case EL_DYNABOMB_INCREASE_NUMBER:
15389     case EL_DYNABOMB_INCREASE_SIZE:
15390     case EL_DYNABOMB_INCREASE_POWER:
15391       RaiseScore(level.score[SC_DYNAMITE]);
15392       break;
15393     case EL_SHIELD_NORMAL:
15394     case EL_SHIELD_DEADLY:
15395       RaiseScore(level.score[SC_SHIELD]);
15396       break;
15397     case EL_EXTRA_TIME:
15398       RaiseScore(level.extra_time_score);
15399       break;
15400     case EL_KEY_1:
15401     case EL_KEY_2:
15402     case EL_KEY_3:
15403     case EL_KEY_4:
15404     case EL_EM_KEY_1:
15405     case EL_EM_KEY_2:
15406     case EL_EM_KEY_3:
15407     case EL_EM_KEY_4:
15408     case EL_EMC_KEY_5:
15409     case EL_EMC_KEY_6:
15410     case EL_EMC_KEY_7:
15411     case EL_EMC_KEY_8:
15412     case EL_DC_KEY_WHITE:
15413       RaiseScore(level.score[SC_KEY]);
15414       break;
15415     default:
15416       RaiseScore(element_info[element].collect_score);
15417       break;
15418   }
15419 }
15420
15421 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15422 {
15423   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15424   {
15425     if (!quick_quit)
15426     {
15427       // prevent short reactivation of overlay buttons while closing door
15428       SetOverlayActive(FALSE);
15429
15430       // door may still be open due to skipped or envelope style request
15431       CloseDoor(DOOR_CLOSE_1);
15432     }
15433
15434     if (network.enabled)
15435       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15436     else
15437     {
15438       if (quick_quit)
15439         FadeSkipNextFadeIn();
15440
15441       SetGameStatus(GAME_MODE_MAIN);
15442
15443       DrawMainMenu();
15444     }
15445   }
15446   else          // continue playing the game
15447   {
15448     if (tape.playing && tape.deactivate_display)
15449       TapeDeactivateDisplayOff(TRUE);
15450
15451     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15452
15453     if (tape.playing && tape.deactivate_display)
15454       TapeDeactivateDisplayOn();
15455   }
15456 }
15457
15458 void RequestQuitGame(boolean escape_key_pressed)
15459 {
15460   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15461   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15462                         level_editor_test_game);
15463   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15464                           quick_quit);
15465
15466   RequestQuitGameExt(skip_request, quick_quit,
15467                      "Do you really want to quit the game?");
15468 }
15469
15470 void RequestRestartGame(char *message)
15471 {
15472   game.restart_game_message = NULL;
15473
15474   boolean has_started_game = hasStartedNetworkGame();
15475   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15476
15477   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15478   {
15479     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15480   }
15481   else
15482   {
15483     // needed in case of envelope request to close game panel
15484     CloseDoor(DOOR_CLOSE_1);
15485
15486     SetGameStatus(GAME_MODE_MAIN);
15487
15488     DrawMainMenu();
15489   }
15490 }
15491
15492 void CheckGameOver(void)
15493 {
15494   static boolean last_game_over = FALSE;
15495   static int game_over_delay = 0;
15496   int game_over_delay_value = 50;
15497   boolean game_over = checkGameFailed();
15498
15499   // do not handle game over if request dialog is already active
15500   if (game.request_active)
15501     return;
15502
15503   // do not ask to play again if game was never actually played
15504   if (!game.GamePlayed)
15505     return;
15506
15507   if (!game_over)
15508   {
15509     last_game_over = FALSE;
15510     game_over_delay = game_over_delay_value;
15511
15512     return;
15513   }
15514
15515   if (game_over_delay > 0)
15516   {
15517     game_over_delay--;
15518
15519     return;
15520   }
15521
15522   if (last_game_over != game_over)
15523     game.restart_game_message = (hasStartedNetworkGame() ?
15524                                  "Game over! Play it again?" :
15525                                  "Game over!");
15526
15527   last_game_over = game_over;
15528 }
15529
15530 boolean checkGameSolved(void)
15531 {
15532   // set for all game engines if level was solved
15533   return game.LevelSolved_GameEnd;
15534 }
15535
15536 boolean checkGameFailed(void)
15537 {
15538   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15539     return (game_em.game_over && !game_em.level_solved);
15540   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15541     return (game_sp.game_over && !game_sp.level_solved);
15542   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15543     return (game_mm.game_over && !game_mm.level_solved);
15544   else                          // GAME_ENGINE_TYPE_RND
15545     return (game.GameOver && !game.LevelSolved);
15546 }
15547
15548 boolean checkGameEnded(void)
15549 {
15550   return (checkGameSolved() || checkGameFailed());
15551 }
15552
15553
15554 // ----------------------------------------------------------------------------
15555 // random generator functions
15556 // ----------------------------------------------------------------------------
15557
15558 unsigned int InitEngineRandom_RND(int seed)
15559 {
15560   game.num_random_calls = 0;
15561
15562   return InitEngineRandom(seed);
15563 }
15564
15565 unsigned int RND(int max)
15566 {
15567   if (max > 0)
15568   {
15569     game.num_random_calls++;
15570
15571     return GetEngineRandom(max);
15572   }
15573
15574   return 0;
15575 }
15576
15577
15578 // ----------------------------------------------------------------------------
15579 // game engine snapshot handling functions
15580 // ----------------------------------------------------------------------------
15581
15582 struct EngineSnapshotInfo
15583 {
15584   // runtime values for custom element collect score
15585   int collect_score[NUM_CUSTOM_ELEMENTS];
15586
15587   // runtime values for group element choice position
15588   int choice_pos[NUM_GROUP_ELEMENTS];
15589
15590   // runtime values for belt position animations
15591   int belt_graphic[4][NUM_BELT_PARTS];
15592   int belt_anim_mode[4][NUM_BELT_PARTS];
15593 };
15594
15595 static struct EngineSnapshotInfo engine_snapshot_rnd;
15596 static char *snapshot_level_identifier = NULL;
15597 static int snapshot_level_nr = -1;
15598
15599 static void SaveEngineSnapshotValues_RND(void)
15600 {
15601   static int belt_base_active_element[4] =
15602   {
15603     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15604     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15605     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15606     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15607   };
15608   int i, j;
15609
15610   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15611   {
15612     int element = EL_CUSTOM_START + i;
15613
15614     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15615   }
15616
15617   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15618   {
15619     int element = EL_GROUP_START + i;
15620
15621     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15622   }
15623
15624   for (i = 0; i < 4; i++)
15625   {
15626     for (j = 0; j < NUM_BELT_PARTS; j++)
15627     {
15628       int element = belt_base_active_element[i] + j;
15629       int graphic = el2img(element);
15630       int anim_mode = graphic_info[graphic].anim_mode;
15631
15632       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15633       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15634     }
15635   }
15636 }
15637
15638 static void LoadEngineSnapshotValues_RND(void)
15639 {
15640   unsigned int num_random_calls = game.num_random_calls;
15641   int i, j;
15642
15643   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15644   {
15645     int element = EL_CUSTOM_START + i;
15646
15647     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15648   }
15649
15650   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15651   {
15652     int element = EL_GROUP_START + i;
15653
15654     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15655   }
15656
15657   for (i = 0; i < 4; i++)
15658   {
15659     for (j = 0; j < NUM_BELT_PARTS; j++)
15660     {
15661       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15662       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15663
15664       graphic_info[graphic].anim_mode = anim_mode;
15665     }
15666   }
15667
15668   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15669   {
15670     InitRND(tape.random_seed);
15671     for (i = 0; i < num_random_calls; i++)
15672       RND(1);
15673   }
15674
15675   if (game.num_random_calls != num_random_calls)
15676   {
15677     Error("number of random calls out of sync");
15678     Error("number of random calls should be %d", num_random_calls);
15679     Error("number of random calls is %d", game.num_random_calls);
15680
15681     Fail("this should not happen -- please debug");
15682   }
15683 }
15684
15685 void FreeEngineSnapshotSingle(void)
15686 {
15687   FreeSnapshotSingle();
15688
15689   setString(&snapshot_level_identifier, NULL);
15690   snapshot_level_nr = -1;
15691 }
15692
15693 void FreeEngineSnapshotList(void)
15694 {
15695   FreeSnapshotList();
15696 }
15697
15698 static ListNode *SaveEngineSnapshotBuffers(void)
15699 {
15700   ListNode *buffers = NULL;
15701
15702   // copy some special values to a structure better suited for the snapshot
15703
15704   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15705     SaveEngineSnapshotValues_RND();
15706   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15707     SaveEngineSnapshotValues_EM();
15708   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15709     SaveEngineSnapshotValues_SP(&buffers);
15710   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15711     SaveEngineSnapshotValues_MM(&buffers);
15712
15713   // save values stored in special snapshot structure
15714
15715   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15716     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15717   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15718     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15719   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15720     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15721   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15722     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15723
15724   // save further RND engine values
15725
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15729
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15735
15736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15739
15740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15741
15742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15744
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15763
15764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15766
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15770
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15773
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15779
15780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15782
15783 #if 0
15784   ListNode *node = engine_snapshot_list_rnd;
15785   int num_bytes = 0;
15786
15787   while (node != NULL)
15788   {
15789     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15790
15791     node = node->next;
15792   }
15793
15794   Debug("game:playing:SaveEngineSnapshotBuffers",
15795         "size of engine snapshot: %d bytes", num_bytes);
15796 #endif
15797
15798   return buffers;
15799 }
15800
15801 void SaveEngineSnapshotSingle(void)
15802 {
15803   ListNode *buffers = SaveEngineSnapshotBuffers();
15804
15805   // finally save all snapshot buffers to single snapshot
15806   SaveSnapshotSingle(buffers);
15807
15808   // save level identification information
15809   setString(&snapshot_level_identifier, leveldir_current->identifier);
15810   snapshot_level_nr = level_nr;
15811 }
15812
15813 boolean CheckSaveEngineSnapshotToList(void)
15814 {
15815   boolean save_snapshot =
15816     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15817      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15818       game.snapshot.changed_action) ||
15819      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15820       game.snapshot.collected_item));
15821
15822   game.snapshot.changed_action = FALSE;
15823   game.snapshot.collected_item = FALSE;
15824   game.snapshot.save_snapshot = save_snapshot;
15825
15826   return save_snapshot;
15827 }
15828
15829 void SaveEngineSnapshotToList(void)
15830 {
15831   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15832       tape.quick_resume)
15833     return;
15834
15835   ListNode *buffers = SaveEngineSnapshotBuffers();
15836
15837   // finally save all snapshot buffers to snapshot list
15838   SaveSnapshotToList(buffers);
15839 }
15840
15841 void SaveEngineSnapshotToListInitial(void)
15842 {
15843   FreeEngineSnapshotList();
15844
15845   SaveEngineSnapshotToList();
15846 }
15847
15848 static void LoadEngineSnapshotValues(void)
15849 {
15850   // restore special values from snapshot structure
15851
15852   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15853     LoadEngineSnapshotValues_RND();
15854   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15855     LoadEngineSnapshotValues_EM();
15856   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15857     LoadEngineSnapshotValues_SP();
15858   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15859     LoadEngineSnapshotValues_MM();
15860 }
15861
15862 void LoadEngineSnapshotSingle(void)
15863 {
15864   LoadSnapshotSingle();
15865
15866   LoadEngineSnapshotValues();
15867 }
15868
15869 static void LoadEngineSnapshot_Undo(int steps)
15870 {
15871   LoadSnapshotFromList_Older(steps);
15872
15873   LoadEngineSnapshotValues();
15874 }
15875
15876 static void LoadEngineSnapshot_Redo(int steps)
15877 {
15878   LoadSnapshotFromList_Newer(steps);
15879
15880   LoadEngineSnapshotValues();
15881 }
15882
15883 boolean CheckEngineSnapshotSingle(void)
15884 {
15885   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15886           snapshot_level_nr == level_nr);
15887 }
15888
15889 boolean CheckEngineSnapshotList(void)
15890 {
15891   return CheckSnapshotList();
15892 }
15893
15894
15895 // ---------- new game button stuff -------------------------------------------
15896
15897 static struct
15898 {
15899   int graphic;
15900   struct XY *pos;
15901   int gadget_id;
15902   boolean *setup_value;
15903   boolean allowed_on_tape;
15904   boolean is_touch_button;
15905   char *infotext;
15906 } gamebutton_info[NUM_GAME_BUTTONS] =
15907 {
15908   {
15909     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15910     GAME_CTRL_ID_STOP,                          NULL,
15911     TRUE, FALSE,                                "stop game"
15912   },
15913   {
15914     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15915     GAME_CTRL_ID_PAUSE,                         NULL,
15916     TRUE, FALSE,                                "pause game"
15917   },
15918   {
15919     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15920     GAME_CTRL_ID_PLAY,                          NULL,
15921     TRUE, FALSE,                                "play game"
15922   },
15923   {
15924     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15925     GAME_CTRL_ID_UNDO,                          NULL,
15926     TRUE, FALSE,                                "undo step"
15927   },
15928   {
15929     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15930     GAME_CTRL_ID_REDO,                          NULL,
15931     TRUE, FALSE,                                "redo step"
15932   },
15933   {
15934     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15935     GAME_CTRL_ID_SAVE,                          NULL,
15936     TRUE, FALSE,                                "save game"
15937   },
15938   {
15939     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15940     GAME_CTRL_ID_PAUSE2,                        NULL,
15941     TRUE, FALSE,                                "pause game"
15942   },
15943   {
15944     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15945     GAME_CTRL_ID_LOAD,                          NULL,
15946     TRUE, FALSE,                                "load game"
15947   },
15948   {
15949     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15950     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15951     FALSE, FALSE,                               "stop game"
15952   },
15953   {
15954     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15955     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15956     FALSE, FALSE,                               "pause game"
15957   },
15958   {
15959     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15960     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15961     FALSE, FALSE,                               "play game"
15962   },
15963   {
15964     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15965     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15966     FALSE, TRUE,                                "stop game"
15967   },
15968   {
15969     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15970     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15971     FALSE, TRUE,                                "pause game"
15972   },
15973   {
15974     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15975     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15976     TRUE, FALSE,                                "background music on/off"
15977   },
15978   {
15979     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15980     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15981     TRUE, FALSE,                                "sound loops on/off"
15982   },
15983   {
15984     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15985     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15986     TRUE, FALSE,                                "normal sounds on/off"
15987   },
15988   {
15989     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15990     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15991     FALSE, FALSE,                               "background music on/off"
15992   },
15993   {
15994     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15995     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15996     FALSE, FALSE,                               "sound loops on/off"
15997   },
15998   {
15999     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16000     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16001     FALSE, FALSE,                               "normal sounds on/off"
16002   }
16003 };
16004
16005 void CreateGameButtons(void)
16006 {
16007   int i;
16008
16009   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16010   {
16011     int graphic = gamebutton_info[i].graphic;
16012     struct GraphicInfo *gfx = &graphic_info[graphic];
16013     struct XY *pos = gamebutton_info[i].pos;
16014     struct GadgetInfo *gi;
16015     int button_type;
16016     boolean checked;
16017     unsigned int event_mask;
16018     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16019     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16020     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16021     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16022     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16023     int gd_x   = gfx->src_x;
16024     int gd_y   = gfx->src_y;
16025     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16026     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16027     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16028     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16029     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16030     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16031     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16032     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16033     int id = i;
16034
16035     if (gfx->bitmap == NULL)
16036     {
16037       game_gadget[id] = NULL;
16038
16039       continue;
16040     }
16041
16042     if (id == GAME_CTRL_ID_STOP ||
16043         id == GAME_CTRL_ID_PANEL_STOP ||
16044         id == GAME_CTRL_ID_TOUCH_STOP ||
16045         id == GAME_CTRL_ID_PLAY ||
16046         id == GAME_CTRL_ID_PANEL_PLAY ||
16047         id == GAME_CTRL_ID_SAVE ||
16048         id == GAME_CTRL_ID_LOAD)
16049     {
16050       button_type = GD_TYPE_NORMAL_BUTTON;
16051       checked = FALSE;
16052       event_mask = GD_EVENT_RELEASED;
16053     }
16054     else if (id == GAME_CTRL_ID_UNDO ||
16055              id == GAME_CTRL_ID_REDO)
16056     {
16057       button_type = GD_TYPE_NORMAL_BUTTON;
16058       checked = FALSE;
16059       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16060     }
16061     else
16062     {
16063       button_type = GD_TYPE_CHECK_BUTTON;
16064       checked = (gamebutton_info[i].setup_value != NULL ?
16065                  *gamebutton_info[i].setup_value : FALSE);
16066       event_mask = GD_EVENT_PRESSED;
16067     }
16068
16069     gi = CreateGadget(GDI_CUSTOM_ID, id,
16070                       GDI_IMAGE_ID, graphic,
16071                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16072                       GDI_X, base_x + x,
16073                       GDI_Y, base_y + y,
16074                       GDI_WIDTH, gfx->width,
16075                       GDI_HEIGHT, gfx->height,
16076                       GDI_TYPE, button_type,
16077                       GDI_STATE, GD_BUTTON_UNPRESSED,
16078                       GDI_CHECKED, checked,
16079                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16080                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16081                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16082                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16083                       GDI_DIRECT_DRAW, FALSE,
16084                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16085                       GDI_EVENT_MASK, event_mask,
16086                       GDI_CALLBACK_ACTION, HandleGameButtons,
16087                       GDI_END);
16088
16089     if (gi == NULL)
16090       Fail("cannot create gadget");
16091
16092     game_gadget[id] = gi;
16093   }
16094 }
16095
16096 void FreeGameButtons(void)
16097 {
16098   int i;
16099
16100   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16101     FreeGadget(game_gadget[i]);
16102 }
16103
16104 static void UnmapGameButtonsAtSamePosition(int id)
16105 {
16106   int i;
16107
16108   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16109     if (i != id &&
16110         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16111         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16112       UnmapGadget(game_gadget[i]);
16113 }
16114
16115 static void UnmapGameButtonsAtSamePosition_All(void)
16116 {
16117   if (setup.show_snapshot_buttons)
16118   {
16119     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16120     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16121     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16122   }
16123   else
16124   {
16125     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16126     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16127     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16128
16129     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16130     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16131     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16132   }
16133 }
16134
16135 static void MapGameButtonsAtSamePosition(int id)
16136 {
16137   int i;
16138
16139   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16140     if (i != id &&
16141         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16142         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16143       MapGadget(game_gadget[i]);
16144
16145   UnmapGameButtonsAtSamePosition_All();
16146 }
16147
16148 void MapUndoRedoButtons(void)
16149 {
16150   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16151   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16152
16153   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16154   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16155 }
16156
16157 void UnmapUndoRedoButtons(void)
16158 {
16159   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16160   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16161
16162   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16163   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16164 }
16165
16166 void ModifyPauseButtons(void)
16167 {
16168   static int ids[] =
16169   {
16170     GAME_CTRL_ID_PAUSE,
16171     GAME_CTRL_ID_PAUSE2,
16172     GAME_CTRL_ID_PANEL_PAUSE,
16173     GAME_CTRL_ID_TOUCH_PAUSE,
16174     -1
16175   };
16176   int i;
16177
16178   for (i = 0; ids[i] > -1; i++)
16179     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16180 }
16181
16182 static void MapGameButtonsExt(boolean on_tape)
16183 {
16184   int i;
16185
16186   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16187     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16188         i != GAME_CTRL_ID_UNDO &&
16189         i != GAME_CTRL_ID_REDO)
16190       MapGadget(game_gadget[i]);
16191
16192   UnmapGameButtonsAtSamePosition_All();
16193
16194   RedrawGameButtons();
16195 }
16196
16197 static void UnmapGameButtonsExt(boolean on_tape)
16198 {
16199   int i;
16200
16201   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16202     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16203       UnmapGadget(game_gadget[i]);
16204 }
16205
16206 static void RedrawGameButtonsExt(boolean on_tape)
16207 {
16208   int i;
16209
16210   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16211     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16212       RedrawGadget(game_gadget[i]);
16213 }
16214
16215 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16216 {
16217   if (gi == NULL)
16218     return;
16219
16220   gi->checked = state;
16221 }
16222
16223 static void RedrawSoundButtonGadget(int id)
16224 {
16225   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16226              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16227              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16228              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16229              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16230              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16231              id);
16232
16233   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16234   RedrawGadget(game_gadget[id2]);
16235 }
16236
16237 void MapGameButtons(void)
16238 {
16239   MapGameButtonsExt(FALSE);
16240 }
16241
16242 void UnmapGameButtons(void)
16243 {
16244   UnmapGameButtonsExt(FALSE);
16245 }
16246
16247 void RedrawGameButtons(void)
16248 {
16249   RedrawGameButtonsExt(FALSE);
16250 }
16251
16252 void MapGameButtonsOnTape(void)
16253 {
16254   MapGameButtonsExt(TRUE);
16255 }
16256
16257 void UnmapGameButtonsOnTape(void)
16258 {
16259   UnmapGameButtonsExt(TRUE);
16260 }
16261
16262 void RedrawGameButtonsOnTape(void)
16263 {
16264   RedrawGameButtonsExt(TRUE);
16265 }
16266
16267 static void GameUndoRedoExt(void)
16268 {
16269   ClearPlayerAction();
16270
16271   tape.pausing = TRUE;
16272
16273   RedrawPlayfield();
16274   UpdateAndDisplayGameControlValues();
16275
16276   DrawCompleteVideoDisplay();
16277   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16278   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16279   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16280
16281   BackToFront();
16282 }
16283
16284 static void GameUndo(int steps)
16285 {
16286   if (!CheckEngineSnapshotList())
16287     return;
16288
16289   LoadEngineSnapshot_Undo(steps);
16290
16291   GameUndoRedoExt();
16292 }
16293
16294 static void GameRedo(int steps)
16295 {
16296   if (!CheckEngineSnapshotList())
16297     return;
16298
16299   LoadEngineSnapshot_Redo(steps);
16300
16301   GameUndoRedoExt();
16302 }
16303
16304 static void HandleGameButtonsExt(int id, int button)
16305 {
16306   static boolean game_undo_executed = FALSE;
16307   int steps = BUTTON_STEPSIZE(button);
16308   boolean handle_game_buttons =
16309     (game_status == GAME_MODE_PLAYING ||
16310      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16311
16312   if (!handle_game_buttons)
16313     return;
16314
16315   switch (id)
16316   {
16317     case GAME_CTRL_ID_STOP:
16318     case GAME_CTRL_ID_PANEL_STOP:
16319     case GAME_CTRL_ID_TOUCH_STOP:
16320       if (game_status == GAME_MODE_MAIN)
16321         break;
16322
16323       if (tape.playing)
16324         TapeStop();
16325       else
16326         RequestQuitGame(FALSE);
16327
16328       break;
16329
16330     case GAME_CTRL_ID_PAUSE:
16331     case GAME_CTRL_ID_PAUSE2:
16332     case GAME_CTRL_ID_PANEL_PAUSE:
16333     case GAME_CTRL_ID_TOUCH_PAUSE:
16334       if (network.enabled && game_status == GAME_MODE_PLAYING)
16335       {
16336         if (tape.pausing)
16337           SendToServer_ContinuePlaying();
16338         else
16339           SendToServer_PausePlaying();
16340       }
16341       else
16342         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16343
16344       game_undo_executed = FALSE;
16345
16346       break;
16347
16348     case GAME_CTRL_ID_PLAY:
16349     case GAME_CTRL_ID_PANEL_PLAY:
16350       if (game_status == GAME_MODE_MAIN)
16351       {
16352         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16353       }
16354       else if (tape.pausing)
16355       {
16356         if (network.enabled)
16357           SendToServer_ContinuePlaying();
16358         else
16359           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16360       }
16361       break;
16362
16363     case GAME_CTRL_ID_UNDO:
16364       // Important: When using "save snapshot when collecting an item" mode,
16365       // load last (current) snapshot for first "undo" after pressing "pause"
16366       // (else the last-but-one snapshot would be loaded, because the snapshot
16367       // pointer already points to the last snapshot when pressing "pause",
16368       // which is fine for "every step/move" mode, but not for "every collect")
16369       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16370           !game_undo_executed)
16371         steps--;
16372
16373       game_undo_executed = TRUE;
16374
16375       GameUndo(steps);
16376       break;
16377
16378     case GAME_CTRL_ID_REDO:
16379       GameRedo(steps);
16380       break;
16381
16382     case GAME_CTRL_ID_SAVE:
16383       TapeQuickSave();
16384       break;
16385
16386     case GAME_CTRL_ID_LOAD:
16387       TapeQuickLoad();
16388       break;
16389
16390     case SOUND_CTRL_ID_MUSIC:
16391     case SOUND_CTRL_ID_PANEL_MUSIC:
16392       if (setup.sound_music)
16393       { 
16394         setup.sound_music = FALSE;
16395
16396         FadeMusic();
16397       }
16398       else if (audio.music_available)
16399       { 
16400         setup.sound = setup.sound_music = TRUE;
16401
16402         SetAudioMode(setup.sound);
16403
16404         if (game_status == GAME_MODE_PLAYING)
16405           PlayLevelMusic();
16406       }
16407
16408       RedrawSoundButtonGadget(id);
16409
16410       break;
16411
16412     case SOUND_CTRL_ID_LOOPS:
16413     case SOUND_CTRL_ID_PANEL_LOOPS:
16414       if (setup.sound_loops)
16415         setup.sound_loops = FALSE;
16416       else if (audio.loops_available)
16417       {
16418         setup.sound = setup.sound_loops = TRUE;
16419
16420         SetAudioMode(setup.sound);
16421       }
16422
16423       RedrawSoundButtonGadget(id);
16424
16425       break;
16426
16427     case SOUND_CTRL_ID_SIMPLE:
16428     case SOUND_CTRL_ID_PANEL_SIMPLE:
16429       if (setup.sound_simple)
16430         setup.sound_simple = FALSE;
16431       else if (audio.sound_available)
16432       {
16433         setup.sound = setup.sound_simple = TRUE;
16434
16435         SetAudioMode(setup.sound);
16436       }
16437
16438       RedrawSoundButtonGadget(id);
16439
16440       break;
16441
16442     default:
16443       break;
16444   }
16445 }
16446
16447 static void HandleGameButtons(struct GadgetInfo *gi)
16448 {
16449   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16450 }
16451
16452 void HandleSoundButtonKeys(Key key)
16453 {
16454   if (key == setup.shortcut.sound_simple)
16455     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16456   else if (key == setup.shortcut.sound_loops)
16457     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16458   else if (key == setup.shortcut.sound_music)
16459     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16460 }