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