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