fixed graphical bugs when using smooth movements with BD engine
[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_GEMS_NEEDED                  2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_NEEDED,
250     &game.panel.gems_needed,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void InitFieldForEngine_RND(int x, int y)
1839 {
1840   int element = Tile[x][y];
1841
1842   // convert BD engine elements to corresponding R'n'D engine elements
1843   element = (element == EL_BD_EMPTY             ? EL_EMPTY :
1844              element == EL_BD_INBOX             ? EL_PLAYER_1 :
1845              element == EL_BD_SAND              ? EL_SAND :
1846              element == EL_BD_STEELWALL         ? EL_STEELWALL :
1847              element == EL_BD_EXIT_CLOSED       ? EL_EXIT_CLOSED :
1848              element == EL_BD_EXIT_OPEN         ? EL_EXIT_OPEN :
1849              element);
1850
1851   Tile[x][y] = element;
1852 }
1853
1854 static void InitFieldForEngine(int x, int y)
1855 {
1856   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1857     InitFieldForEngine_RND(x, y);
1858 }
1859
1860 static void InitField(int x, int y, boolean init_game)
1861 {
1862   int element = Tile[x][y];
1863
1864   switch (element)
1865   {
1866     case EL_SP_MURPHY:
1867     case EL_PLAYER_1:
1868     case EL_PLAYER_2:
1869     case EL_PLAYER_3:
1870     case EL_PLAYER_4:
1871       InitPlayerField(x, y, element, init_game);
1872       break;
1873
1874     case EL_SOKOBAN_FIELD_PLAYER:
1875       element = Tile[x][y] = EL_PLAYER_1;
1876       InitField(x, y, init_game);
1877
1878       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1879       InitField(x, y, init_game);
1880       break;
1881
1882     case EL_SOKOBAN_FIELD_EMPTY:
1883       IncrementSokobanFieldsNeeded();
1884       break;
1885
1886     case EL_SOKOBAN_OBJECT:
1887       IncrementSokobanObjectsNeeded();
1888       break;
1889
1890     case EL_STONEBLOCK:
1891       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1892         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1893       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1894         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1895       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1896         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1897       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1898         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1899       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1900         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1901       break;
1902
1903     case EL_BUG:
1904     case EL_BUG_RIGHT:
1905     case EL_BUG_UP:
1906     case EL_BUG_LEFT:
1907     case EL_BUG_DOWN:
1908     case EL_SPACESHIP:
1909     case EL_SPACESHIP_RIGHT:
1910     case EL_SPACESHIP_UP:
1911     case EL_SPACESHIP_LEFT:
1912     case EL_SPACESHIP_DOWN:
1913     case EL_BD_BUTTERFLY:
1914     case EL_BD_BUTTERFLY_RIGHT:
1915     case EL_BD_BUTTERFLY_UP:
1916     case EL_BD_BUTTERFLY_LEFT:
1917     case EL_BD_BUTTERFLY_DOWN:
1918     case EL_BD_FIREFLY:
1919     case EL_BD_FIREFLY_RIGHT:
1920     case EL_BD_FIREFLY_UP:
1921     case EL_BD_FIREFLY_LEFT:
1922     case EL_BD_FIREFLY_DOWN:
1923     case EL_PACMAN_RIGHT:
1924     case EL_PACMAN_UP:
1925     case EL_PACMAN_LEFT:
1926     case EL_PACMAN_DOWN:
1927     case EL_YAMYAM:
1928     case EL_YAMYAM_LEFT:
1929     case EL_YAMYAM_RIGHT:
1930     case EL_YAMYAM_UP:
1931     case EL_YAMYAM_DOWN:
1932     case EL_DARK_YAMYAM:
1933     case EL_ROBOT:
1934     case EL_PACMAN:
1935     case EL_SP_SNIKSNAK:
1936     case EL_SP_ELECTRON:
1937     case EL_MOLE:
1938     case EL_MOLE_LEFT:
1939     case EL_MOLE_RIGHT:
1940     case EL_MOLE_UP:
1941     case EL_MOLE_DOWN:
1942     case EL_SPRING_LEFT:
1943     case EL_SPRING_RIGHT:
1944       InitMovDir(x, y);
1945       break;
1946
1947     case EL_AMOEBA_FULL:
1948     case EL_BD_AMOEBA:
1949       InitAmoebaNr(x, y);
1950       break;
1951
1952     case EL_AMOEBA_DROP:
1953       if (y == lev_fieldy - 1)
1954       {
1955         Tile[x][y] = EL_AMOEBA_GROWING;
1956         Store[x][y] = EL_AMOEBA_WET;
1957       }
1958       break;
1959
1960     case EL_DYNAMITE_ACTIVE:
1961     case EL_SP_DISK_RED_ACTIVE:
1962     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1963     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1964     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1965     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1966       MovDelay[x][y] = 96;
1967       break;
1968
1969     case EL_EM_DYNAMITE_ACTIVE:
1970       MovDelay[x][y] = 32;
1971       break;
1972
1973     case EL_LAMP:
1974       game.lights_still_needed++;
1975       break;
1976
1977     case EL_PENGUIN:
1978       game.friends_still_needed++;
1979       break;
1980
1981     case EL_PIG:
1982     case EL_DRAGON:
1983       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1984       break;
1985
1986     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1987     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1988     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1989     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1990     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1991     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1992     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1993     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1994     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1995     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1996     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1997     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1998       if (init_game)
1999       {
2000         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2001         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2002         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2003
2004         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2005         {
2006           game.belt_dir[belt_nr] = belt_dir;
2007           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2008         }
2009         else    // more than one switch -- set it like the first switch
2010         {
2011           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2012         }
2013       }
2014       break;
2015
2016     case EL_LIGHT_SWITCH_ACTIVE:
2017       if (init_game)
2018         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2019       break;
2020
2021     case EL_INVISIBLE_STEELWALL:
2022     case EL_INVISIBLE_WALL:
2023     case EL_INVISIBLE_SAND:
2024       if (game.light_time_left > 0 ||
2025           game.lenses_time_left > 0)
2026         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2027       break;
2028
2029     case EL_EMC_MAGIC_BALL:
2030       if (game.ball_active)
2031         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2032       break;
2033
2034     case EL_EMC_MAGIC_BALL_SWITCH:
2035       if (game.ball_active)
2036         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2037       break;
2038
2039     case EL_TRIGGER_PLAYER:
2040     case EL_TRIGGER_ELEMENT:
2041     case EL_TRIGGER_CE_VALUE:
2042     case EL_TRIGGER_CE_SCORE:
2043     case EL_SELF:
2044     case EL_ANY_ELEMENT:
2045     case EL_CURRENT_CE_VALUE:
2046     case EL_CURRENT_CE_SCORE:
2047     case EL_PREV_CE_1:
2048     case EL_PREV_CE_2:
2049     case EL_PREV_CE_3:
2050     case EL_PREV_CE_4:
2051     case EL_PREV_CE_5:
2052     case EL_PREV_CE_6:
2053     case EL_PREV_CE_7:
2054     case EL_PREV_CE_8:
2055     case EL_NEXT_CE_1:
2056     case EL_NEXT_CE_2:
2057     case EL_NEXT_CE_3:
2058     case EL_NEXT_CE_4:
2059     case EL_NEXT_CE_5:
2060     case EL_NEXT_CE_6:
2061     case EL_NEXT_CE_7:
2062     case EL_NEXT_CE_8:
2063       // reference elements should not be used on the playfield
2064       Tile[x][y] = EL_EMPTY;
2065       break;
2066
2067     default:
2068       if (IS_CUSTOM_ELEMENT(element))
2069       {
2070         if (CAN_MOVE(element))
2071           InitMovDir(x, y);
2072
2073         if (!element_info[element].use_last_ce_value || init_game)
2074           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2075       }
2076       else if (IS_GROUP_ELEMENT(element))
2077       {
2078         Tile[x][y] = GetElementFromGroupElement(element);
2079
2080         InitField(x, y, init_game);
2081       }
2082       else if (IS_EMPTY_ELEMENT(element))
2083       {
2084         GfxElementEmpty[x][y] = element;
2085         Tile[x][y] = EL_EMPTY;
2086
2087         if (element_info[element].use_gfx_element)
2088           game.use_masked_elements = TRUE;
2089       }
2090
2091       break;
2092   }
2093
2094   if (!init_game)
2095     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2096 }
2097
2098 static void InitField_WithBug1(int x, int y, boolean init_game)
2099 {
2100   InitField(x, y, init_game);
2101
2102   // not needed to call InitMovDir() -- already done by InitField()!
2103   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2104       CAN_MOVE(Tile[x][y]))
2105     InitMovDir(x, y);
2106 }
2107
2108 static void InitField_WithBug2(int x, int y, boolean init_game)
2109 {
2110   int old_element = Tile[x][y];
2111
2112   InitField(x, y, init_game);
2113
2114   // not needed to call InitMovDir() -- already done by InitField()!
2115   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2116       CAN_MOVE(old_element) &&
2117       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2118     InitMovDir(x, y);
2119
2120   /* this case is in fact a combination of not less than three bugs:
2121      first, it calls InitMovDir() for elements that can move, although this is
2122      already done by InitField(); then, it checks the element that was at this
2123      field _before_ the call to InitField() (which can change it); lastly, it
2124      was not called for "mole with direction" elements, which were treated as
2125      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2126   */
2127 }
2128
2129 static int get_key_element_from_nr(int key_nr)
2130 {
2131   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2132                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2133                           EL_EM_KEY_1 : EL_KEY_1);
2134
2135   return key_base_element + key_nr;
2136 }
2137
2138 static int get_next_dropped_element(struct PlayerInfo *player)
2139 {
2140   return (player->inventory_size > 0 ?
2141           player->inventory_element[player->inventory_size - 1] :
2142           player->inventory_infinite_element != EL_UNDEFINED ?
2143           player->inventory_infinite_element :
2144           player->dynabombs_left > 0 ?
2145           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2146           EL_UNDEFINED);
2147 }
2148
2149 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2150 {
2151   // pos >= 0: get element from bottom of the stack;
2152   // pos <  0: get element from top of the stack
2153
2154   if (pos < 0)
2155   {
2156     int min_inventory_size = -pos;
2157     int inventory_pos = player->inventory_size - min_inventory_size;
2158     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2159
2160     return (player->inventory_size >= min_inventory_size ?
2161             player->inventory_element[inventory_pos] :
2162             player->inventory_infinite_element != EL_UNDEFINED ?
2163             player->inventory_infinite_element :
2164             player->dynabombs_left >= min_dynabombs_left ?
2165             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2166             EL_UNDEFINED);
2167   }
2168   else
2169   {
2170     int min_dynabombs_left = pos + 1;
2171     int min_inventory_size = pos + 1 - player->dynabombs_left;
2172     int inventory_pos = pos - player->dynabombs_left;
2173
2174     return (player->inventory_infinite_element != EL_UNDEFINED ?
2175             player->inventory_infinite_element :
2176             player->dynabombs_left >= min_dynabombs_left ?
2177             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2178             player->inventory_size >= min_inventory_size ?
2179             player->inventory_element[inventory_pos] :
2180             EL_UNDEFINED);
2181   }
2182 }
2183
2184 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2185 {
2186   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2187   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2188   int compare_result;
2189
2190   if (gpo1->sort_priority != gpo2->sort_priority)
2191     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2192   else
2193     compare_result = gpo1->nr - gpo2->nr;
2194
2195   return compare_result;
2196 }
2197
2198 int getPlayerInventorySize(int player_nr)
2199 {
2200   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2201     return game_em.ply[player_nr]->dynamite;
2202   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2203     return game_sp.red_disk_count;
2204   else
2205     return stored_player[player_nr].inventory_size;
2206 }
2207
2208 static void InitGameControlValues(void)
2209 {
2210   int i;
2211
2212   for (i = 0; game_panel_controls[i].nr != -1; i++)
2213   {
2214     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2215     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2216     struct TextPosInfo *pos = gpc->pos;
2217     int nr = gpc->nr;
2218     int type = gpc->type;
2219
2220     if (nr != i)
2221     {
2222       Error("'game_panel_controls' structure corrupted at %d", i);
2223
2224       Fail("this should not happen -- please debug");
2225     }
2226
2227     // force update of game controls after initialization
2228     gpc->value = gpc->last_value = -1;
2229     gpc->frame = gpc->last_frame = -1;
2230     gpc->gfx_frame = -1;
2231
2232     // determine panel value width for later calculation of alignment
2233     if (type == TYPE_INTEGER || type == TYPE_STRING)
2234     {
2235       pos->width = pos->size * getFontWidth(pos->font);
2236       pos->height = getFontHeight(pos->font);
2237     }
2238     else if (type == TYPE_ELEMENT)
2239     {
2240       pos->width = pos->size;
2241       pos->height = pos->size;
2242     }
2243
2244     // fill structure for game panel draw order
2245     gpo->nr = gpc->nr;
2246     gpo->sort_priority = pos->sort_priority;
2247   }
2248
2249   // sort game panel controls according to sort_priority and control number
2250   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2251         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2252 }
2253
2254 static void UpdatePlayfieldElementCount(void)
2255 {
2256   boolean use_element_count = FALSE;
2257   int i, j, x, y;
2258
2259   // first check if it is needed at all to calculate playfield element count
2260   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2261     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2262       use_element_count = TRUE;
2263
2264   if (!use_element_count)
2265     return;
2266
2267   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2268     element_info[i].element_count = 0;
2269
2270   SCAN_PLAYFIELD(x, y)
2271   {
2272     element_info[Tile[x][y]].element_count++;
2273   }
2274
2275   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2276     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2277       if (IS_IN_GROUP(j, i))
2278         element_info[EL_GROUP_START + i].element_count +=
2279           element_info[j].element_count;
2280 }
2281
2282 static void UpdateGameControlValues(void)
2283 {
2284   int i, k;
2285   int time = (game.LevelSolved ?
2286               game.LevelSolved_CountingTime :
2287               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2288               game_bd.time_played :
2289               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2290               game_em.lev->time :
2291               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2292               game_sp.time_played :
2293               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2294               game_mm.energy_left :
2295               game.no_level_time_limit ? TimePlayed : TimeLeft);
2296   int score = (game.LevelSolved ?
2297                game.LevelSolved_CountingScore :
2298                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2299                game_bd.score :
2300                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2301                game_em.lev->score :
2302                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2303                game_sp.score :
2304                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2305                game_mm.score :
2306                game.score);
2307   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2308               game_bd.gems_still_needed :
2309               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2310               game_em.lev->gems_needed :
2311               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2312               game_sp.infotrons_still_needed :
2313               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2314               game_mm.kettles_still_needed :
2315               game.gems_still_needed);
2316   int gems_needed = level.gems_needed;
2317   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2318                         game_bd.game->cave->diamonds_collected :
2319                         gems_needed - gems);
2320   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2321                     game_bd.game->cave->diamond_value :
2322                     level.score[SC_EMERALD]);
2323   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2324                      game_bd.gems_still_needed > 0 :
2325                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2326                      game_em.lev->gems_needed > 0 :
2327                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2328                      game_sp.infotrons_still_needed > 0 :
2329                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2330                      game_mm.kettles_still_needed > 0 ||
2331                      game_mm.lights_still_needed > 0 :
2332                      game.gems_still_needed > 0 ||
2333                      game.sokoban_fields_still_needed > 0 ||
2334                      game.sokoban_objects_still_needed > 0 ||
2335                      game.lights_still_needed > 0);
2336   int health = (game.LevelSolved ?
2337                 game.LevelSolved_CountingHealth :
2338                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2339                 MM_HEALTH(game_mm.laser_overload_value) :
2340                 game.health);
2341   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2342
2343   UpdatePlayfieldElementCount();
2344
2345   // update game panel control values
2346
2347   // used instead of "level_nr" (for network games)
2348   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2349   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2350   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2351   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2352   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2353
2354   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2355   for (i = 0; i < MAX_NUM_KEYS; i++)
2356     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2357   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2358   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2359
2360   if (game.centered_player_nr == -1)
2361   {
2362     for (i = 0; i < MAX_PLAYERS; i++)
2363     {
2364       // only one player in Supaplex game engine
2365       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2366         break;
2367
2368       for (k = 0; k < MAX_NUM_KEYS; k++)
2369       {
2370         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2371         {
2372           if (game_em.ply[i]->keys & (1 << k))
2373             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2374               get_key_element_from_nr(k);
2375         }
2376         else if (stored_player[i].key[k])
2377           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2378             get_key_element_from_nr(k);
2379       }
2380
2381       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2382         getPlayerInventorySize(i);
2383
2384       if (stored_player[i].num_white_keys > 0)
2385         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2386           EL_DC_KEY_WHITE;
2387
2388       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2389         stored_player[i].num_white_keys;
2390     }
2391   }
2392   else
2393   {
2394     int player_nr = game.centered_player_nr;
2395
2396     for (k = 0; k < MAX_NUM_KEYS; k++)
2397     {
2398       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2399       {
2400         if (game_em.ply[player_nr]->keys & (1 << k))
2401           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2402             get_key_element_from_nr(k);
2403       }
2404       else if (stored_player[player_nr].key[k])
2405         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2406           get_key_element_from_nr(k);
2407     }
2408
2409     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2410       getPlayerInventorySize(player_nr);
2411
2412     if (stored_player[player_nr].num_white_keys > 0)
2413       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2414
2415     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2416       stored_player[player_nr].num_white_keys;
2417   }
2418
2419   // re-arrange keys on game panel, if needed or if defined by style settings
2420   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2421   {
2422     int nr = GAME_PANEL_KEY_1 + i;
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2424     struct TextPosInfo *pos = gpc->pos;
2425
2426     // skip check if key is not in the player's inventory
2427     if (gpc->value == EL_EMPTY)
2428       continue;
2429
2430     // check if keys should be arranged on panel from left to right
2431     if (pos->style == STYLE_LEFTMOST_POSITION)
2432     {
2433       // check previous key positions (left from current key)
2434       for (k = 0; k < i; k++)
2435       {
2436         int nr_new = GAME_PANEL_KEY_1 + k;
2437
2438         if (game_panel_controls[nr_new].value == EL_EMPTY)
2439         {
2440           game_panel_controls[nr_new].value = gpc->value;
2441           gpc->value = EL_EMPTY;
2442
2443           break;
2444         }
2445       }
2446     }
2447
2448     // check if "undefined" keys can be placed at some other position
2449     if (pos->x == -1 && pos->y == -1)
2450     {
2451       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2452
2453       // 1st try: display key at the same position as normal or EM keys
2454       if (game_panel_controls[nr_new].value == EL_EMPTY)
2455       {
2456         game_panel_controls[nr_new].value = gpc->value;
2457       }
2458       else
2459       {
2460         // 2nd try: display key at the next free position in the key panel
2461         for (k = 0; k < STD_NUM_KEYS; k++)
2462         {
2463           nr_new = GAME_PANEL_KEY_1 + k;
2464
2465           if (game_panel_controls[nr_new].value == EL_EMPTY)
2466           {
2467             game_panel_controls[nr_new].value = gpc->value;
2468
2469             break;
2470           }
2471         }
2472       }
2473     }
2474   }
2475
2476   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2477   {
2478     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2479       get_inventory_element_from_pos(local_player, i);
2480     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2481       get_inventory_element_from_pos(local_player, -i - 1);
2482   }
2483
2484   game_panel_controls[GAME_PANEL_SCORE].value = score;
2485   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2486
2487   game_panel_controls[GAME_PANEL_TIME].value = time;
2488
2489   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2490   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2491   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2492
2493   if (level.time == 0)
2494     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2495   else
2496     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2497
2498   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2499   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2500
2501   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2502
2503   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2504     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2505      EL_EMPTY);
2506   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2507     local_player->shield_normal_time_left;
2508   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2509     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2510      EL_EMPTY);
2511   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2512     local_player->shield_deadly_time_left;
2513
2514   game_panel_controls[GAME_PANEL_EXIT].value =
2515     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2516
2517   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2518     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2519   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2520     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2521      EL_EMC_MAGIC_BALL_SWITCH);
2522
2523   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2524     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2525   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2526     game.light_time_left;
2527
2528   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2529     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2530   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2531     game.timegate_time_left;
2532
2533   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2534     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2535
2536   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2537     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2538   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2539     game.lenses_time_left;
2540
2541   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2542     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2543   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2544     game.magnify_time_left;
2545
2546   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2547     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2548      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2549      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2550      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2551      EL_BALLOON_SWITCH_NONE);
2552
2553   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2554     local_player->dynabomb_count;
2555   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2556     local_player->dynabomb_size;
2557   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2558     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2559
2560   game_panel_controls[GAME_PANEL_PENGUINS].value =
2561     game.friends_still_needed;
2562
2563   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2564     game.sokoban_objects_still_needed;
2565   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2566     game.sokoban_fields_still_needed;
2567
2568   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2569     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2570
2571   for (i = 0; i < NUM_BELTS; i++)
2572   {
2573     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2574       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2575        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2576     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2577       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2578   }
2579
2580   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2581     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2582   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2583     game.magic_wall_time_left;
2584
2585   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2586     local_player->gravity;
2587
2588   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2589     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2590
2591   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2592     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2593       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2594        game.panel.element[i].id : EL_UNDEFINED);
2595
2596   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2597     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2598       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2599        element_info[game.panel.element_count[i].id].element_count : 0);
2600
2601   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2602     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2603       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2604        element_info[game.panel.ce_score[i].id].collect_score : 0);
2605
2606   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2607     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2608       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2609        element_info[game.panel.ce_score_element[i].id].collect_score :
2610        EL_UNDEFINED);
2611
2612   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2613   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2614   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2615
2616   // update game panel control frames
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (gpc->type == TYPE_ELEMENT)
2623     {
2624       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2625       {
2626         int last_anim_random_frame = gfx.anim_random_frame;
2627         int element = gpc->value;
2628         int graphic = el2panelimg(element);
2629         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2630                                sync_random_frame :
2631                                graphic_info[graphic].anim_global_anim_sync ?
2632                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2633
2634         if (gpc->value != gpc->last_value)
2635         {
2636           gpc->gfx_frame = 0;
2637           gpc->gfx_random = init_gfx_random;
2638         }
2639         else
2640         {
2641           gpc->gfx_frame++;
2642
2643           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2644               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2645             gpc->gfx_random = init_gfx_random;
2646         }
2647
2648         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2649           gfx.anim_random_frame = gpc->gfx_random;
2650
2651         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2652           gpc->gfx_frame = element_info[element].collect_score;
2653
2654         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2655
2656         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2657           gfx.anim_random_frame = last_anim_random_frame;
2658       }
2659     }
2660     else if (gpc->type == TYPE_GRAPHIC)
2661     {
2662       if (gpc->graphic != IMG_UNDEFINED)
2663       {
2664         int last_anim_random_frame = gfx.anim_random_frame;
2665         int graphic = gpc->graphic;
2666         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2667                                sync_random_frame :
2668                                graphic_info[graphic].anim_global_anim_sync ?
2669                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2670
2671         if (gpc->value != gpc->last_value)
2672         {
2673           gpc->gfx_frame = 0;
2674           gpc->gfx_random = init_gfx_random;
2675         }
2676         else
2677         {
2678           gpc->gfx_frame++;
2679
2680           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2681               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2682             gpc->gfx_random = init_gfx_random;
2683         }
2684
2685         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2686           gfx.anim_random_frame = gpc->gfx_random;
2687
2688         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2689
2690         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2691           gfx.anim_random_frame = last_anim_random_frame;
2692       }
2693     }
2694   }
2695 }
2696
2697 static void DisplayGameControlValues(void)
2698 {
2699   boolean redraw_panel = FALSE;
2700   int i;
2701
2702   for (i = 0; game_panel_controls[i].nr != -1; i++)
2703   {
2704     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2705
2706     if (PANEL_DEACTIVATED(gpc->pos))
2707       continue;
2708
2709     if (gpc->value == gpc->last_value &&
2710         gpc->frame == gpc->last_frame)
2711       continue;
2712
2713     redraw_panel = TRUE;
2714   }
2715
2716   if (!redraw_panel)
2717     return;
2718
2719   // copy default game door content to main double buffer
2720
2721   // !!! CHECK AGAIN !!!
2722   SetPanelBackground();
2723   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2724   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2725
2726   // redraw game control buttons
2727   RedrawGameButtons();
2728
2729   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2730
2731   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2732   {
2733     int nr = game_panel_order[i].nr;
2734     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2735     struct TextPosInfo *pos = gpc->pos;
2736     int type = gpc->type;
2737     int value = gpc->value;
2738     int frame = gpc->frame;
2739     int size = pos->size;
2740     int font = pos->font;
2741     boolean draw_masked = pos->draw_masked;
2742     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2743
2744     if (PANEL_DEACTIVATED(pos))
2745       continue;
2746
2747     if (pos->class == get_hash_from_string("extra_panel_items") &&
2748         !setup.prefer_extra_panel_items)
2749       continue;
2750
2751     gpc->last_value = value;
2752     gpc->last_frame = frame;
2753
2754     if (type == TYPE_INTEGER)
2755     {
2756       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2757           nr == GAME_PANEL_INVENTORY_COUNT ||
2758           nr == GAME_PANEL_SCORE ||
2759           nr == GAME_PANEL_HIGHSCORE ||
2760           nr == GAME_PANEL_TIME)
2761       {
2762         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2763
2764         if (use_dynamic_size)           // use dynamic number of digits
2765         {
2766           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2767                               nr == GAME_PANEL_INVENTORY_COUNT ||
2768                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2769           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2770                           nr == GAME_PANEL_INVENTORY_COUNT ||
2771                           nr == GAME_PANEL_TIME ? 1 : 2);
2772           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2773                        nr == GAME_PANEL_INVENTORY_COUNT ||
2774                        nr == GAME_PANEL_TIME ? 3 : 5);
2775           int size2 = size1 + size_add;
2776           int font1 = pos->font;
2777           int font2 = pos->font_alt;
2778
2779           size = (value < value_change ? size1 : size2);
2780           font = (value < value_change ? font1 : font2);
2781         }
2782       }
2783
2784       // correct text size if "digits" is zero or less
2785       if (size <= 0)
2786         size = strlen(int2str(value, size));
2787
2788       // dynamically correct text alignment
2789       pos->width = size * getFontWidth(font);
2790
2791       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2792                   int2str(value, size), font, mask_mode);
2793     }
2794     else if (type == TYPE_ELEMENT)
2795     {
2796       int element, graphic;
2797       Bitmap *src_bitmap;
2798       int src_x, src_y;
2799       int width, height;
2800       int dst_x = PANEL_XPOS(pos);
2801       int dst_y = PANEL_YPOS(pos);
2802
2803       if (value != EL_UNDEFINED && value != EL_EMPTY)
2804       {
2805         element = value;
2806         graphic = el2panelimg(value);
2807
2808 #if 0
2809         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2810               element, EL_NAME(element), size);
2811 #endif
2812
2813         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2814           size = TILESIZE;
2815
2816         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2817                               &src_x, &src_y);
2818
2819         width  = graphic_info[graphic].width  * size / TILESIZE;
2820         height = graphic_info[graphic].height * size / TILESIZE;
2821
2822         if (draw_masked)
2823           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2824                            dst_x, dst_y);
2825         else
2826           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2827                      dst_x, dst_y);
2828       }
2829     }
2830     else if (type == TYPE_GRAPHIC)
2831     {
2832       int graphic        = gpc->graphic;
2833       int graphic_active = gpc->graphic_active;
2834       Bitmap *src_bitmap;
2835       int src_x, src_y;
2836       int width, height;
2837       int dst_x = PANEL_XPOS(pos);
2838       int dst_y = PANEL_YPOS(pos);
2839       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2840                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2841
2842       if (graphic != IMG_UNDEFINED && !skip)
2843       {
2844         if (pos->style == STYLE_REVERSE)
2845           value = 100 - value;
2846
2847         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2848
2849         if (pos->direction & MV_HORIZONTAL)
2850         {
2851           width  = graphic_info[graphic_active].width * value / 100;
2852           height = graphic_info[graphic_active].height;
2853
2854           if (pos->direction == MV_LEFT)
2855           {
2856             src_x += graphic_info[graphic_active].width - width;
2857             dst_x += graphic_info[graphic_active].width - width;
2858           }
2859         }
2860         else
2861         {
2862           width  = graphic_info[graphic_active].width;
2863           height = graphic_info[graphic_active].height * value / 100;
2864
2865           if (pos->direction == MV_UP)
2866           {
2867             src_y += graphic_info[graphic_active].height - height;
2868             dst_y += graphic_info[graphic_active].height - height;
2869           }
2870         }
2871
2872         if (draw_masked)
2873           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2874                            dst_x, dst_y);
2875         else
2876           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2877                      dst_x, dst_y);
2878
2879         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2880
2881         if (pos->direction & MV_HORIZONTAL)
2882         {
2883           if (pos->direction == MV_RIGHT)
2884           {
2885             src_x += width;
2886             dst_x += width;
2887           }
2888           else
2889           {
2890             dst_x = PANEL_XPOS(pos);
2891           }
2892
2893           width = graphic_info[graphic].width - width;
2894         }
2895         else
2896         {
2897           if (pos->direction == MV_DOWN)
2898           {
2899             src_y += height;
2900             dst_y += height;
2901           }
2902           else
2903           {
2904             dst_y = PANEL_YPOS(pos);
2905           }
2906
2907           height = graphic_info[graphic].height - height;
2908         }
2909
2910         if (draw_masked)
2911           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2912                            dst_x, dst_y);
2913         else
2914           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2915                      dst_x, dst_y);
2916       }
2917     }
2918     else if (type == TYPE_STRING)
2919     {
2920       boolean active = (value != 0);
2921       char *state_normal = "off";
2922       char *state_active = "on";
2923       char *state = (active ? state_active : state_normal);
2924       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2925                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2926                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2927                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2928
2929       if (nr == GAME_PANEL_GRAVITY_STATE)
2930       {
2931         int font1 = pos->font;          // (used for normal state)
2932         int font2 = pos->font_alt;      // (used for active state)
2933
2934         font = (active ? font2 : font1);
2935       }
2936
2937       if (s != NULL)
2938       {
2939         char *s_cut;
2940
2941         if (size <= 0)
2942         {
2943           // don't truncate output if "chars" is zero or less
2944           size = strlen(s);
2945
2946           // dynamically correct text alignment
2947           pos->width = size * getFontWidth(font);
2948         }
2949
2950         s_cut = getStringCopyN(s, size);
2951
2952         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2953                     s_cut, font, mask_mode);
2954
2955         free(s_cut);
2956       }
2957     }
2958
2959     redraw_mask |= REDRAW_DOOR_1;
2960   }
2961
2962   SetGameStatus(GAME_MODE_PLAYING);
2963 }
2964
2965 void UpdateAndDisplayGameControlValues(void)
2966 {
2967   if (tape.deactivate_display)
2968     return;
2969
2970   UpdateGameControlValues();
2971   DisplayGameControlValues();
2972 }
2973
2974 void UpdateGameDoorValues(void)
2975 {
2976   UpdateGameControlValues();
2977 }
2978
2979 void DrawGameDoorValues(void)
2980 {
2981   DisplayGameControlValues();
2982 }
2983
2984
2985 // ============================================================================
2986 // InitGameEngine()
2987 // ----------------------------------------------------------------------------
2988 // initialize game engine due to level / tape version number
2989 // ============================================================================
2990
2991 static void InitGameEngine(void)
2992 {
2993   int i, j, k, l, x, y;
2994
2995   // set game engine from tape file when re-playing, else from level file
2996   game.engine_version = (tape.playing ? tape.engine_version :
2997                          level.game_version);
2998
2999   // set single or multi-player game mode (needed for re-playing tapes)
3000   game.team_mode = setup.team_mode;
3001
3002   if (tape.playing)
3003   {
3004     int num_players = 0;
3005
3006     for (i = 0; i < MAX_PLAYERS; i++)
3007       if (tape.player_participates[i])
3008         num_players++;
3009
3010     // multi-player tapes contain input data for more than one player
3011     game.team_mode = (num_players > 1);
3012   }
3013
3014 #if 0
3015   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3016         level.game_version);
3017   Debug("game:init:level", "          tape.file_version   == %06d",
3018         tape.file_version);
3019   Debug("game:init:level", "          tape.game_version   == %06d",
3020         tape.game_version);
3021   Debug("game:init:level", "          tape.engine_version == %06d",
3022         tape.engine_version);
3023   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3024         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3025 #endif
3026
3027   // --------------------------------------------------------------------------
3028   // set flags for bugs and changes according to active game engine version
3029   // --------------------------------------------------------------------------
3030
3031   /*
3032     Summary of bugfix:
3033     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3034
3035     Bug was introduced in version:
3036     2.0.1
3037
3038     Bug was fixed in version:
3039     4.2.0.0
3040
3041     Description:
3042     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3043     but the property "can fall" was missing, which caused some levels to be
3044     unsolvable. This was fixed in version 4.2.0.0.
3045
3046     Affected levels/tapes:
3047     An example for a tape that was fixed by this bugfix is tape 029 from the
3048     level set "rnd_sam_bateman".
3049     The wrong behaviour will still be used for all levels or tapes that were
3050     created/recorded with it. An example for this is tape 023 from the level
3051     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3052   */
3053
3054   boolean use_amoeba_dropping_cannot_fall_bug =
3055     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3056       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3057      (tape.playing &&
3058       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3059       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3060
3061   /*
3062     Summary of bugfix/change:
3063     Fixed move speed of elements entering or leaving magic wall.
3064
3065     Fixed/changed in version:
3066     2.0.1
3067
3068     Description:
3069     Before 2.0.1, move speed of elements entering or leaving magic wall was
3070     twice as fast as it is now.
3071     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3072
3073     Affected levels/tapes:
3074     The first condition is generally needed for all levels/tapes before version
3075     2.0.1, which might use the old behaviour before it was changed; known tapes
3076     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3077     The second condition is an exception from the above case and is needed for
3078     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3079     above, but before it was known that this change would break tapes like the
3080     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3081     although the engine version while recording maybe was before 2.0.1. There
3082     are a lot of tapes that are affected by this exception, like tape 006 from
3083     the level set "rnd_conor_mancone".
3084   */
3085
3086   boolean use_old_move_stepsize_for_magic_wall =
3087     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3088      !(tape.playing &&
3089        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3090        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3091
3092   /*
3093     Summary of bugfix/change:
3094     Fixed handling for custom elements that change when pushed by the player.
3095
3096     Fixed/changed in version:
3097     3.1.0
3098
3099     Description:
3100     Before 3.1.0, custom elements that "change when pushing" changed directly
3101     after the player started pushing them (until then handled in "DigField()").
3102     Since 3.1.0, these custom elements are not changed until the "pushing"
3103     move of the element is finished (now handled in "ContinueMoving()").
3104
3105     Affected levels/tapes:
3106     The first condition is generally needed for all levels/tapes before version
3107     3.1.0, which might use the old behaviour before it was changed; known tapes
3108     that are affected are some tapes from the level set "Walpurgis Gardens" by
3109     Jamie Cullen.
3110     The second condition is an exception from the above case and is needed for
3111     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3112     above (including some development versions of 3.1.0), but before it was
3113     known that this change would break tapes like the above and was fixed in
3114     3.1.1, so that the changed behaviour was active although the engine version
3115     while recording maybe was before 3.1.0. There is at least one tape that is
3116     affected by this exception, which is the tape for the one-level set "Bug
3117     Machine" by Juergen Bonhagen.
3118   */
3119
3120   game.use_change_when_pushing_bug =
3121     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3122      !(tape.playing &&
3123        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3124        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3125
3126   /*
3127     Summary of bugfix/change:
3128     Fixed handling for blocking the field the player leaves when moving.
3129
3130     Fixed/changed in version:
3131     3.1.1
3132
3133     Description:
3134     Before 3.1.1, when "block last field when moving" was enabled, the field
3135     the player is leaving when moving was blocked for the time of the move,
3136     and was directly unblocked afterwards. This resulted in the last field
3137     being blocked for exactly one less than the number of frames of one player
3138     move. Additionally, even when blocking was disabled, the last field was
3139     blocked for exactly one frame.
3140     Since 3.1.1, due to changes in player movement handling, the last field
3141     is not blocked at all when blocking is disabled. When blocking is enabled,
3142     the last field is blocked for exactly the number of frames of one player
3143     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3144     last field is blocked for exactly one more than the number of frames of
3145     one player move.
3146
3147     Affected levels/tapes:
3148     (!!! yet to be determined -- probably many !!!)
3149   */
3150
3151   game.use_block_last_field_bug =
3152     (game.engine_version < VERSION_IDENT(3,1,1,0));
3153
3154   /* various special flags and settings for native Emerald Mine game engine */
3155
3156   game_em.use_single_button =
3157     (game.engine_version > VERSION_IDENT(4,0,0,2));
3158
3159   game_em.use_push_delay =
3160     (game.engine_version > VERSION_IDENT(4,3,7,1));
3161
3162   game_em.use_snap_key_bug =
3163     (game.engine_version < VERSION_IDENT(4,0,1,0));
3164
3165   game_em.use_random_bug =
3166     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3167
3168   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3169
3170   game_em.use_old_explosions            = use_old_em_engine;
3171   game_em.use_old_android               = use_old_em_engine;
3172   game_em.use_old_push_elements         = use_old_em_engine;
3173   game_em.use_old_push_into_acid        = use_old_em_engine;
3174
3175   game_em.use_wrap_around               = !use_old_em_engine;
3176
3177   // --------------------------------------------------------------------------
3178
3179   // set maximal allowed number of custom element changes per game frame
3180   game.max_num_changes_per_frame = 1;
3181
3182   // default scan direction: scan playfield from top/left to bottom/right
3183   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3184
3185   // dynamically adjust element properties according to game engine version
3186   InitElementPropertiesEngine(game.engine_version);
3187
3188   // ---------- initialize special element properties -------------------------
3189
3190   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3191   if (use_amoeba_dropping_cannot_fall_bug)
3192     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3193
3194   // ---------- initialize player's initial move delay ------------------------
3195
3196   // dynamically adjust player properties according to level information
3197   for (i = 0; i < MAX_PLAYERS; i++)
3198     game.initial_move_delay_value[i] =
3199       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3200
3201   // dynamically adjust player properties according to game engine version
3202   for (i = 0; i < MAX_PLAYERS; i++)
3203     game.initial_move_delay[i] =
3204       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3205        game.initial_move_delay_value[i] : 0);
3206
3207   // ---------- initialize player's initial push delay ------------------------
3208
3209   // dynamically adjust player properties according to game engine version
3210   game.initial_push_delay_value =
3211     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3212
3213   // ---------- initialize changing elements ----------------------------------
3214
3215   // initialize changing elements information
3216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[i];
3219
3220     // this pointer might have been changed in the level editor
3221     ei->change = &ei->change_page[0];
3222
3223     if (!IS_CUSTOM_ELEMENT(i))
3224     {
3225       ei->change->target_element = EL_EMPTY_SPACE;
3226       ei->change->delay_fixed = 0;
3227       ei->change->delay_random = 0;
3228       ei->change->delay_frames = 1;
3229     }
3230
3231     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3232     {
3233       ei->has_change_event[j] = FALSE;
3234
3235       ei->event_page_nr[j] = 0;
3236       ei->event_page[j] = &ei->change_page[0];
3237     }
3238   }
3239
3240   // add changing elements from pre-defined list
3241   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3242   {
3243     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3244     struct ElementInfo *ei = &element_info[ch_delay->element];
3245
3246     ei->change->target_element       = ch_delay->target_element;
3247     ei->change->delay_fixed          = ch_delay->change_delay;
3248
3249     ei->change->pre_change_function  = ch_delay->pre_change_function;
3250     ei->change->change_function      = ch_delay->change_function;
3251     ei->change->post_change_function = ch_delay->post_change_function;
3252
3253     ei->change->can_change = TRUE;
3254     ei->change->can_change_or_has_action = TRUE;
3255
3256     ei->has_change_event[CE_DELAY] = TRUE;
3257
3258     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3259     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3260   }
3261
3262   // ---------- initialize if element can trigger global animations -----------
3263
3264   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3265   {
3266     struct ElementInfo *ei = &element_info[i];
3267
3268     ei->has_anim_event = FALSE;
3269   }
3270
3271   InitGlobalAnimEventsForCustomElements();
3272
3273   // ---------- initialize internal run-time variables ------------------------
3274
3275   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3276   {
3277     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3278
3279     for (j = 0; j < ei->num_change_pages; j++)
3280     {
3281       ei->change_page[j].can_change_or_has_action =
3282         (ei->change_page[j].can_change |
3283          ei->change_page[j].has_action);
3284     }
3285   }
3286
3287   // add change events from custom element configuration
3288   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3289   {
3290     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3291
3292     for (j = 0; j < ei->num_change_pages; j++)
3293     {
3294       if (!ei->change_page[j].can_change_or_has_action)
3295         continue;
3296
3297       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3298       {
3299         // only add event page for the first page found with this event
3300         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3301         {
3302           ei->has_change_event[k] = TRUE;
3303
3304           ei->event_page_nr[k] = j;
3305           ei->event_page[k] = &ei->change_page[j];
3306         }
3307       }
3308     }
3309   }
3310
3311   // ---------- initialize reference elements in change conditions ------------
3312
3313   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3314   {
3315     int element = EL_CUSTOM_START + i;
3316     struct ElementInfo *ei = &element_info[element];
3317
3318     for (j = 0; j < ei->num_change_pages; j++)
3319     {
3320       int trigger_element = ei->change_page[j].initial_trigger_element;
3321
3322       if (trigger_element >= EL_PREV_CE_8 &&
3323           trigger_element <= EL_NEXT_CE_8)
3324         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3325
3326       ei->change_page[j].trigger_element = trigger_element;
3327     }
3328   }
3329
3330   // ---------- initialize run-time trigger player and element ----------------
3331
3332   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3333   {
3334     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3335
3336     for (j = 0; j < ei->num_change_pages; j++)
3337     {
3338       struct ElementChangeInfo *change = &ei->change_page[j];
3339
3340       change->actual_trigger_element = EL_EMPTY;
3341       change->actual_trigger_player = EL_EMPTY;
3342       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3343       change->actual_trigger_side = CH_SIDE_NONE;
3344       change->actual_trigger_ce_value = 0;
3345       change->actual_trigger_ce_score = 0;
3346       change->actual_trigger_x = -1;
3347       change->actual_trigger_y = -1;
3348     }
3349   }
3350
3351   // ---------- initialize trigger events -------------------------------------
3352
3353   // initialize trigger events information
3354   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3355     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3356       trigger_events[i][j] = FALSE;
3357
3358   // add trigger events from element change event properties
3359   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3360   {
3361     struct ElementInfo *ei = &element_info[i];
3362
3363     for (j = 0; j < ei->num_change_pages; j++)
3364     {
3365       struct ElementChangeInfo *change = &ei->change_page[j];
3366
3367       if (!change->can_change_or_has_action)
3368         continue;
3369
3370       if (change->has_event[CE_BY_OTHER_ACTION])
3371       {
3372         int trigger_element = change->trigger_element;
3373
3374         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3375         {
3376           if (change->has_event[k])
3377           {
3378             if (IS_GROUP_ELEMENT(trigger_element))
3379             {
3380               struct ElementGroupInfo *group =
3381                 element_info[trigger_element].group;
3382
3383               for (l = 0; l < group->num_elements_resolved; l++)
3384                 trigger_events[group->element_resolved[l]][k] = TRUE;
3385             }
3386             else if (trigger_element == EL_ANY_ELEMENT)
3387               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3388                 trigger_events[l][k] = TRUE;
3389             else
3390               trigger_events[trigger_element][k] = TRUE;
3391           }
3392         }
3393       }
3394     }
3395   }
3396
3397   // ---------- initialize push delay -----------------------------------------
3398
3399   // initialize push delay values to default
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401   {
3402     if (!IS_CUSTOM_ELEMENT(i))
3403     {
3404       // set default push delay values (corrected since version 3.0.7-1)
3405       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3406       {
3407         element_info[i].push_delay_fixed = 2;
3408         element_info[i].push_delay_random = 8;
3409       }
3410       else
3411       {
3412         element_info[i].push_delay_fixed = 8;
3413         element_info[i].push_delay_random = 8;
3414       }
3415     }
3416   }
3417
3418   // set push delay value for certain elements from pre-defined list
3419   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3420   {
3421     int e = push_delay_list[i].element;
3422
3423     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3424     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3425   }
3426
3427   // set push delay value for Supaplex elements for newer engine versions
3428   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3429   {
3430     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3431     {
3432       if (IS_SP_ELEMENT(i))
3433       {
3434         // set SP push delay to just enough to push under a falling zonk
3435         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3436
3437         element_info[i].push_delay_fixed  = delay;
3438         element_info[i].push_delay_random = 0;
3439       }
3440     }
3441   }
3442
3443   // ---------- initialize move stepsize --------------------------------------
3444
3445   // initialize move stepsize values to default
3446   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3447     if (!IS_CUSTOM_ELEMENT(i))
3448       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3449
3450   // set move stepsize value for certain elements from pre-defined list
3451   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3452   {
3453     int e = move_stepsize_list[i].element;
3454
3455     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3456
3457     // set move stepsize value for certain elements for older engine versions
3458     if (use_old_move_stepsize_for_magic_wall)
3459     {
3460       if (e == EL_MAGIC_WALL_FILLING ||
3461           e == EL_MAGIC_WALL_EMPTYING ||
3462           e == EL_BD_MAGIC_WALL_FILLING ||
3463           e == EL_BD_MAGIC_WALL_EMPTYING)
3464         element_info[e].move_stepsize *= 2;
3465     }
3466   }
3467
3468   // ---------- initialize collect score --------------------------------------
3469
3470   // initialize collect score values for custom elements from initial value
3471   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3472     if (IS_CUSTOM_ELEMENT(i))
3473       element_info[i].collect_score = element_info[i].collect_score_initial;
3474
3475   // ---------- initialize collect count --------------------------------------
3476
3477   // initialize collect count values for non-custom elements
3478   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3479     if (!IS_CUSTOM_ELEMENT(i))
3480       element_info[i].collect_count_initial = 0;
3481
3482   // add collect count values for all elements from pre-defined list
3483   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3484     element_info[collect_count_list[i].element].collect_count_initial =
3485       collect_count_list[i].count;
3486
3487   // ---------- initialize access direction -----------------------------------
3488
3489   // initialize access direction values to default (access from every side)
3490   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3491     if (!IS_CUSTOM_ELEMENT(i))
3492       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3493
3494   // set access direction value for certain elements from pre-defined list
3495   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3496     element_info[access_direction_list[i].element].access_direction =
3497       access_direction_list[i].direction;
3498
3499   // ---------- initialize explosion content ----------------------------------
3500   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3501   {
3502     if (IS_CUSTOM_ELEMENT(i))
3503       continue;
3504
3505     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3506     {
3507       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3508
3509       element_info[i].content.e[x][y] =
3510         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3511          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3512          i == EL_PLAYER_3 ? EL_EMERALD :
3513          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3514          i == EL_MOLE ? EL_EMERALD_RED :
3515          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3516          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3517          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3518          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3519          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3520          i == EL_WALL_EMERALD ? EL_EMERALD :
3521          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3522          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3523          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3524          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3525          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3526          i == EL_WALL_PEARL ? EL_PEARL :
3527          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3528          EL_EMPTY);
3529     }
3530   }
3531
3532   // ---------- initialize recursion detection --------------------------------
3533   recursion_loop_depth = 0;
3534   recursion_loop_detected = FALSE;
3535   recursion_loop_element = EL_UNDEFINED;
3536
3537   // ---------- initialize graphics engine ------------------------------------
3538   game.scroll_delay_value =
3539     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3540      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3541      !setup.forced_scroll_delay           ? 0 :
3542      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3543   if (game.forced_scroll_delay_value == -1)
3544     game.scroll_delay_value =
3545       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3546
3547   // ---------- initialize game engine snapshots ------------------------------
3548   for (i = 0; i < MAX_PLAYERS; i++)
3549     game.snapshot.last_action[i] = 0;
3550   game.snapshot.changed_action = FALSE;
3551   game.snapshot.collected_item = FALSE;
3552   game.snapshot.mode =
3553     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3554      SNAPSHOT_MODE_EVERY_STEP :
3555      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3556      SNAPSHOT_MODE_EVERY_MOVE :
3557      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3558      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3559   game.snapshot.save_snapshot = FALSE;
3560
3561   // ---------- initialize level time for Supaplex engine ---------------------
3562   // Supaplex levels with time limit currently unsupported -- should be added
3563   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3564     level.time = 0;
3565
3566   // ---------- initialize flags for handling game actions --------------------
3567
3568   // set flags for game actions to default values
3569   game.use_key_actions = TRUE;
3570   game.use_mouse_actions = FALSE;
3571
3572   // when using Mirror Magic game engine, handle mouse events only
3573   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3574   {
3575     game.use_key_actions = FALSE;
3576     game.use_mouse_actions = TRUE;
3577   }
3578
3579   // check for custom elements with mouse click events
3580   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3581   {
3582     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3583     {
3584       int element = EL_CUSTOM_START + i;
3585
3586       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3587           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3588           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3589           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3590         game.use_mouse_actions = TRUE;
3591     }
3592   }
3593 }
3594
3595 static int get_num_special_action(int element, int action_first,
3596                                   int action_last)
3597 {
3598   int num_special_action = 0;
3599   int i, j;
3600
3601   for (i = action_first; i <= action_last; i++)
3602   {
3603     boolean found = FALSE;
3604
3605     for (j = 0; j < NUM_DIRECTIONS; j++)
3606       if (el_act_dir2img(element, i, j) !=
3607           el_act_dir2img(element, ACTION_DEFAULT, j))
3608         found = TRUE;
3609
3610     if (found)
3611       num_special_action++;
3612     else
3613       break;
3614   }
3615
3616   return num_special_action;
3617 }
3618
3619
3620 // ============================================================================
3621 // InitGame()
3622 // ----------------------------------------------------------------------------
3623 // initialize and start new game
3624 // ============================================================================
3625
3626 #if DEBUG_INIT_PLAYER
3627 static void DebugPrintPlayerStatus(char *message)
3628 {
3629   int i;
3630
3631   if (!options.debug)
3632     return;
3633
3634   Debug("game:init:player", "%s:", message);
3635
3636   for (i = 0; i < MAX_PLAYERS; i++)
3637   {
3638     struct PlayerInfo *player = &stored_player[i];
3639
3640     Debug("game:init:player",
3641           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3642           i + 1,
3643           player->present,
3644           player->connected,
3645           player->connected_locally,
3646           player->connected_network,
3647           player->active,
3648           (local_player == player ? " (local player)" : ""));
3649   }
3650 }
3651 #endif
3652
3653 void InitGame(void)
3654 {
3655   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3656   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3657   int fade_mask = REDRAW_FIELD;
3658   boolean restarting = (game_status == GAME_MODE_PLAYING);
3659   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3660   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3661   int initial_move_dir = MV_DOWN;
3662   int i, j, x, y;
3663
3664   // required here to update video display before fading (FIX THIS)
3665   DrawMaskedBorder(REDRAW_DOOR_2);
3666
3667   if (!game.restart_level)
3668     CloseDoor(DOOR_CLOSE_1);
3669
3670   if (restarting)
3671   {
3672     // force fading out global animations displayed during game play
3673     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3674   }
3675   else
3676   {
3677     SetGameStatus(GAME_MODE_PLAYING);
3678   }
3679
3680   if (level_editor_test_game)
3681     FadeSkipNextFadeOut();
3682   else
3683     FadeSetEnterScreen();
3684
3685   if (CheckFadeAll())
3686     fade_mask = REDRAW_ALL;
3687
3688   FadeLevelSoundsAndMusic();
3689
3690   ExpireSoundLoops(TRUE);
3691
3692   FadeOut(fade_mask);
3693
3694   if (restarting)
3695   {
3696     // force restarting global animations displayed during game play
3697     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3698
3699     // this is required for "transforming" fade modes like cross-fading
3700     // (else global animations will be stopped, but not restarted here)
3701     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3702
3703     SetGameStatus(GAME_MODE_PLAYING);
3704   }
3705
3706   if (level_editor_test_game)
3707     FadeSkipNextFadeIn();
3708
3709   // needed if different viewport properties defined for playing
3710   ChangeViewportPropertiesIfNeeded();
3711
3712   ClearField();
3713
3714   DrawCompleteVideoDisplay();
3715
3716   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3717
3718   InitGameEngine();
3719   InitGameControlValues();
3720
3721   if (tape.recording)
3722   {
3723     // initialize tape actions from game when recording tape
3724     tape.use_key_actions   = game.use_key_actions;
3725     tape.use_mouse_actions = game.use_mouse_actions;
3726
3727     // initialize visible playfield size when recording tape (for team mode)
3728     tape.scr_fieldx = SCR_FIELDX;
3729     tape.scr_fieldy = SCR_FIELDY;
3730   }
3731
3732   // don't play tapes over network
3733   network_playing = (network.enabled && !tape.playing);
3734
3735   for (i = 0; i < MAX_PLAYERS; i++)
3736   {
3737     struct PlayerInfo *player = &stored_player[i];
3738
3739     player->index_nr = i;
3740     player->index_bit = (1 << i);
3741     player->element_nr = EL_PLAYER_1 + i;
3742
3743     player->present = FALSE;
3744     player->active = FALSE;
3745     player->mapped = FALSE;
3746
3747     player->killed = FALSE;
3748     player->reanimated = FALSE;
3749     player->buried = FALSE;
3750
3751     player->action = 0;
3752     player->effective_action = 0;
3753     player->programmed_action = 0;
3754     player->snap_action = 0;
3755
3756     player->mouse_action.lx = 0;
3757     player->mouse_action.ly = 0;
3758     player->mouse_action.button = 0;
3759     player->mouse_action.button_hint = 0;
3760
3761     player->effective_mouse_action.lx = 0;
3762     player->effective_mouse_action.ly = 0;
3763     player->effective_mouse_action.button = 0;
3764     player->effective_mouse_action.button_hint = 0;
3765
3766     for (j = 0; j < MAX_NUM_KEYS; j++)
3767       player->key[j] = FALSE;
3768
3769     player->num_white_keys = 0;
3770
3771     player->dynabomb_count = 0;
3772     player->dynabomb_size = 1;
3773     player->dynabombs_left = 0;
3774     player->dynabomb_xl = FALSE;
3775
3776     player->MovDir = initial_move_dir;
3777     player->MovPos = 0;
3778     player->GfxPos = 0;
3779     player->GfxDir = initial_move_dir;
3780     player->GfxAction = ACTION_DEFAULT;
3781     player->Frame = 0;
3782     player->StepFrame = 0;
3783
3784     player->initial_element = player->element_nr;
3785     player->artwork_element =
3786       (level.use_artwork_element[i] ? level.artwork_element[i] :
3787        player->element_nr);
3788     player->use_murphy = FALSE;
3789
3790     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3791     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3792
3793     player->gravity = level.initial_player_gravity[i];
3794
3795     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3796
3797     player->actual_frame_counter.count = 0;
3798     player->actual_frame_counter.value = 1;
3799
3800     player->step_counter = 0;
3801
3802     player->last_move_dir = initial_move_dir;
3803
3804     player->is_active = FALSE;
3805
3806     player->is_waiting = FALSE;
3807     player->is_moving = FALSE;
3808     player->is_auto_moving = FALSE;
3809     player->is_digging = FALSE;
3810     player->is_snapping = FALSE;
3811     player->is_collecting = FALSE;
3812     player->is_pushing = FALSE;
3813     player->is_switching = FALSE;
3814     player->is_dropping = FALSE;
3815     player->is_dropping_pressed = FALSE;
3816
3817     player->is_bored = FALSE;
3818     player->is_sleeping = FALSE;
3819
3820     player->was_waiting = TRUE;
3821     player->was_moving = FALSE;
3822     player->was_snapping = FALSE;
3823     player->was_dropping = FALSE;
3824
3825     player->force_dropping = FALSE;
3826
3827     player->frame_counter_bored = -1;
3828     player->frame_counter_sleeping = -1;
3829
3830     player->anim_delay_counter = 0;
3831     player->post_delay_counter = 0;
3832
3833     player->dir_waiting = initial_move_dir;
3834     player->action_waiting = ACTION_DEFAULT;
3835     player->last_action_waiting = ACTION_DEFAULT;
3836     player->special_action_bored = ACTION_DEFAULT;
3837     player->special_action_sleeping = ACTION_DEFAULT;
3838
3839     player->switch_x = -1;
3840     player->switch_y = -1;
3841
3842     player->drop_x = -1;
3843     player->drop_y = -1;
3844
3845     player->show_envelope = 0;
3846
3847     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3848
3849     player->push_delay       = -1;      // initialized when pushing starts
3850     player->push_delay_value = game.initial_push_delay_value;
3851
3852     player->drop_delay = 0;
3853     player->drop_pressed_delay = 0;
3854
3855     player->last_jx = -1;
3856     player->last_jy = -1;
3857     player->jx = -1;
3858     player->jy = -1;
3859
3860     player->shield_normal_time_left = 0;
3861     player->shield_deadly_time_left = 0;
3862
3863     player->last_removed_element = EL_UNDEFINED;
3864
3865     player->inventory_infinite_element = EL_UNDEFINED;
3866     player->inventory_size = 0;
3867
3868     if (level.use_initial_inventory[i])
3869     {
3870       for (j = 0; j < level.initial_inventory_size[i]; j++)
3871       {
3872         int element = level.initial_inventory_content[i][j];
3873         int collect_count = element_info[element].collect_count_initial;
3874         int k;
3875
3876         if (!IS_CUSTOM_ELEMENT(element))
3877           collect_count = 1;
3878
3879         if (collect_count == 0)
3880           player->inventory_infinite_element = element;
3881         else
3882           for (k = 0; k < collect_count; k++)
3883             if (player->inventory_size < MAX_INVENTORY_SIZE)
3884               player->inventory_element[player->inventory_size++] = element;
3885       }
3886     }
3887
3888     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3889     SnapField(player, 0, 0);
3890
3891     map_player_action[i] = i;
3892   }
3893
3894   network_player_action_received = FALSE;
3895
3896   // initial null action
3897   if (network_playing)
3898     SendToServer_MovePlayer(MV_NONE);
3899
3900   FrameCounter = 0;
3901   TimeFrames = 0;
3902   TimePlayed = 0;
3903   TimeLeft = level.time;
3904
3905   TapeTimeFrames = 0;
3906   TapeTime = 0;
3907
3908   ScreenMovDir = MV_NONE;
3909   ScreenMovPos = 0;
3910   ScreenGfxPos = 0;
3911
3912   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3913
3914   game.robot_wheel_x = -1;
3915   game.robot_wheel_y = -1;
3916
3917   game.exit_x = -1;
3918   game.exit_y = -1;
3919
3920   game.all_players_gone = FALSE;
3921
3922   game.LevelSolved = FALSE;
3923   game.GameOver = FALSE;
3924
3925   game.GamePlayed = !tape.playing;
3926
3927   game.LevelSolved_GameWon = FALSE;
3928   game.LevelSolved_GameEnd = FALSE;
3929   game.LevelSolved_SaveTape = FALSE;
3930   game.LevelSolved_SaveScore = FALSE;
3931
3932   game.LevelSolved_CountingTime = 0;
3933   game.LevelSolved_CountingScore = 0;
3934   game.LevelSolved_CountingHealth = 0;
3935
3936   game.RestartGameRequested = FALSE;
3937
3938   game.panel.active = TRUE;
3939
3940   game.no_level_time_limit = (level.time == 0);
3941   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3942
3943   game.yamyam_content_nr = 0;
3944   game.robot_wheel_active = FALSE;
3945   game.magic_wall_active = FALSE;
3946   game.magic_wall_time_left = 0;
3947   game.light_time_left = 0;
3948   game.timegate_time_left = 0;
3949   game.switchgate_pos = 0;
3950   game.wind_direction = level.wind_direction_initial;
3951
3952   game.time_final = 0;
3953   game.score_time_final = 0;
3954
3955   game.score = 0;
3956   game.score_final = 0;
3957
3958   game.health = MAX_HEALTH;
3959   game.health_final = MAX_HEALTH;
3960
3961   game.gems_still_needed = level.gems_needed;
3962   game.sokoban_fields_still_needed = 0;
3963   game.sokoban_objects_still_needed = 0;
3964   game.lights_still_needed = 0;
3965   game.players_still_needed = 0;
3966   game.friends_still_needed = 0;
3967
3968   game.lenses_time_left = 0;
3969   game.magnify_time_left = 0;
3970
3971   game.ball_active = level.ball_active_initial;
3972   game.ball_content_nr = 0;
3973
3974   game.explosions_delayed = TRUE;
3975
3976   game.envelope_active = FALSE;
3977
3978   // special case: set custom artwork setting to initial value
3979   game.use_masked_elements = game.use_masked_elements_initial;
3980
3981   for (i = 0; i < NUM_BELTS; i++)
3982   {
3983     game.belt_dir[i] = MV_NONE;
3984     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3985   }
3986
3987   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3988     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3989
3990 #if DEBUG_INIT_PLAYER
3991   DebugPrintPlayerStatus("Player status at level initialization");
3992 #endif
3993
3994   SCAN_PLAYFIELD(x, y)
3995   {
3996     Tile[x][y] = Last[x][y] = level.field[x][y];
3997     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3998     ChangeDelay[x][y] = 0;
3999     ChangePage[x][y] = -1;
4000     CustomValue[x][y] = 0;              // initialized in InitField()
4001     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4002     AmoebaNr[x][y] = 0;
4003     WasJustMoving[x][y] = 0;
4004     WasJustFalling[x][y] = 0;
4005     CheckCollision[x][y] = 0;
4006     CheckImpact[x][y] = 0;
4007     Stop[x][y] = FALSE;
4008     Pushed[x][y] = FALSE;
4009
4010     ChangeCount[x][y] = 0;
4011     ChangeEvent[x][y] = -1;
4012
4013     ExplodePhase[x][y] = 0;
4014     ExplodeDelay[x][y] = 0;
4015     ExplodeField[x][y] = EX_TYPE_NONE;
4016
4017     RunnerVisit[x][y] = 0;
4018     PlayerVisit[x][y] = 0;
4019
4020     GfxFrame[x][y] = 0;
4021     GfxRandom[x][y] = INIT_GFX_RANDOM();
4022     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4023     GfxElement[x][y] = EL_UNDEFINED;
4024     GfxElementEmpty[x][y] = EL_EMPTY;
4025     GfxAction[x][y] = ACTION_DEFAULT;
4026     GfxDir[x][y] = MV_NONE;
4027     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4028   }
4029
4030   SCAN_PLAYFIELD(x, y)
4031   {
4032     InitFieldForEngine(x, y);
4033
4034     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4035       emulate_bd = FALSE;
4036     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4037       emulate_sp = FALSE;
4038
4039     InitField(x, y, TRUE);
4040
4041     ResetGfxAnimation(x, y);
4042   }
4043
4044   InitBeltMovement();
4045
4046   // required if level does not contain any "empty space" element
4047   if (element_info[EL_EMPTY].use_gfx_element)
4048     game.use_masked_elements = TRUE;
4049
4050   for (i = 0; i < MAX_PLAYERS; i++)
4051   {
4052     struct PlayerInfo *player = &stored_player[i];
4053
4054     // set number of special actions for bored and sleeping animation
4055     player->num_special_action_bored =
4056       get_num_special_action(player->artwork_element,
4057                              ACTION_BORING_1, ACTION_BORING_LAST);
4058     player->num_special_action_sleeping =
4059       get_num_special_action(player->artwork_element,
4060                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4061   }
4062
4063   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4064                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4065
4066   // initialize type of slippery elements
4067   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4068   {
4069     if (!IS_CUSTOM_ELEMENT(i))
4070     {
4071       // default: elements slip down either to the left or right randomly
4072       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4073
4074       // SP style elements prefer to slip down on the left side
4075       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4076         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4077
4078       // BD style elements prefer to slip down on the left side
4079       if (game.emulation == EMU_BOULDERDASH)
4080         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4081     }
4082   }
4083
4084   // initialize explosion and ignition delay
4085   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4086   {
4087     if (!IS_CUSTOM_ELEMENT(i))
4088     {
4089       int num_phase = 8;
4090       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4091                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4092                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4093       int last_phase = (num_phase + 1) * delay;
4094       int half_phase = (num_phase / 2) * delay;
4095
4096       element_info[i].explosion_delay = last_phase - 1;
4097       element_info[i].ignition_delay = half_phase;
4098
4099       if (i == EL_BLACK_ORB)
4100         element_info[i].ignition_delay = 1;
4101     }
4102   }
4103
4104   // correct non-moving belts to start moving left
4105   for (i = 0; i < NUM_BELTS; i++)
4106     if (game.belt_dir[i] == MV_NONE)
4107       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4108
4109 #if USE_NEW_PLAYER_ASSIGNMENTS
4110   // use preferred player also in local single-player mode
4111   if (!network.enabled && !game.team_mode)
4112   {
4113     int new_index_nr = setup.network_player_nr;
4114
4115     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4116     {
4117       for (i = 0; i < MAX_PLAYERS; i++)
4118         stored_player[i].connected_locally = FALSE;
4119
4120       stored_player[new_index_nr].connected_locally = TRUE;
4121     }
4122   }
4123
4124   for (i = 0; i < MAX_PLAYERS; i++)
4125   {
4126     stored_player[i].connected = FALSE;
4127
4128     // in network game mode, the local player might not be the first player
4129     if (stored_player[i].connected_locally)
4130       local_player = &stored_player[i];
4131   }
4132
4133   if (!network.enabled)
4134     local_player->connected = TRUE;
4135
4136   if (tape.playing)
4137   {
4138     for (i = 0; i < MAX_PLAYERS; i++)
4139       stored_player[i].connected = tape.player_participates[i];
4140   }
4141   else if (network.enabled)
4142   {
4143     // add team mode players connected over the network (needed for correct
4144     // assignment of player figures from level to locally playing players)
4145
4146     for (i = 0; i < MAX_PLAYERS; i++)
4147       if (stored_player[i].connected_network)
4148         stored_player[i].connected = TRUE;
4149   }
4150   else if (game.team_mode)
4151   {
4152     // try to guess locally connected team mode players (needed for correct
4153     // assignment of player figures from level to locally playing players)
4154
4155     for (i = 0; i < MAX_PLAYERS; i++)
4156       if (setup.input[i].use_joystick ||
4157           setup.input[i].key.left != KSYM_UNDEFINED)
4158         stored_player[i].connected = TRUE;
4159   }
4160
4161 #if DEBUG_INIT_PLAYER
4162   DebugPrintPlayerStatus("Player status after level initialization");
4163 #endif
4164
4165 #if DEBUG_INIT_PLAYER
4166   Debug("game:init:player", "Reassigning players ...");
4167 #endif
4168
4169   // check if any connected player was not found in playfield
4170   for (i = 0; i < MAX_PLAYERS; i++)
4171   {
4172     struct PlayerInfo *player = &stored_player[i];
4173
4174     if (player->connected && !player->present)
4175     {
4176       struct PlayerInfo *field_player = NULL;
4177
4178 #if DEBUG_INIT_PLAYER
4179       Debug("game:init:player",
4180             "- looking for field player for player %d ...", i + 1);
4181 #endif
4182
4183       // assign first free player found that is present in the playfield
4184
4185       // first try: look for unmapped playfield player that is not connected
4186       for (j = 0; j < MAX_PLAYERS; j++)
4187         if (field_player == NULL &&
4188             stored_player[j].present &&
4189             !stored_player[j].mapped &&
4190             !stored_player[j].connected)
4191           field_player = &stored_player[j];
4192
4193       // second try: look for *any* unmapped playfield player
4194       for (j = 0; j < MAX_PLAYERS; j++)
4195         if (field_player == NULL &&
4196             stored_player[j].present &&
4197             !stored_player[j].mapped)
4198           field_player = &stored_player[j];
4199
4200       if (field_player != NULL)
4201       {
4202         int jx = field_player->jx, jy = field_player->jy;
4203
4204 #if DEBUG_INIT_PLAYER
4205         Debug("game:init:player", "- found player %d",
4206               field_player->index_nr + 1);
4207 #endif
4208
4209         player->present = FALSE;
4210         player->active = FALSE;
4211
4212         field_player->present = TRUE;
4213         field_player->active = TRUE;
4214
4215         /*
4216         player->initial_element = field_player->initial_element;
4217         player->artwork_element = field_player->artwork_element;
4218
4219         player->block_last_field       = field_player->block_last_field;
4220         player->block_delay_adjustment = field_player->block_delay_adjustment;
4221         */
4222
4223         StorePlayer[jx][jy] = field_player->element_nr;
4224
4225         field_player->jx = field_player->last_jx = jx;
4226         field_player->jy = field_player->last_jy = jy;
4227
4228         if (local_player == player)
4229           local_player = field_player;
4230
4231         map_player_action[field_player->index_nr] = i;
4232
4233         field_player->mapped = TRUE;
4234
4235 #if DEBUG_INIT_PLAYER
4236         Debug("game:init:player", "- map_player_action[%d] == %d",
4237               field_player->index_nr + 1, i + 1);
4238 #endif
4239       }
4240     }
4241
4242     if (player->connected && player->present)
4243       player->mapped = TRUE;
4244   }
4245
4246 #if DEBUG_INIT_PLAYER
4247   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4248 #endif
4249
4250 #else
4251
4252   // check if any connected player was not found in playfield
4253   for (i = 0; i < MAX_PLAYERS; i++)
4254   {
4255     struct PlayerInfo *player = &stored_player[i];
4256
4257     if (player->connected && !player->present)
4258     {
4259       for (j = 0; j < MAX_PLAYERS; j++)
4260       {
4261         struct PlayerInfo *field_player = &stored_player[j];
4262         int jx = field_player->jx, jy = field_player->jy;
4263
4264         // assign first free player found that is present in the playfield
4265         if (field_player->present && !field_player->connected)
4266         {
4267           player->present = TRUE;
4268           player->active = TRUE;
4269
4270           field_player->present = FALSE;
4271           field_player->active = FALSE;
4272
4273           player->initial_element = field_player->initial_element;
4274           player->artwork_element = field_player->artwork_element;
4275
4276           player->block_last_field       = field_player->block_last_field;
4277           player->block_delay_adjustment = field_player->block_delay_adjustment;
4278
4279           StorePlayer[jx][jy] = player->element_nr;
4280
4281           player->jx = player->last_jx = jx;
4282           player->jy = player->last_jy = jy;
4283
4284           break;
4285         }
4286       }
4287     }
4288   }
4289 #endif
4290
4291 #if 0
4292   Debug("game:init:player", "local_player->present == %d",
4293         local_player->present);
4294 #endif
4295
4296   // set focus to local player for network games, else to all players
4297   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4298   game.centered_player_nr_next = game.centered_player_nr;
4299   game.set_centered_player = FALSE;
4300   game.set_centered_player_wrap = FALSE;
4301
4302   if (network_playing && tape.recording)
4303   {
4304     // store client dependent player focus when recording network games
4305     tape.centered_player_nr_next = game.centered_player_nr_next;
4306     tape.set_centered_player = TRUE;
4307   }
4308
4309   if (tape.playing)
4310   {
4311     // when playing a tape, eliminate all players who do not participate
4312
4313 #if USE_NEW_PLAYER_ASSIGNMENTS
4314
4315     if (!game.team_mode)
4316     {
4317       for (i = 0; i < MAX_PLAYERS; i++)
4318       {
4319         if (stored_player[i].active &&
4320             !tape.player_participates[map_player_action[i]])
4321         {
4322           struct PlayerInfo *player = &stored_player[i];
4323           int jx = player->jx, jy = player->jy;
4324
4325 #if DEBUG_INIT_PLAYER
4326           Debug("game:init:player", "Removing player %d at (%d, %d)",
4327                 i + 1, jx, jy);
4328 #endif
4329
4330           player->active = FALSE;
4331           StorePlayer[jx][jy] = 0;
4332           Tile[jx][jy] = EL_EMPTY;
4333         }
4334       }
4335     }
4336
4337 #else
4338
4339     for (i = 0; i < MAX_PLAYERS; i++)
4340     {
4341       if (stored_player[i].active &&
4342           !tape.player_participates[i])
4343       {
4344         struct PlayerInfo *player = &stored_player[i];
4345         int jx = player->jx, jy = player->jy;
4346
4347         player->active = FALSE;
4348         StorePlayer[jx][jy] = 0;
4349         Tile[jx][jy] = EL_EMPTY;
4350       }
4351     }
4352 #endif
4353   }
4354   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4355   {
4356     // when in single player mode, eliminate all but the local player
4357
4358     for (i = 0; i < MAX_PLAYERS; i++)
4359     {
4360       struct PlayerInfo *player = &stored_player[i];
4361
4362       if (player->active && player != local_player)
4363       {
4364         int jx = player->jx, jy = player->jy;
4365
4366         player->active = FALSE;
4367         player->present = FALSE;
4368
4369         StorePlayer[jx][jy] = 0;
4370         Tile[jx][jy] = EL_EMPTY;
4371       }
4372     }
4373   }
4374
4375   for (i = 0; i < MAX_PLAYERS; i++)
4376     if (stored_player[i].active)
4377       game.players_still_needed++;
4378
4379   if (level.solved_by_one_player)
4380     game.players_still_needed = 1;
4381
4382   // when recording the game, store which players take part in the game
4383   if (tape.recording)
4384   {
4385 #if USE_NEW_PLAYER_ASSIGNMENTS
4386     for (i = 0; i < MAX_PLAYERS; i++)
4387       if (stored_player[i].connected)
4388         tape.player_participates[i] = TRUE;
4389 #else
4390     for (i = 0; i < MAX_PLAYERS; i++)
4391       if (stored_player[i].active)
4392         tape.player_participates[i] = TRUE;
4393 #endif
4394   }
4395
4396 #if DEBUG_INIT_PLAYER
4397   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4398 #endif
4399
4400   if (BorderElement == EL_EMPTY)
4401   {
4402     SBX_Left = 0;
4403     SBX_Right = lev_fieldx - SCR_FIELDX;
4404     SBY_Upper = 0;
4405     SBY_Lower = lev_fieldy - SCR_FIELDY;
4406   }
4407   else
4408   {
4409     SBX_Left = -1;
4410     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4411     SBY_Upper = -1;
4412     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4413   }
4414
4415   if (full_lev_fieldx <= SCR_FIELDX)
4416     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4417   if (full_lev_fieldy <= SCR_FIELDY)
4418     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4419
4420   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4421     SBX_Left--;
4422   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4423     SBY_Upper--;
4424
4425   // if local player not found, look for custom element that might create
4426   // the player (make some assumptions about the right custom element)
4427   if (!local_player->present)
4428   {
4429     int start_x = 0, start_y = 0;
4430     int found_rating = 0;
4431     int found_element = EL_UNDEFINED;
4432     int player_nr = local_player->index_nr;
4433
4434     SCAN_PLAYFIELD(x, y)
4435     {
4436       int element = Tile[x][y];
4437       int content;
4438       int xx, yy;
4439       boolean is_player;
4440
4441       if (level.use_start_element[player_nr] &&
4442           level.start_element[player_nr] == element &&
4443           found_rating < 4)
4444       {
4445         start_x = x;
4446         start_y = y;
4447
4448         found_rating = 4;
4449         found_element = element;
4450       }
4451
4452       if (!IS_CUSTOM_ELEMENT(element))
4453         continue;
4454
4455       if (CAN_CHANGE(element))
4456       {
4457         for (i = 0; i < element_info[element].num_change_pages; i++)
4458         {
4459           // check for player created from custom element as single target
4460           content = element_info[element].change_page[i].target_element;
4461           is_player = IS_PLAYER_ELEMENT(content);
4462
4463           if (is_player && (found_rating < 3 ||
4464                             (found_rating == 3 && element < found_element)))
4465           {
4466             start_x = x;
4467             start_y = y;
4468
4469             found_rating = 3;
4470             found_element = element;
4471           }
4472         }
4473       }
4474
4475       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4476       {
4477         // check for player created from custom element as explosion content
4478         content = element_info[element].content.e[xx][yy];
4479         is_player = IS_PLAYER_ELEMENT(content);
4480
4481         if (is_player && (found_rating < 2 ||
4482                           (found_rating == 2 && element < found_element)))
4483         {
4484           start_x = x + xx - 1;
4485           start_y = y + yy - 1;
4486
4487           found_rating = 2;
4488           found_element = element;
4489         }
4490
4491         if (!CAN_CHANGE(element))
4492           continue;
4493
4494         for (i = 0; i < element_info[element].num_change_pages; i++)
4495         {
4496           // check for player created from custom element as extended target
4497           content =
4498             element_info[element].change_page[i].target_content.e[xx][yy];
4499
4500           is_player = IS_PLAYER_ELEMENT(content);
4501
4502           if (is_player && (found_rating < 1 ||
4503                             (found_rating == 1 && element < found_element)))
4504           {
4505             start_x = x + xx - 1;
4506             start_y = y + yy - 1;
4507
4508             found_rating = 1;
4509             found_element = element;
4510           }
4511         }
4512       }
4513     }
4514
4515     scroll_x = SCROLL_POSITION_X(start_x);
4516     scroll_y = SCROLL_POSITION_Y(start_y);
4517   }
4518   else
4519   {
4520     scroll_x = SCROLL_POSITION_X(local_player->jx);
4521     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4522   }
4523
4524   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4525     scroll_x = game.forced_scroll_x;
4526   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4527     scroll_y = game.forced_scroll_y;
4528
4529   // !!! FIX THIS (START) !!!
4530   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4531   {
4532     InitGameEngine_BD();
4533   }
4534   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4535   {
4536     InitGameEngine_EM();
4537   }
4538   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4539   {
4540     InitGameEngine_SP();
4541   }
4542   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4543   {
4544     InitGameEngine_MM();
4545   }
4546   else
4547   {
4548     DrawLevel(REDRAW_FIELD);
4549     DrawAllPlayers();
4550
4551     // after drawing the level, correct some elements
4552     if (game.timegate_time_left == 0)
4553       CloseAllOpenTimegates();
4554   }
4555
4556   // blit playfield from scroll buffer to normal back buffer for fading in
4557   BlitScreenToBitmap(backbuffer);
4558   // !!! FIX THIS (END) !!!
4559
4560   DrawMaskedBorder(fade_mask);
4561
4562   FadeIn(fade_mask);
4563
4564 #if 1
4565   // full screen redraw is required at this point in the following cases:
4566   // - special editor door undrawn when game was started from level editor
4567   // - drawing area (playfield) was changed and has to be removed completely
4568   redraw_mask = REDRAW_ALL;
4569   BackToFront();
4570 #endif
4571
4572   if (!game.restart_level)
4573   {
4574     // copy default game door content to main double buffer
4575
4576     // !!! CHECK AGAIN !!!
4577     SetPanelBackground();
4578     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4579     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4580   }
4581
4582   SetPanelBackground();
4583   SetDrawBackgroundMask(REDRAW_DOOR_1);
4584
4585   UpdateAndDisplayGameControlValues();
4586
4587   if (!game.restart_level)
4588   {
4589     UnmapGameButtons();
4590     UnmapTapeButtons();
4591
4592     FreeGameButtons();
4593     CreateGameButtons();
4594
4595     MapGameButtons();
4596     MapTapeButtons();
4597
4598     // copy actual game door content to door double buffer for OpenDoor()
4599     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4600
4601     OpenDoor(DOOR_OPEN_ALL);
4602
4603     KeyboardAutoRepeatOffUnlessAutoplay();
4604
4605 #if DEBUG_INIT_PLAYER
4606     DebugPrintPlayerStatus("Player status (final)");
4607 #endif
4608   }
4609
4610   UnmapAllGadgets();
4611
4612   MapGameButtons();
4613   MapTapeButtons();
4614
4615   if (!game.restart_level && !tape.playing)
4616   {
4617     LevelStats_incPlayed(level_nr);
4618
4619     SaveLevelSetup_SeriesInfo();
4620   }
4621
4622   game.restart_level = FALSE;
4623   game.request_active = FALSE;
4624
4625   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4626     InitGameActions_MM();
4627
4628   SaveEngineSnapshotToListInitial();
4629
4630   if (!game.restart_level)
4631   {
4632     PlaySound(SND_GAME_STARTING);
4633
4634     if (setup.sound_music)
4635       PlayLevelMusic();
4636   }
4637
4638   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4639 }
4640
4641 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4642                         int actual_player_x, int actual_player_y)
4643 {
4644   // this is used for non-R'n'D game engines to update certain engine values
4645
4646   // needed to determine if sounds are played within the visible screen area
4647   scroll_x = actual_scroll_x;
4648   scroll_y = actual_scroll_y;
4649
4650   // needed to get player position for "follow finger" playing input method
4651   local_player->jx = actual_player_x;
4652   local_player->jy = actual_player_y;
4653 }
4654
4655 void InitMovDir(int x, int y)
4656 {
4657   int i, element = Tile[x][y];
4658   static int xy[4][2] =
4659   {
4660     {  0, +1 },
4661     { +1,  0 },
4662     {  0, -1 },
4663     { -1,  0 }
4664   };
4665   static int direction[3][4] =
4666   {
4667     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4668     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4669     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4670   };
4671
4672   switch (element)
4673   {
4674     case EL_BUG_RIGHT:
4675     case EL_BUG_UP:
4676     case EL_BUG_LEFT:
4677     case EL_BUG_DOWN:
4678       Tile[x][y] = EL_BUG;
4679       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4680       break;
4681
4682     case EL_SPACESHIP_RIGHT:
4683     case EL_SPACESHIP_UP:
4684     case EL_SPACESHIP_LEFT:
4685     case EL_SPACESHIP_DOWN:
4686       Tile[x][y] = EL_SPACESHIP;
4687       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4688       break;
4689
4690     case EL_BD_BUTTERFLY_RIGHT:
4691     case EL_BD_BUTTERFLY_UP:
4692     case EL_BD_BUTTERFLY_LEFT:
4693     case EL_BD_BUTTERFLY_DOWN:
4694       Tile[x][y] = EL_BD_BUTTERFLY;
4695       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4696       break;
4697
4698     case EL_BD_FIREFLY_RIGHT:
4699     case EL_BD_FIREFLY_UP:
4700     case EL_BD_FIREFLY_LEFT:
4701     case EL_BD_FIREFLY_DOWN:
4702       Tile[x][y] = EL_BD_FIREFLY;
4703       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4704       break;
4705
4706     case EL_PACMAN_RIGHT:
4707     case EL_PACMAN_UP:
4708     case EL_PACMAN_LEFT:
4709     case EL_PACMAN_DOWN:
4710       Tile[x][y] = EL_PACMAN;
4711       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4712       break;
4713
4714     case EL_YAMYAM_LEFT:
4715     case EL_YAMYAM_RIGHT:
4716     case EL_YAMYAM_UP:
4717     case EL_YAMYAM_DOWN:
4718       Tile[x][y] = EL_YAMYAM;
4719       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4720       break;
4721
4722     case EL_SP_SNIKSNAK:
4723       MovDir[x][y] = MV_UP;
4724       break;
4725
4726     case EL_SP_ELECTRON:
4727       MovDir[x][y] = MV_LEFT;
4728       break;
4729
4730     case EL_MOLE_LEFT:
4731     case EL_MOLE_RIGHT:
4732     case EL_MOLE_UP:
4733     case EL_MOLE_DOWN:
4734       Tile[x][y] = EL_MOLE;
4735       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4736       break;
4737
4738     case EL_SPRING_LEFT:
4739     case EL_SPRING_RIGHT:
4740       Tile[x][y] = EL_SPRING;
4741       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4742       break;
4743
4744     default:
4745       if (IS_CUSTOM_ELEMENT(element))
4746       {
4747         struct ElementInfo *ei = &element_info[element];
4748         int move_direction_initial = ei->move_direction_initial;
4749         int move_pattern = ei->move_pattern;
4750
4751         if (move_direction_initial == MV_START_PREVIOUS)
4752         {
4753           if (MovDir[x][y] != MV_NONE)
4754             return;
4755
4756           move_direction_initial = MV_START_AUTOMATIC;
4757         }
4758
4759         if (move_direction_initial == MV_START_RANDOM)
4760           MovDir[x][y] = 1 << RND(4);
4761         else if (move_direction_initial & MV_ANY_DIRECTION)
4762           MovDir[x][y] = move_direction_initial;
4763         else if (move_pattern == MV_ALL_DIRECTIONS ||
4764                  move_pattern == MV_TURNING_LEFT ||
4765                  move_pattern == MV_TURNING_RIGHT ||
4766                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4767                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4768                  move_pattern == MV_TURNING_RANDOM)
4769           MovDir[x][y] = 1 << RND(4);
4770         else if (move_pattern == MV_HORIZONTAL)
4771           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4772         else if (move_pattern == MV_VERTICAL)
4773           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4774         else if (move_pattern & MV_ANY_DIRECTION)
4775           MovDir[x][y] = element_info[element].move_pattern;
4776         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4777                  move_pattern == MV_ALONG_RIGHT_SIDE)
4778         {
4779           // use random direction as default start direction
4780           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4781             MovDir[x][y] = 1 << RND(4);
4782
4783           for (i = 0; i < NUM_DIRECTIONS; i++)
4784           {
4785             int x1 = x + xy[i][0];
4786             int y1 = y + xy[i][1];
4787
4788             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4789             {
4790               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4791                 MovDir[x][y] = direction[0][i];
4792               else
4793                 MovDir[x][y] = direction[1][i];
4794
4795               break;
4796             }
4797           }
4798         }                
4799       }
4800       else
4801       {
4802         MovDir[x][y] = 1 << RND(4);
4803
4804         if (element != EL_BUG &&
4805             element != EL_SPACESHIP &&
4806             element != EL_BD_BUTTERFLY &&
4807             element != EL_BD_FIREFLY)
4808           break;
4809
4810         for (i = 0; i < NUM_DIRECTIONS; i++)
4811         {
4812           int x1 = x + xy[i][0];
4813           int y1 = y + xy[i][1];
4814
4815           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4816           {
4817             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4818             {
4819               MovDir[x][y] = direction[0][i];
4820               break;
4821             }
4822             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4823                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4824             {
4825               MovDir[x][y] = direction[1][i];
4826               break;
4827             }
4828           }
4829         }
4830       }
4831       break;
4832   }
4833
4834   GfxDir[x][y] = MovDir[x][y];
4835 }
4836
4837 void InitAmoebaNr(int x, int y)
4838 {
4839   int i;
4840   int group_nr = AmoebaNeighbourNr(x, y);
4841
4842   if (group_nr == 0)
4843   {
4844     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4845     {
4846       if (AmoebaCnt[i] == 0)
4847       {
4848         group_nr = i;
4849         break;
4850       }
4851     }
4852   }
4853
4854   AmoebaNr[x][y] = group_nr;
4855   AmoebaCnt[group_nr]++;
4856   AmoebaCnt2[group_nr]++;
4857 }
4858
4859 static void LevelSolved_SetFinalGameValues(void)
4860 {
4861   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4862                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4863   game.score_time_final = (level.use_step_counter ? TimePlayed :
4864                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4865
4866   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4867                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4868                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4869                       game.score);
4870
4871   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4872                        MM_HEALTH(game_mm.laser_overload_value) :
4873                        game.health);
4874
4875   game.LevelSolved_CountingTime = game.time_final;
4876   game.LevelSolved_CountingScore = game.score_final;
4877   game.LevelSolved_CountingHealth = game.health_final;
4878 }
4879
4880 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4881 {
4882   game.LevelSolved_CountingTime = time;
4883   game.LevelSolved_CountingScore = score;
4884   game.LevelSolved_CountingHealth = health;
4885
4886   game_panel_controls[GAME_PANEL_TIME].value = time;
4887   game_panel_controls[GAME_PANEL_SCORE].value = score;
4888   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4889
4890   DisplayGameControlValues();
4891 }
4892
4893 static void LevelSolved(void)
4894 {
4895   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4896       game.players_still_needed > 0)
4897     return;
4898
4899   game.LevelSolved = TRUE;
4900   game.GameOver = TRUE;
4901
4902   tape.solved = TRUE;
4903
4904   // needed here to display correct panel values while player walks into exit
4905   LevelSolved_SetFinalGameValues();
4906 }
4907
4908 void GameWon(void)
4909 {
4910   static int time_count_steps;
4911   static int time, time_final;
4912   static float score, score_final; // needed for time score < 10 for 10 seconds
4913   static int health, health_final;
4914   static int game_over_delay_1 = 0;
4915   static int game_over_delay_2 = 0;
4916   static int game_over_delay_3 = 0;
4917   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4918   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4919
4920   if (!game.LevelSolved_GameWon)
4921   {
4922     int i;
4923
4924     // do not start end game actions before the player stops moving (to exit)
4925     if (local_player->active && local_player->MovPos)
4926       return;
4927
4928     // calculate final game values after player finished walking into exit
4929     LevelSolved_SetFinalGameValues();
4930
4931     game.LevelSolved_GameWon = TRUE;
4932     game.LevelSolved_SaveTape = tape.recording;
4933     game.LevelSolved_SaveScore = !tape.playing;
4934
4935     if (!tape.playing)
4936     {
4937       LevelStats_incSolved(level_nr);
4938
4939       SaveLevelSetup_SeriesInfo();
4940     }
4941
4942     if (tape.auto_play)         // tape might already be stopped here
4943       tape.auto_play_level_solved = TRUE;
4944
4945     TapeStop();
4946
4947     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4948     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4949     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4950
4951     time = time_final = game.time_final;
4952     score = score_final = game.score_final;
4953     health = health_final = game.health_final;
4954
4955     // update game panel values before (delayed) counting of score (if any)
4956     LevelSolved_DisplayFinalGameValues(time, score, health);
4957
4958     // if level has time score defined, calculate new final game values
4959     if (time_score > 0)
4960     {
4961       int time_final_max = 999;
4962       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4963       int time_frames = 0;
4964       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4965       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4966
4967       if (TimeLeft > 0)
4968       {
4969         time_final = 0;
4970         time_frames = time_frames_left;
4971       }
4972       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4973       {
4974         time_final = time_final_max;
4975         time_frames = time_frames_final_max - time_frames_played;
4976       }
4977
4978       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4979
4980       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4981
4982       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4983       {
4984         // keep previous values (final values already processed here)
4985         time_final = time;
4986         score_final = score;
4987       }
4988       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4989       {
4990         health_final = 0;
4991         score_final += health * time_score;
4992       }
4993
4994       game.score_final = score_final;
4995       game.health_final = health_final;
4996     }
4997
4998     // if not counting score after game, immediately update game panel values
4999     if (level_editor_test_game || !setup.count_score_after_game ||
5000         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5001     {
5002       time = time_final;
5003       score = score_final;
5004
5005       LevelSolved_DisplayFinalGameValues(time, score, health);
5006     }
5007
5008     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5009     {
5010       // check if last player has left the level
5011       if (game.exit_x >= 0 &&
5012           game.exit_y >= 0)
5013       {
5014         int x = game.exit_x;
5015         int y = game.exit_y;
5016         int element = Tile[x][y];
5017
5018         // close exit door after last player
5019         if ((game.all_players_gone &&
5020              (element == EL_EXIT_OPEN ||
5021               element == EL_SP_EXIT_OPEN ||
5022               element == EL_STEEL_EXIT_OPEN)) ||
5023             element == EL_EM_EXIT_OPEN ||
5024             element == EL_EM_STEEL_EXIT_OPEN)
5025         {
5026
5027           Tile[x][y] =
5028             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5029              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5030              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5031              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5032              EL_EM_STEEL_EXIT_CLOSING);
5033
5034           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5035         }
5036
5037         // player disappears
5038         DrawLevelField(x, y);
5039       }
5040
5041       for (i = 0; i < MAX_PLAYERS; i++)
5042       {
5043         struct PlayerInfo *player = &stored_player[i];
5044
5045         if (player->present)
5046         {
5047           RemovePlayer(player);
5048
5049           // player disappears
5050           DrawLevelField(player->jx, player->jy);
5051         }
5052       }
5053     }
5054
5055     PlaySound(SND_GAME_WINNING);
5056   }
5057
5058   if (setup.count_score_after_game)
5059   {
5060     if (time != time_final)
5061     {
5062       if (game_over_delay_1 > 0)
5063       {
5064         game_over_delay_1--;
5065
5066         return;
5067       }
5068
5069       int time_to_go = ABS(time_final - time);
5070       int time_count_dir = (time < time_final ? +1 : -1);
5071
5072       if (time_to_go < time_count_steps)
5073         time_count_steps = 1;
5074
5075       time  += time_count_steps * time_count_dir;
5076       score += time_count_steps * time_score;
5077
5078       // set final score to correct rounding differences after counting score
5079       if (time == time_final)
5080         score = score_final;
5081
5082       LevelSolved_DisplayFinalGameValues(time, score, health);
5083
5084       if (time == time_final)
5085         StopSound(SND_GAME_LEVELTIME_BONUS);
5086       else if (setup.sound_loops)
5087         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5088       else
5089         PlaySound(SND_GAME_LEVELTIME_BONUS);
5090
5091       return;
5092     }
5093
5094     if (health != health_final)
5095     {
5096       if (game_over_delay_2 > 0)
5097       {
5098         game_over_delay_2--;
5099
5100         return;
5101       }
5102
5103       int health_count_dir = (health < health_final ? +1 : -1);
5104
5105       health += health_count_dir;
5106       score  += time_score;
5107
5108       LevelSolved_DisplayFinalGameValues(time, score, health);
5109
5110       if (health == health_final)
5111         StopSound(SND_GAME_LEVELTIME_BONUS);
5112       else if (setup.sound_loops)
5113         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5114       else
5115         PlaySound(SND_GAME_LEVELTIME_BONUS);
5116
5117       return;
5118     }
5119   }
5120
5121   game.panel.active = FALSE;
5122
5123   if (game_over_delay_3 > 0)
5124   {
5125     game_over_delay_3--;
5126
5127     return;
5128   }
5129
5130   GameEnd();
5131 }
5132
5133 void GameEnd(void)
5134 {
5135   // used instead of "level_nr" (needed for network games)
5136   int last_level_nr = levelset.level_nr;
5137   boolean tape_saved = FALSE;
5138
5139   game.LevelSolved_GameEnd = TRUE;
5140
5141   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5142   {
5143     // make sure that request dialog to save tape does not open door again
5144     if (!global.use_envelope_request)
5145       CloseDoor(DOOR_CLOSE_1);
5146
5147     // ask to save tape
5148     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5149
5150     // set unique basename for score tape (also saved in high score table)
5151     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5152   }
5153
5154   // if no tape is to be saved, close both doors simultaneously
5155   CloseDoor(DOOR_CLOSE_ALL);
5156
5157   if (level_editor_test_game || score_info_tape_play)
5158   {
5159     SetGameStatus(GAME_MODE_MAIN);
5160
5161     DrawMainMenu();
5162
5163     return;
5164   }
5165
5166   if (!game.LevelSolved_SaveScore)
5167   {
5168     SetGameStatus(GAME_MODE_MAIN);
5169
5170     DrawMainMenu();
5171
5172     return;
5173   }
5174
5175   if (level_nr == leveldir_current->handicap_level)
5176   {
5177     leveldir_current->handicap_level++;
5178
5179     SaveLevelSetup_SeriesInfo();
5180   }
5181
5182   // save score and score tape before potentially erasing tape below
5183   NewHighScore(last_level_nr, tape_saved);
5184
5185   if (setup.increment_levels &&
5186       level_nr < leveldir_current->last_level &&
5187       !network_playing)
5188   {
5189     level_nr++;         // advance to next level
5190     TapeErase();        // start with empty tape
5191
5192     if (setup.auto_play_next_level)
5193     {
5194       scores.continue_playing = TRUE;
5195       scores.next_level_nr = level_nr;
5196
5197       LoadLevel(level_nr);
5198
5199       SaveLevelSetup_SeriesInfo();
5200     }
5201   }
5202
5203   if (scores.last_added >= 0 && setup.show_scores_after_game)
5204   {
5205     SetGameStatus(GAME_MODE_SCORES);
5206
5207     DrawHallOfFame(last_level_nr);
5208   }
5209   else if (scores.continue_playing)
5210   {
5211     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5212   }
5213   else
5214   {
5215     SetGameStatus(GAME_MODE_MAIN);
5216
5217     DrawMainMenu();
5218   }
5219 }
5220
5221 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5222                          boolean one_score_entry_per_name)
5223 {
5224   int i;
5225
5226   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5227     return -1;
5228
5229   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5230   {
5231     struct ScoreEntry *entry = &list->entry[i];
5232     boolean score_is_better = (new_entry->score >  entry->score);
5233     boolean score_is_equal  = (new_entry->score == entry->score);
5234     boolean time_is_better  = (new_entry->time  <  entry->time);
5235     boolean time_is_equal   = (new_entry->time  == entry->time);
5236     boolean better_by_score = (score_is_better ||
5237                                (score_is_equal && time_is_better));
5238     boolean better_by_time  = (time_is_better ||
5239                                (time_is_equal && score_is_better));
5240     boolean is_better = (level.rate_time_over_score ? better_by_time :
5241                          better_by_score);
5242     boolean entry_is_empty = (entry->score == 0 &&
5243                               entry->time == 0);
5244
5245     // prevent adding server score entries if also existing in local score file
5246     // (special case: historic score entries have an empty tape basename entry)
5247     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5248         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5249     {
5250       // add fields from server score entry not stored in local score entry
5251       // (currently, this means setting platform, version and country fields;
5252       // in rare cases, this may also correct an invalid score value, as
5253       // historic scores might have been truncated to 16-bit values locally)
5254       *entry = *new_entry;
5255
5256       return -1;
5257     }
5258
5259     if (is_better || entry_is_empty)
5260     {
5261       // player has made it to the hall of fame
5262
5263       if (i < MAX_SCORE_ENTRIES - 1)
5264       {
5265         int m = MAX_SCORE_ENTRIES - 1;
5266         int l;
5267
5268         if (one_score_entry_per_name)
5269         {
5270           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5271             if (strEqual(list->entry[l].name, new_entry->name))
5272               m = l;
5273
5274           if (m == i)   // player's new highscore overwrites his old one
5275             goto put_into_list;
5276         }
5277
5278         for (l = m; l > i; l--)
5279           list->entry[l] = list->entry[l - 1];
5280       }
5281
5282       put_into_list:
5283
5284       *entry = *new_entry;
5285
5286       return i;
5287     }
5288     else if (one_score_entry_per_name &&
5289              strEqual(entry->name, new_entry->name))
5290     {
5291       // player already in high score list with better score or time
5292
5293       return -1;
5294     }
5295   }
5296
5297   // special case: new score is beyond the last high score list position
5298   return MAX_SCORE_ENTRIES;
5299 }
5300
5301 void NewHighScore(int level_nr, boolean tape_saved)
5302 {
5303   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5304   boolean one_per_name = FALSE;
5305
5306   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5307   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5308
5309   new_entry.score = game.score_final;
5310   new_entry.time = game.score_time_final;
5311
5312   LoadScore(level_nr);
5313
5314   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5315
5316   if (scores.last_added >= MAX_SCORE_ENTRIES)
5317   {
5318     scores.last_added = MAX_SCORE_ENTRIES - 1;
5319     scores.force_last_added = TRUE;
5320
5321     scores.entry[scores.last_added] = new_entry;
5322
5323     // store last added local score entry (before merging server scores)
5324     scores.last_added_local = scores.last_added;
5325
5326     return;
5327   }
5328
5329   if (scores.last_added < 0)
5330     return;
5331
5332   SaveScore(level_nr);
5333
5334   // store last added local score entry (before merging server scores)
5335   scores.last_added_local = scores.last_added;
5336
5337   if (!game.LevelSolved_SaveTape)
5338     return;
5339
5340   SaveScoreTape(level_nr);
5341
5342   if (setup.ask_for_using_api_server)
5343   {
5344     setup.use_api_server =
5345       Request("Upload your score and tape to the high score server?", REQ_ASK);
5346
5347     if (!setup.use_api_server)
5348       Request("Not using high score server! Use setup menu to enable again!",
5349               REQ_CONFIRM);
5350
5351     runtime.use_api_server = setup.use_api_server;
5352
5353     // after asking for using API server once, do not ask again
5354     setup.ask_for_using_api_server = FALSE;
5355
5356     SaveSetup_ServerSetup();
5357   }
5358
5359   SaveServerScore(level_nr, tape_saved);
5360 }
5361
5362 void MergeServerScore(void)
5363 {
5364   struct ScoreEntry last_added_entry;
5365   boolean one_per_name = FALSE;
5366   int i;
5367
5368   if (scores.last_added >= 0)
5369     last_added_entry = scores.entry[scores.last_added];
5370
5371   for (i = 0; i < server_scores.num_entries; i++)
5372   {
5373     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5374
5375     if (pos >= 0 && pos <= scores.last_added)
5376       scores.last_added++;
5377   }
5378
5379   if (scores.last_added >= MAX_SCORE_ENTRIES)
5380   {
5381     scores.last_added = MAX_SCORE_ENTRIES - 1;
5382     scores.force_last_added = TRUE;
5383
5384     scores.entry[scores.last_added] = last_added_entry;
5385   }
5386 }
5387
5388 static int getElementMoveStepsizeExt(int x, int y, int direction)
5389 {
5390   int element = Tile[x][y];
5391   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5392   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5393   int horiz_move = (dx != 0);
5394   int sign = (horiz_move ? dx : dy);
5395   int step = sign * element_info[element].move_stepsize;
5396
5397   // special values for move stepsize for spring and things on conveyor belt
5398   if (horiz_move)
5399   {
5400     if (CAN_FALL(element) &&
5401         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5402       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5403     else if (element == EL_SPRING)
5404       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5405   }
5406
5407   return step;
5408 }
5409
5410 static int getElementMoveStepsize(int x, int y)
5411 {
5412   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5413 }
5414
5415 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5416 {
5417   if (player->GfxAction != action || player->GfxDir != dir)
5418   {
5419     player->GfxAction = action;
5420     player->GfxDir = dir;
5421     player->Frame = 0;
5422     player->StepFrame = 0;
5423   }
5424 }
5425
5426 static void ResetGfxFrame(int x, int y)
5427 {
5428   // profiling showed that "autotest" spends 10~20% of its time in this function
5429   if (DrawingDeactivatedField())
5430     return;
5431
5432   int element = Tile[x][y];
5433   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5434
5435   if (graphic_info[graphic].anim_global_sync)
5436     GfxFrame[x][y] = FrameCounter;
5437   else if (graphic_info[graphic].anim_global_anim_sync)
5438     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5439   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5440     GfxFrame[x][y] = CustomValue[x][y];
5441   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5442     GfxFrame[x][y] = element_info[element].collect_score;
5443   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5444     GfxFrame[x][y] = ChangeDelay[x][y];
5445 }
5446
5447 static void ResetGfxAnimation(int x, int y)
5448 {
5449   GfxAction[x][y] = ACTION_DEFAULT;
5450   GfxDir[x][y] = MovDir[x][y];
5451   GfxFrame[x][y] = 0;
5452
5453   ResetGfxFrame(x, y);
5454 }
5455
5456 static void ResetRandomAnimationValue(int x, int y)
5457 {
5458   GfxRandom[x][y] = INIT_GFX_RANDOM();
5459 }
5460
5461 static void InitMovingField(int x, int y, int direction)
5462 {
5463   int element = Tile[x][y];
5464   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5465   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5466   int newx = x + dx;
5467   int newy = y + dy;
5468   boolean is_moving_before, is_moving_after;
5469
5470   // check if element was/is moving or being moved before/after mode change
5471   is_moving_before = (WasJustMoving[x][y] != 0);
5472   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5473
5474   // reset animation only for moving elements which change direction of moving
5475   // or which just started or stopped moving
5476   // (else CEs with property "can move" / "not moving" are reset each frame)
5477   if (is_moving_before != is_moving_after ||
5478       direction != MovDir[x][y])
5479     ResetGfxAnimation(x, y);
5480
5481   MovDir[x][y] = direction;
5482   GfxDir[x][y] = direction;
5483
5484   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5485                      direction == MV_DOWN && CAN_FALL(element) ?
5486                      ACTION_FALLING : ACTION_MOVING);
5487
5488   // this is needed for CEs with property "can move" / "not moving"
5489
5490   if (is_moving_after)
5491   {
5492     if (Tile[newx][newy] == EL_EMPTY)
5493       Tile[newx][newy] = EL_BLOCKED;
5494
5495     MovDir[newx][newy] = MovDir[x][y];
5496
5497     CustomValue[newx][newy] = CustomValue[x][y];
5498
5499     GfxFrame[newx][newy] = GfxFrame[x][y];
5500     GfxRandom[newx][newy] = GfxRandom[x][y];
5501     GfxAction[newx][newy] = GfxAction[x][y];
5502     GfxDir[newx][newy] = GfxDir[x][y];
5503   }
5504 }
5505
5506 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5507 {
5508   int direction = MovDir[x][y];
5509   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5510   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5511
5512   *goes_to_x = newx;
5513   *goes_to_y = newy;
5514 }
5515
5516 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5517 {
5518   int direction = MovDir[x][y];
5519   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5520   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5521
5522   *comes_from_x = oldx;
5523   *comes_from_y = oldy;
5524 }
5525
5526 static int MovingOrBlocked2Element(int x, int y)
5527 {
5528   int element = Tile[x][y];
5529
5530   if (element == EL_BLOCKED)
5531   {
5532     int oldx, oldy;
5533
5534     Blocked2Moving(x, y, &oldx, &oldy);
5535
5536     return Tile[oldx][oldy];
5537   }
5538
5539   return element;
5540 }
5541
5542 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5543 {
5544   // like MovingOrBlocked2Element(), but if element is moving
5545   // and (x, y) is the field the moving element is just leaving,
5546   // return EL_BLOCKED instead of the element value
5547   int element = Tile[x][y];
5548
5549   if (IS_MOVING(x, y))
5550   {
5551     if (element == EL_BLOCKED)
5552     {
5553       int oldx, oldy;
5554
5555       Blocked2Moving(x, y, &oldx, &oldy);
5556       return Tile[oldx][oldy];
5557     }
5558     else
5559       return EL_BLOCKED;
5560   }
5561   else
5562     return element;
5563 }
5564
5565 static void RemoveField(int x, int y)
5566 {
5567   Tile[x][y] = EL_EMPTY;
5568
5569   MovPos[x][y] = 0;
5570   MovDir[x][y] = 0;
5571   MovDelay[x][y] = 0;
5572
5573   CustomValue[x][y] = 0;
5574
5575   AmoebaNr[x][y] = 0;
5576   ChangeDelay[x][y] = 0;
5577   ChangePage[x][y] = -1;
5578   Pushed[x][y] = FALSE;
5579
5580   GfxElement[x][y] = EL_UNDEFINED;
5581   GfxAction[x][y] = ACTION_DEFAULT;
5582   GfxDir[x][y] = MV_NONE;
5583 }
5584
5585 static void RemoveMovingField(int x, int y)
5586 {
5587   int oldx = x, oldy = y, newx = x, newy = y;
5588   int element = Tile[x][y];
5589   int next_element = EL_UNDEFINED;
5590
5591   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5592     return;
5593
5594   if (IS_MOVING(x, y))
5595   {
5596     Moving2Blocked(x, y, &newx, &newy);
5597
5598     if (Tile[newx][newy] != EL_BLOCKED)
5599     {
5600       // element is moving, but target field is not free (blocked), but
5601       // already occupied by something different (example: acid pool);
5602       // in this case, only remove the moving field, but not the target
5603
5604       RemoveField(oldx, oldy);
5605
5606       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5607
5608       TEST_DrawLevelField(oldx, oldy);
5609
5610       return;
5611     }
5612   }
5613   else if (element == EL_BLOCKED)
5614   {
5615     Blocked2Moving(x, y, &oldx, &oldy);
5616     if (!IS_MOVING(oldx, oldy))
5617       return;
5618   }
5619
5620   if (element == EL_BLOCKED &&
5621       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5622        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5623        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5624        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5625        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5626        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5627     next_element = get_next_element(Tile[oldx][oldy]);
5628
5629   RemoveField(oldx, oldy);
5630   RemoveField(newx, newy);
5631
5632   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5633
5634   if (next_element != EL_UNDEFINED)
5635     Tile[oldx][oldy] = next_element;
5636
5637   TEST_DrawLevelField(oldx, oldy);
5638   TEST_DrawLevelField(newx, newy);
5639 }
5640
5641 void DrawDynamite(int x, int y)
5642 {
5643   int sx = SCREENX(x), sy = SCREENY(y);
5644   int graphic = el2img(Tile[x][y]);
5645   int frame;
5646
5647   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5648     return;
5649
5650   if (IS_WALKABLE_INSIDE(Back[x][y]))
5651     return;
5652
5653   if (Back[x][y])
5654     DrawLevelElement(x, y, Back[x][y]);
5655   else if (Store[x][y])
5656     DrawLevelElement(x, y, Store[x][y]);
5657   else if (game.use_masked_elements)
5658     DrawLevelElement(x, y, EL_EMPTY);
5659
5660   frame = getGraphicAnimationFrameXY(graphic, x, y);
5661
5662   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5663     DrawGraphicThruMask(sx, sy, graphic, frame);
5664   else
5665     DrawGraphic(sx, sy, graphic, frame);
5666 }
5667
5668 static void CheckDynamite(int x, int y)
5669 {
5670   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5671   {
5672     MovDelay[x][y]--;
5673
5674     if (MovDelay[x][y] != 0)
5675     {
5676       DrawDynamite(x, y);
5677       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5678
5679       return;
5680     }
5681   }
5682
5683   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5684
5685   Bang(x, y);
5686 }
5687
5688 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5689 {
5690   boolean num_checked_players = 0;
5691   int i;
5692
5693   for (i = 0; i < MAX_PLAYERS; i++)
5694   {
5695     if (stored_player[i].active)
5696     {
5697       int sx = stored_player[i].jx;
5698       int sy = stored_player[i].jy;
5699
5700       if (num_checked_players == 0)
5701       {
5702         *sx1 = *sx2 = sx;
5703         *sy1 = *sy2 = sy;
5704       }
5705       else
5706       {
5707         *sx1 = MIN(*sx1, sx);
5708         *sy1 = MIN(*sy1, sy);
5709         *sx2 = MAX(*sx2, sx);
5710         *sy2 = MAX(*sy2, sy);
5711       }
5712
5713       num_checked_players++;
5714     }
5715   }
5716 }
5717
5718 static boolean checkIfAllPlayersFitToScreen_RND(void)
5719 {
5720   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5721
5722   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5723
5724   return (sx2 - sx1 < SCR_FIELDX &&
5725           sy2 - sy1 < SCR_FIELDY);
5726 }
5727
5728 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5729 {
5730   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5731
5732   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5733
5734   *sx = (sx1 + sx2) / 2;
5735   *sy = (sy1 + sy2) / 2;
5736 }
5737
5738 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5739                                boolean center_screen, boolean quick_relocation)
5740 {
5741   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5742   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5743   boolean no_delay = (tape.warp_forward);
5744   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5745   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5746   int new_scroll_x, new_scroll_y;
5747
5748   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5749   {
5750     // case 1: quick relocation inside visible screen (without scrolling)
5751
5752     RedrawPlayfield();
5753
5754     return;
5755   }
5756
5757   if (!level.shifted_relocation || center_screen)
5758   {
5759     // relocation _with_ centering of screen
5760
5761     new_scroll_x = SCROLL_POSITION_X(x);
5762     new_scroll_y = SCROLL_POSITION_Y(y);
5763   }
5764   else
5765   {
5766     // relocation _without_ centering of screen
5767
5768     // apply distance between old and new player position to scroll position
5769     int shifted_scroll_x = scroll_x + (x - old_x);
5770     int shifted_scroll_y = scroll_y + (y - old_y);
5771
5772     // make sure that shifted scroll position does not scroll beyond screen
5773     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5774     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5775
5776     // special case for teleporting from one end of the playfield to the other
5777     // (this kludge prevents the destination area to be shifted by half a tile
5778     // against the source destination for even screen width or screen height;
5779     // probably most useful when used with high "game.forced_scroll_delay_value"
5780     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5781     if (quick_relocation)
5782     {
5783       if (EVEN(SCR_FIELDX))
5784       {
5785         // relocate (teleport) between left and right border (half or full)
5786         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5787           new_scroll_x = SBX_Right;
5788         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5789           new_scroll_x = SBX_Right - 1;
5790         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5791           new_scroll_x = SBX_Left;
5792         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5793           new_scroll_x = SBX_Left + 1;
5794       }
5795
5796       if (EVEN(SCR_FIELDY))
5797       {
5798         // relocate (teleport) between top and bottom border (half or full)
5799         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5800           new_scroll_y = SBY_Lower;
5801         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5802           new_scroll_y = SBY_Lower - 1;
5803         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5804           new_scroll_y = SBY_Upper;
5805         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5806           new_scroll_y = SBY_Upper + 1;
5807       }
5808     }
5809   }
5810
5811   if (quick_relocation)
5812   {
5813     // case 2: quick relocation (redraw without visible scrolling)
5814
5815     scroll_x = new_scroll_x;
5816     scroll_y = new_scroll_y;
5817
5818     RedrawPlayfield();
5819
5820     return;
5821   }
5822
5823   // case 3: visible relocation (with scrolling to new position)
5824
5825   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5826
5827   SetVideoFrameDelay(wait_delay_value);
5828
5829   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5830   {
5831     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5832     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5833
5834     if (dx == 0 && dy == 0)             // no scrolling needed at all
5835       break;
5836
5837     scroll_x -= dx;
5838     scroll_y -= dy;
5839
5840     // set values for horizontal/vertical screen scrolling (half tile size)
5841     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5842     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5843     int pos_x = dx * TILEX / 2;
5844     int pos_y = dy * TILEY / 2;
5845     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5846     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5847
5848     ScrollLevel(dx, dy);
5849     DrawAllPlayers();
5850
5851     // scroll in two steps of half tile size to make things smoother
5852     BlitScreenToBitmapExt_RND(window, fx, fy);
5853
5854     // scroll second step to align at full tile size
5855     BlitScreenToBitmap(window);
5856   }
5857
5858   DrawAllPlayers();
5859   BackToFront();
5860
5861   SetVideoFrameDelay(frame_delay_value_old);
5862 }
5863
5864 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5865 {
5866   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5867   int player_nr = GET_PLAYER_NR(el_player);
5868   struct PlayerInfo *player = &stored_player[player_nr];
5869   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5870   boolean no_delay = (tape.warp_forward);
5871   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5872   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5873   int old_jx = player->jx;
5874   int old_jy = player->jy;
5875   int old_element = Tile[old_jx][old_jy];
5876   int element = Tile[jx][jy];
5877   boolean player_relocated = (old_jx != jx || old_jy != jy);
5878
5879   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5880   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5881   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5882   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5883   int leave_side_horiz = move_dir_horiz;
5884   int leave_side_vert  = move_dir_vert;
5885   int enter_side = enter_side_horiz | enter_side_vert;
5886   int leave_side = leave_side_horiz | leave_side_vert;
5887
5888   if (player->buried)           // do not reanimate dead player
5889     return;
5890
5891   if (!player_relocated)        // no need to relocate the player
5892     return;
5893
5894   if (IS_PLAYER(jx, jy))        // player already placed at new position
5895   {
5896     RemoveField(jx, jy);        // temporarily remove newly placed player
5897     DrawLevelField(jx, jy);
5898   }
5899
5900   if (player->present)
5901   {
5902     while (player->MovPos)
5903     {
5904       ScrollPlayer(player, SCROLL_GO_ON);
5905       ScrollScreen(NULL, SCROLL_GO_ON);
5906
5907       AdvanceFrameAndPlayerCounters(player->index_nr);
5908
5909       DrawPlayer(player);
5910
5911       BackToFront_WithFrameDelay(wait_delay_value);
5912     }
5913
5914     DrawPlayer(player);         // needed here only to cleanup last field
5915     DrawLevelField(player->jx, player->jy);     // remove player graphic
5916
5917     player->is_moving = FALSE;
5918   }
5919
5920   if (IS_CUSTOM_ELEMENT(old_element))
5921     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5922                                CE_LEFT_BY_PLAYER,
5923                                player->index_bit, leave_side);
5924
5925   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5926                                       CE_PLAYER_LEAVES_X,
5927                                       player->index_bit, leave_side);
5928
5929   Tile[jx][jy] = el_player;
5930   InitPlayerField(jx, jy, el_player, TRUE);
5931
5932   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5933      possible that the relocation target field did not contain a player element,
5934      but a walkable element, to which the new player was relocated -- in this
5935      case, restore that (already initialized!) element on the player field */
5936   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5937   {
5938     Tile[jx][jy] = element;     // restore previously existing element
5939   }
5940
5941   // only visually relocate centered player
5942   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5943                      FALSE, level.instant_relocation);
5944
5945   TestIfPlayerTouchesBadThing(jx, jy);
5946   TestIfPlayerTouchesCustomElement(jx, jy);
5947
5948   if (IS_CUSTOM_ELEMENT(element))
5949     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5950                                player->index_bit, enter_side);
5951
5952   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5953                                       player->index_bit, enter_side);
5954
5955   if (player->is_switching)
5956   {
5957     /* ensure that relocation while still switching an element does not cause
5958        a new element to be treated as also switched directly after relocation
5959        (this is important for teleporter switches that teleport the player to
5960        a place where another teleporter switch is in the same direction, which
5961        would then incorrectly be treated as immediately switched before the
5962        direction key that caused the switch was released) */
5963
5964     player->switch_x += jx - old_jx;
5965     player->switch_y += jy - old_jy;
5966   }
5967 }
5968
5969 static void Explode(int ex, int ey, int phase, int mode)
5970 {
5971   int x, y;
5972   int last_phase;
5973   int border_element;
5974
5975   if (game.explosions_delayed)
5976   {
5977     ExplodeField[ex][ey] = mode;
5978     return;
5979   }
5980
5981   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5982   {
5983     int center_element = Tile[ex][ey];
5984     int ce_value = CustomValue[ex][ey];
5985     int ce_score = element_info[center_element].collect_score;
5986     int artwork_element, explosion_element;     // set these values later
5987
5988     // remove things displayed in background while burning dynamite
5989     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5990       Back[ex][ey] = 0;
5991
5992     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5993     {
5994       // put moving element to center field (and let it explode there)
5995       center_element = MovingOrBlocked2Element(ex, ey);
5996       RemoveMovingField(ex, ey);
5997       Tile[ex][ey] = center_element;
5998     }
5999
6000     // now "center_element" is finally determined -- set related values now
6001     artwork_element = center_element;           // for custom player artwork
6002     explosion_element = center_element;         // for custom player artwork
6003
6004     if (IS_PLAYER(ex, ey))
6005     {
6006       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6007
6008       artwork_element = stored_player[player_nr].artwork_element;
6009
6010       if (level.use_explosion_element[player_nr])
6011       {
6012         explosion_element = level.explosion_element[player_nr];
6013         artwork_element = explosion_element;
6014       }
6015     }
6016
6017     if (mode == EX_TYPE_NORMAL ||
6018         mode == EX_TYPE_CENTER ||
6019         mode == EX_TYPE_CROSS)
6020       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6021
6022     last_phase = element_info[explosion_element].explosion_delay + 1;
6023
6024     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6025     {
6026       int xx = x - ex + 1;
6027       int yy = y - ey + 1;
6028       int element;
6029
6030       if (!IN_LEV_FIELD(x, y) ||
6031           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6032           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6033         continue;
6034
6035       element = Tile[x][y];
6036
6037       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6038       {
6039         element = MovingOrBlocked2Element(x, y);
6040
6041         if (!IS_EXPLOSION_PROOF(element))
6042           RemoveMovingField(x, y);
6043       }
6044
6045       // indestructible elements can only explode in center (but not flames)
6046       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6047                                            mode == EX_TYPE_BORDER)) ||
6048           element == EL_FLAMES)
6049         continue;
6050
6051       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6052          behaviour, for example when touching a yamyam that explodes to rocks
6053          with active deadly shield, a rock is created under the player !!! */
6054       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6055 #if 0
6056       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6057           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6058            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6059 #else
6060       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6061 #endif
6062       {
6063         if (IS_ACTIVE_BOMB(element))
6064         {
6065           // re-activate things under the bomb like gate or penguin
6066           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6067           Back[x][y] = 0;
6068         }
6069
6070         continue;
6071       }
6072
6073       // save walkable background elements while explosion on same tile
6074       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6075           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6076         Back[x][y] = element;
6077
6078       // ignite explodable elements reached by other explosion
6079       if (element == EL_EXPLOSION)
6080         element = Store2[x][y];
6081
6082       if (AmoebaNr[x][y] &&
6083           (element == EL_AMOEBA_FULL ||
6084            element == EL_BD_AMOEBA ||
6085            element == EL_AMOEBA_GROWING))
6086       {
6087         AmoebaCnt[AmoebaNr[x][y]]--;
6088         AmoebaCnt2[AmoebaNr[x][y]]--;
6089       }
6090
6091       RemoveField(x, y);
6092
6093       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6094       {
6095         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6096
6097         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6098
6099         if (PLAYERINFO(ex, ey)->use_murphy)
6100           Store[x][y] = EL_EMPTY;
6101       }
6102
6103       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6104       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6105       else if (IS_PLAYER_ELEMENT(center_element))
6106         Store[x][y] = EL_EMPTY;
6107       else if (center_element == EL_YAMYAM)
6108         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6109       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6110         Store[x][y] = element_info[center_element].content.e[xx][yy];
6111 #if 1
6112       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6113       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6114       // otherwise) -- FIX THIS !!!
6115       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6116         Store[x][y] = element_info[element].content.e[1][1];
6117 #else
6118       else if (!CAN_EXPLODE(element))
6119         Store[x][y] = element_info[element].content.e[1][1];
6120 #endif
6121       else
6122         Store[x][y] = EL_EMPTY;
6123
6124       if (IS_CUSTOM_ELEMENT(center_element))
6125         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6126                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6127                        Store[x][y] >= EL_PREV_CE_8 &&
6128                        Store[x][y] <= EL_NEXT_CE_8 ?
6129                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6130                        Store[x][y]);
6131
6132       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6133           center_element == EL_AMOEBA_TO_DIAMOND)
6134         Store2[x][y] = element;
6135
6136       Tile[x][y] = EL_EXPLOSION;
6137       GfxElement[x][y] = artwork_element;
6138
6139       ExplodePhase[x][y] = 1;
6140       ExplodeDelay[x][y] = last_phase;
6141
6142       Stop[x][y] = TRUE;
6143     }
6144
6145     if (center_element == EL_YAMYAM)
6146       game.yamyam_content_nr =
6147         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6148
6149     return;
6150   }
6151
6152   if (Stop[ex][ey])
6153     return;
6154
6155   x = ex;
6156   y = ey;
6157
6158   if (phase == 1)
6159     GfxFrame[x][y] = 0;         // restart explosion animation
6160
6161   last_phase = ExplodeDelay[x][y];
6162
6163   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6164
6165   // this can happen if the player leaves an explosion just in time
6166   if (GfxElement[x][y] == EL_UNDEFINED)
6167     GfxElement[x][y] = EL_EMPTY;
6168
6169   border_element = Store2[x][y];
6170   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6171     border_element = StorePlayer[x][y];
6172
6173   if (phase == element_info[border_element].ignition_delay ||
6174       phase == last_phase)
6175   {
6176     boolean border_explosion = FALSE;
6177
6178     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6179         !PLAYER_EXPLOSION_PROTECTED(x, y))
6180     {
6181       KillPlayerUnlessExplosionProtected(x, y);
6182       border_explosion = TRUE;
6183     }
6184     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6185     {
6186       Tile[x][y] = Store2[x][y];
6187       Store2[x][y] = 0;
6188       Bang(x, y);
6189       border_explosion = TRUE;
6190     }
6191     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6192     {
6193       AmoebaToDiamond(x, y);
6194       Store2[x][y] = 0;
6195       border_explosion = TRUE;
6196     }
6197
6198     // if an element just explodes due to another explosion (chain-reaction),
6199     // do not immediately end the new explosion when it was the last frame of
6200     // the explosion (as it would be done in the following "if"-statement!)
6201     if (border_explosion && phase == last_phase)
6202       return;
6203   }
6204
6205   // this can happen if the player was just killed by an explosion
6206   if (GfxElement[x][y] == EL_UNDEFINED)
6207     GfxElement[x][y] = EL_EMPTY;
6208
6209   if (phase == last_phase)
6210   {
6211     int element;
6212
6213     element = Tile[x][y] = Store[x][y];
6214     Store[x][y] = Store2[x][y] = 0;
6215     GfxElement[x][y] = EL_UNDEFINED;
6216
6217     // player can escape from explosions and might therefore be still alive
6218     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6219         element <= EL_PLAYER_IS_EXPLODING_4)
6220     {
6221       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6222       int explosion_element = EL_PLAYER_1 + player_nr;
6223       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6224       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6225
6226       if (level.use_explosion_element[player_nr])
6227         explosion_element = level.explosion_element[player_nr];
6228
6229       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6230                     element_info[explosion_element].content.e[xx][yy]);
6231     }
6232
6233     // restore probably existing indestructible background element
6234     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6235       element = Tile[x][y] = Back[x][y];
6236     Back[x][y] = 0;
6237
6238     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6239     GfxDir[x][y] = MV_NONE;
6240     ChangeDelay[x][y] = 0;
6241     ChangePage[x][y] = -1;
6242
6243     CustomValue[x][y] = 0;
6244
6245     InitField_WithBug2(x, y, FALSE);
6246
6247     TEST_DrawLevelField(x, y);
6248
6249     TestIfElementTouchesCustomElement(x, y);
6250
6251     if (GFX_CRUMBLED(element))
6252       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6253
6254     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6255       StorePlayer[x][y] = 0;
6256
6257     if (IS_PLAYER_ELEMENT(element))
6258       RelocatePlayer(x, y, element);
6259   }
6260   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6261   {
6262     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6263     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6264
6265     if (phase == 1)
6266       TEST_DrawLevelFieldCrumbled(x, y);
6267
6268     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6269     {
6270       DrawLevelElement(x, y, Back[x][y]);
6271       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6272     }
6273     else if (IS_WALKABLE_UNDER(Back[x][y]))
6274     {
6275       DrawLevelGraphic(x, y, graphic, frame);
6276       DrawLevelElementThruMask(x, y, Back[x][y]);
6277     }
6278     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6279       DrawLevelGraphic(x, y, graphic, frame);
6280   }
6281 }
6282
6283 static void DynaExplode(int ex, int ey)
6284 {
6285   int i, j;
6286   int dynabomb_element = Tile[ex][ey];
6287   int dynabomb_size = 1;
6288   boolean dynabomb_xl = FALSE;
6289   struct PlayerInfo *player;
6290   struct XY *xy = xy_topdown;
6291
6292   if (IS_ACTIVE_BOMB(dynabomb_element))
6293   {
6294     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6295     dynabomb_size = player->dynabomb_size;
6296     dynabomb_xl = player->dynabomb_xl;
6297     player->dynabombs_left++;
6298   }
6299
6300   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6301
6302   for (i = 0; i < NUM_DIRECTIONS; i++)
6303   {
6304     for (j = 1; j <= dynabomb_size; j++)
6305     {
6306       int x = ex + j * xy[i].x;
6307       int y = ey + j * xy[i].y;
6308       int element;
6309
6310       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6311         break;
6312
6313       element = Tile[x][y];
6314
6315       // do not restart explosions of fields with active bombs
6316       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6317         continue;
6318
6319       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6320
6321       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6322           !IS_DIGGABLE(element) && !dynabomb_xl)
6323         break;
6324     }
6325   }
6326 }
6327
6328 void Bang(int x, int y)
6329 {
6330   int element = MovingOrBlocked2Element(x, y);
6331   int explosion_type = EX_TYPE_NORMAL;
6332
6333   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6334   {
6335     struct PlayerInfo *player = PLAYERINFO(x, y);
6336
6337     element = Tile[x][y] = player->initial_element;
6338
6339     if (level.use_explosion_element[player->index_nr])
6340     {
6341       int explosion_element = level.explosion_element[player->index_nr];
6342
6343       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6344         explosion_type = EX_TYPE_CROSS;
6345       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6346         explosion_type = EX_TYPE_CENTER;
6347     }
6348   }
6349
6350   switch (element)
6351   {
6352     case EL_BUG:
6353     case EL_SPACESHIP:
6354     case EL_BD_BUTTERFLY:
6355     case EL_BD_FIREFLY:
6356     case EL_YAMYAM:
6357     case EL_DARK_YAMYAM:
6358     case EL_ROBOT:
6359     case EL_PACMAN:
6360     case EL_MOLE:
6361       RaiseScoreElement(element);
6362       break;
6363
6364     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6365     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6366     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6367     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6368     case EL_DYNABOMB_INCREASE_NUMBER:
6369     case EL_DYNABOMB_INCREASE_SIZE:
6370     case EL_DYNABOMB_INCREASE_POWER:
6371       explosion_type = EX_TYPE_DYNA;
6372       break;
6373
6374     case EL_DC_LANDMINE:
6375       explosion_type = EX_TYPE_CENTER;
6376       break;
6377
6378     case EL_PENGUIN:
6379     case EL_LAMP:
6380     case EL_LAMP_ACTIVE:
6381     case EL_AMOEBA_TO_DIAMOND:
6382       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6383         explosion_type = EX_TYPE_CENTER;
6384       break;
6385
6386     default:
6387       if (element_info[element].explosion_type == EXPLODES_CROSS)
6388         explosion_type = EX_TYPE_CROSS;
6389       else if (element_info[element].explosion_type == EXPLODES_1X1)
6390         explosion_type = EX_TYPE_CENTER;
6391       break;
6392   }
6393
6394   if (explosion_type == EX_TYPE_DYNA)
6395     DynaExplode(x, y);
6396   else
6397     Explode(x, y, EX_PHASE_START, explosion_type);
6398
6399   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6400 }
6401
6402 static void SplashAcid(int x, int y)
6403 {
6404   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6405       (!IN_LEV_FIELD(x - 1, y - 2) ||
6406        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6407     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6408
6409   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6410       (!IN_LEV_FIELD(x + 1, y - 2) ||
6411        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6412     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6413
6414   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6415 }
6416
6417 static void InitBeltMovement(void)
6418 {
6419   static int belt_base_element[4] =
6420   {
6421     EL_CONVEYOR_BELT_1_LEFT,
6422     EL_CONVEYOR_BELT_2_LEFT,
6423     EL_CONVEYOR_BELT_3_LEFT,
6424     EL_CONVEYOR_BELT_4_LEFT
6425   };
6426   static int belt_base_active_element[4] =
6427   {
6428     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6429     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6430     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6431     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6432   };
6433
6434   int x, y, i, j;
6435
6436   // set frame order for belt animation graphic according to belt direction
6437   for (i = 0; i < NUM_BELTS; i++)
6438   {
6439     int belt_nr = i;
6440
6441     for (j = 0; j < NUM_BELT_PARTS; j++)
6442     {
6443       int element = belt_base_active_element[belt_nr] + j;
6444       int graphic_1 = el2img(element);
6445       int graphic_2 = el2panelimg(element);
6446
6447       if (game.belt_dir[i] == MV_LEFT)
6448       {
6449         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6450         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6451       }
6452       else
6453       {
6454         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6455         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6456       }
6457     }
6458   }
6459
6460   SCAN_PLAYFIELD(x, y)
6461   {
6462     int element = Tile[x][y];
6463
6464     for (i = 0; i < NUM_BELTS; i++)
6465     {
6466       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6467       {
6468         int e_belt_nr = getBeltNrFromBeltElement(element);
6469         int belt_nr = i;
6470
6471         if (e_belt_nr == belt_nr)
6472         {
6473           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6474
6475           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6476         }
6477       }
6478     }
6479   }
6480 }
6481
6482 static void ToggleBeltSwitch(int x, int y)
6483 {
6484   static int belt_base_element[4] =
6485   {
6486     EL_CONVEYOR_BELT_1_LEFT,
6487     EL_CONVEYOR_BELT_2_LEFT,
6488     EL_CONVEYOR_BELT_3_LEFT,
6489     EL_CONVEYOR_BELT_4_LEFT
6490   };
6491   static int belt_base_active_element[4] =
6492   {
6493     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6494     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6495     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6496     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6497   };
6498   static int belt_base_switch_element[4] =
6499   {
6500     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6501     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6502     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6503     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6504   };
6505   static int belt_move_dir[4] =
6506   {
6507     MV_LEFT,
6508     MV_NONE,
6509     MV_RIGHT,
6510     MV_NONE,
6511   };
6512
6513   int element = Tile[x][y];
6514   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6515   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6516   int belt_dir = belt_move_dir[belt_dir_nr];
6517   int xx, yy, i;
6518
6519   if (!IS_BELT_SWITCH(element))
6520     return;
6521
6522   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6523   game.belt_dir[belt_nr] = belt_dir;
6524
6525   if (belt_dir_nr == 3)
6526     belt_dir_nr = 1;
6527
6528   // set frame order for belt animation graphic according to belt direction
6529   for (i = 0; i < NUM_BELT_PARTS; i++)
6530   {
6531     int element = belt_base_active_element[belt_nr] + i;
6532     int graphic_1 = el2img(element);
6533     int graphic_2 = el2panelimg(element);
6534
6535     if (belt_dir == MV_LEFT)
6536     {
6537       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6538       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6539     }
6540     else
6541     {
6542       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6543       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6544     }
6545   }
6546
6547   SCAN_PLAYFIELD(xx, yy)
6548   {
6549     int element = Tile[xx][yy];
6550
6551     if (IS_BELT_SWITCH(element))
6552     {
6553       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6554
6555       if (e_belt_nr == belt_nr)
6556       {
6557         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6558         TEST_DrawLevelField(xx, yy);
6559       }
6560     }
6561     else if (IS_BELT(element) && belt_dir != MV_NONE)
6562     {
6563       int e_belt_nr = getBeltNrFromBeltElement(element);
6564
6565       if (e_belt_nr == belt_nr)
6566       {
6567         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6568
6569         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6570         TEST_DrawLevelField(xx, yy);
6571       }
6572     }
6573     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6574     {
6575       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6576
6577       if (e_belt_nr == belt_nr)
6578       {
6579         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6580
6581         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6582         TEST_DrawLevelField(xx, yy);
6583       }
6584     }
6585   }
6586 }
6587
6588 static void ToggleSwitchgateSwitch(void)
6589 {
6590   int xx, yy;
6591
6592   game.switchgate_pos = !game.switchgate_pos;
6593
6594   SCAN_PLAYFIELD(xx, yy)
6595   {
6596     int element = Tile[xx][yy];
6597
6598     if (element == EL_SWITCHGATE_SWITCH_UP)
6599     {
6600       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6601       TEST_DrawLevelField(xx, yy);
6602     }
6603     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6604     {
6605       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6606       TEST_DrawLevelField(xx, yy);
6607     }
6608     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6609     {
6610       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6611       TEST_DrawLevelField(xx, yy);
6612     }
6613     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6614     {
6615       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6616       TEST_DrawLevelField(xx, yy);
6617     }
6618     else if (element == EL_SWITCHGATE_OPEN ||
6619              element == EL_SWITCHGATE_OPENING)
6620     {
6621       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6622
6623       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6624     }
6625     else if (element == EL_SWITCHGATE_CLOSED ||
6626              element == EL_SWITCHGATE_CLOSING)
6627     {
6628       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6629
6630       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6631     }
6632   }
6633 }
6634
6635 static int getInvisibleActiveFromInvisibleElement(int element)
6636 {
6637   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6638           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6639           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6640           element);
6641 }
6642
6643 static int getInvisibleFromInvisibleActiveElement(int element)
6644 {
6645   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6646           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6647           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6648           element);
6649 }
6650
6651 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6652 {
6653   int x, y;
6654
6655   SCAN_PLAYFIELD(x, y)
6656   {
6657     int element = Tile[x][y];
6658
6659     if (element == EL_LIGHT_SWITCH &&
6660         game.light_time_left > 0)
6661     {
6662       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6663       TEST_DrawLevelField(x, y);
6664     }
6665     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6666              game.light_time_left == 0)
6667     {
6668       Tile[x][y] = EL_LIGHT_SWITCH;
6669       TEST_DrawLevelField(x, y);
6670     }
6671     else if (element == EL_EMC_DRIPPER &&
6672              game.light_time_left > 0)
6673     {
6674       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6675       TEST_DrawLevelField(x, y);
6676     }
6677     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6678              game.light_time_left == 0)
6679     {
6680       Tile[x][y] = EL_EMC_DRIPPER;
6681       TEST_DrawLevelField(x, y);
6682     }
6683     else if (element == EL_INVISIBLE_STEELWALL ||
6684              element == EL_INVISIBLE_WALL ||
6685              element == EL_INVISIBLE_SAND)
6686     {
6687       if (game.light_time_left > 0)
6688         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6689
6690       TEST_DrawLevelField(x, y);
6691
6692       // uncrumble neighbour fields, if needed
6693       if (element == EL_INVISIBLE_SAND)
6694         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6695     }
6696     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6697              element == EL_INVISIBLE_WALL_ACTIVE ||
6698              element == EL_INVISIBLE_SAND_ACTIVE)
6699     {
6700       if (game.light_time_left == 0)
6701         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6702
6703       TEST_DrawLevelField(x, y);
6704
6705       // re-crumble neighbour fields, if needed
6706       if (element == EL_INVISIBLE_SAND)
6707         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6708     }
6709   }
6710 }
6711
6712 static void RedrawAllInvisibleElementsForLenses(void)
6713 {
6714   int x, y;
6715
6716   SCAN_PLAYFIELD(x, y)
6717   {
6718     int element = Tile[x][y];
6719
6720     if (element == EL_EMC_DRIPPER &&
6721         game.lenses_time_left > 0)
6722     {
6723       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6724       TEST_DrawLevelField(x, y);
6725     }
6726     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6727              game.lenses_time_left == 0)
6728     {
6729       Tile[x][y] = EL_EMC_DRIPPER;
6730       TEST_DrawLevelField(x, y);
6731     }
6732     else if (element == EL_INVISIBLE_STEELWALL ||
6733              element == EL_INVISIBLE_WALL ||
6734              element == EL_INVISIBLE_SAND)
6735     {
6736       if (game.lenses_time_left > 0)
6737         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6738
6739       TEST_DrawLevelField(x, y);
6740
6741       // uncrumble neighbour fields, if needed
6742       if (element == EL_INVISIBLE_SAND)
6743         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6744     }
6745     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6746              element == EL_INVISIBLE_WALL_ACTIVE ||
6747              element == EL_INVISIBLE_SAND_ACTIVE)
6748     {
6749       if (game.lenses_time_left == 0)
6750         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6751
6752       TEST_DrawLevelField(x, y);
6753
6754       // re-crumble neighbour fields, if needed
6755       if (element == EL_INVISIBLE_SAND)
6756         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6757     }
6758   }
6759 }
6760
6761 static void RedrawAllInvisibleElementsForMagnifier(void)
6762 {
6763   int x, y;
6764
6765   SCAN_PLAYFIELD(x, y)
6766   {
6767     int element = Tile[x][y];
6768
6769     if (element == EL_EMC_FAKE_GRASS &&
6770         game.magnify_time_left > 0)
6771     {
6772       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6773       TEST_DrawLevelField(x, y);
6774     }
6775     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6776              game.magnify_time_left == 0)
6777     {
6778       Tile[x][y] = EL_EMC_FAKE_GRASS;
6779       TEST_DrawLevelField(x, y);
6780     }
6781     else if (IS_GATE_GRAY(element) &&
6782              game.magnify_time_left > 0)
6783     {
6784       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6785                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6786                     IS_EM_GATE_GRAY(element) ?
6787                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6788                     IS_EMC_GATE_GRAY(element) ?
6789                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6790                     IS_DC_GATE_GRAY(element) ?
6791                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6792                     element);
6793       TEST_DrawLevelField(x, y);
6794     }
6795     else if (IS_GATE_GRAY_ACTIVE(element) &&
6796              game.magnify_time_left == 0)
6797     {
6798       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6799                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6800                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6801                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6802                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6803                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6804                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6805                     EL_DC_GATE_WHITE_GRAY :
6806                     element);
6807       TEST_DrawLevelField(x, y);
6808     }
6809   }
6810 }
6811
6812 static void ToggleLightSwitch(int x, int y)
6813 {
6814   int element = Tile[x][y];
6815
6816   game.light_time_left =
6817     (element == EL_LIGHT_SWITCH ?
6818      level.time_light * FRAMES_PER_SECOND : 0);
6819
6820   RedrawAllLightSwitchesAndInvisibleElements();
6821 }
6822
6823 static void ActivateTimegateSwitch(int x, int y)
6824 {
6825   int xx, yy;
6826
6827   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6828
6829   SCAN_PLAYFIELD(xx, yy)
6830   {
6831     int element = Tile[xx][yy];
6832
6833     if (element == EL_TIMEGATE_CLOSED ||
6834         element == EL_TIMEGATE_CLOSING)
6835     {
6836       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6837       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6838     }
6839
6840     /*
6841     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6842     {
6843       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6844       TEST_DrawLevelField(xx, yy);
6845     }
6846     */
6847
6848   }
6849
6850   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6851                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6852 }
6853
6854 static void Impact(int x, int y)
6855 {
6856   boolean last_line = (y == lev_fieldy - 1);
6857   boolean object_hit = FALSE;
6858   boolean impact = (last_line || object_hit);
6859   int element = Tile[x][y];
6860   int smashed = EL_STEELWALL;
6861
6862   if (!last_line)       // check if element below was hit
6863   {
6864     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6865       return;
6866
6867     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6868                                          MovDir[x][y + 1] != MV_DOWN ||
6869                                          MovPos[x][y + 1] <= TILEY / 2));
6870
6871     // do not smash moving elements that left the smashed field in time
6872     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6873         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6874       object_hit = FALSE;
6875
6876 #if USE_QUICKSAND_IMPACT_BUGFIX
6877     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6878     {
6879       RemoveMovingField(x, y + 1);
6880       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6881       Tile[x][y + 2] = EL_ROCK;
6882       TEST_DrawLevelField(x, y + 2);
6883
6884       object_hit = TRUE;
6885     }
6886
6887     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6888     {
6889       RemoveMovingField(x, y + 1);
6890       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6891       Tile[x][y + 2] = EL_ROCK;
6892       TEST_DrawLevelField(x, y + 2);
6893
6894       object_hit = TRUE;
6895     }
6896 #endif
6897
6898     if (object_hit)
6899       smashed = MovingOrBlocked2Element(x, y + 1);
6900
6901     impact = (last_line || object_hit);
6902   }
6903
6904   if (!last_line && smashed == EL_ACID) // element falls into acid
6905   {
6906     SplashAcid(x, y + 1);
6907     return;
6908   }
6909
6910   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6911   // only reset graphic animation if graphic really changes after impact
6912   if (impact &&
6913       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6914   {
6915     ResetGfxAnimation(x, y);
6916     TEST_DrawLevelField(x, y);
6917   }
6918
6919   if (impact && CAN_EXPLODE_IMPACT(element))
6920   {
6921     Bang(x, y);
6922     return;
6923   }
6924   else if (impact && element == EL_PEARL &&
6925            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6926   {
6927     ResetGfxAnimation(x, y);
6928
6929     Tile[x][y] = EL_PEARL_BREAKING;
6930     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6931     return;
6932   }
6933   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6934   {
6935     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6936
6937     return;
6938   }
6939
6940   if (impact && element == EL_AMOEBA_DROP)
6941   {
6942     if (object_hit && IS_PLAYER(x, y + 1))
6943       KillPlayerUnlessEnemyProtected(x, y + 1);
6944     else if (object_hit && smashed == EL_PENGUIN)
6945       Bang(x, y + 1);
6946     else
6947     {
6948       Tile[x][y] = EL_AMOEBA_GROWING;
6949       Store[x][y] = EL_AMOEBA_WET;
6950
6951       ResetRandomAnimationValue(x, y);
6952     }
6953     return;
6954   }
6955
6956   if (object_hit)               // check which object was hit
6957   {
6958     if ((CAN_PASS_MAGIC_WALL(element) && 
6959          (smashed == EL_MAGIC_WALL ||
6960           smashed == EL_BD_MAGIC_WALL)) ||
6961         (CAN_PASS_DC_MAGIC_WALL(element) &&
6962          smashed == EL_DC_MAGIC_WALL))
6963     {
6964       int xx, yy;
6965       int activated_magic_wall =
6966         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6967          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6968          EL_DC_MAGIC_WALL_ACTIVE);
6969
6970       // activate magic wall / mill
6971       SCAN_PLAYFIELD(xx, yy)
6972       {
6973         if (Tile[xx][yy] == smashed)
6974           Tile[xx][yy] = activated_magic_wall;
6975       }
6976
6977       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6978       game.magic_wall_active = TRUE;
6979
6980       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6981                             SND_MAGIC_WALL_ACTIVATING :
6982                             smashed == EL_BD_MAGIC_WALL ?
6983                             SND_BD_MAGIC_WALL_ACTIVATING :
6984                             SND_DC_MAGIC_WALL_ACTIVATING));
6985     }
6986
6987     if (IS_PLAYER(x, y + 1))
6988     {
6989       if (CAN_SMASH_PLAYER(element))
6990       {
6991         KillPlayerUnlessEnemyProtected(x, y + 1);
6992         return;
6993       }
6994     }
6995     else if (smashed == EL_PENGUIN)
6996     {
6997       if (CAN_SMASH_PLAYER(element))
6998       {
6999         Bang(x, y + 1);
7000         return;
7001       }
7002     }
7003     else if (element == EL_BD_DIAMOND)
7004     {
7005       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7006       {
7007         Bang(x, y + 1);
7008         return;
7009       }
7010     }
7011     else if (((element == EL_SP_INFOTRON ||
7012                element == EL_SP_ZONK) &&
7013               (smashed == EL_SP_SNIKSNAK ||
7014                smashed == EL_SP_ELECTRON ||
7015                smashed == EL_SP_DISK_ORANGE)) ||
7016              (element == EL_SP_INFOTRON &&
7017               smashed == EL_SP_DISK_YELLOW))
7018     {
7019       Bang(x, y + 1);
7020       return;
7021     }
7022     else if (CAN_SMASH_EVERYTHING(element))
7023     {
7024       if (IS_CLASSIC_ENEMY(smashed) ||
7025           CAN_EXPLODE_SMASHED(smashed))
7026       {
7027         Bang(x, y + 1);
7028         return;
7029       }
7030       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7031       {
7032         if (smashed == EL_LAMP ||
7033             smashed == EL_LAMP_ACTIVE)
7034         {
7035           Bang(x, y + 1);
7036           return;
7037         }
7038         else if (smashed == EL_NUT)
7039         {
7040           Tile[x][y + 1] = EL_NUT_BREAKING;
7041           PlayLevelSound(x, y, SND_NUT_BREAKING);
7042           RaiseScoreElement(EL_NUT);
7043           return;
7044         }
7045         else if (smashed == EL_PEARL)
7046         {
7047           ResetGfxAnimation(x, y);
7048
7049           Tile[x][y + 1] = EL_PEARL_BREAKING;
7050           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7051           return;
7052         }
7053         else if (smashed == EL_DIAMOND)
7054         {
7055           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7056           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7057           return;
7058         }
7059         else if (IS_BELT_SWITCH(smashed))
7060         {
7061           ToggleBeltSwitch(x, y + 1);
7062         }
7063         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7064                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7065                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7066                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7067         {
7068           ToggleSwitchgateSwitch();
7069         }
7070         else if (smashed == EL_LIGHT_SWITCH ||
7071                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7072         {
7073           ToggleLightSwitch(x, y + 1);
7074         }
7075         else
7076         {
7077           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7078
7079           CheckElementChangeBySide(x, y + 1, smashed, element,
7080                                    CE_SWITCHED, CH_SIDE_TOP);
7081           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7082                                             CH_SIDE_TOP);
7083         }
7084       }
7085       else
7086       {
7087         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7088       }
7089     }
7090   }
7091
7092   // play sound of magic wall / mill
7093   if (!last_line &&
7094       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7095        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7096        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7097   {
7098     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7099       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7100     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7101       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7102     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7103       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7104
7105     return;
7106   }
7107
7108   // play sound of object that hits the ground
7109   if (last_line || object_hit)
7110     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7111 }
7112
7113 static void TurnRoundExt(int x, int y)
7114 {
7115   static struct
7116   {
7117     int dx, dy;
7118   } move_xy[] =
7119   {
7120     {  0,  0 },
7121     { -1,  0 },
7122     { +1,  0 },
7123     {  0,  0 },
7124     {  0, -1 },
7125     {  0,  0 }, { 0, 0 }, { 0, 0 },
7126     {  0, +1 }
7127   };
7128   static struct
7129   {
7130     int left, right, back;
7131   } turn[] =
7132   {
7133     { 0,        0,              0        },
7134     { MV_DOWN,  MV_UP,          MV_RIGHT },
7135     { MV_UP,    MV_DOWN,        MV_LEFT  },
7136     { 0,        0,              0        },
7137     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7138     { 0,        0,              0        },
7139     { 0,        0,              0        },
7140     { 0,        0,              0        },
7141     { MV_RIGHT, MV_LEFT,        MV_UP    }
7142   };
7143
7144   int element = Tile[x][y];
7145   int move_pattern = element_info[element].move_pattern;
7146
7147   int old_move_dir = MovDir[x][y];
7148   int left_dir  = turn[old_move_dir].left;
7149   int right_dir = turn[old_move_dir].right;
7150   int back_dir  = turn[old_move_dir].back;
7151
7152   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7153   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7154   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7155   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7156
7157   int left_x  = x + left_dx,  left_y  = y + left_dy;
7158   int right_x = x + right_dx, right_y = y + right_dy;
7159   int move_x  = x + move_dx,  move_y  = y + move_dy;
7160
7161   int xx, yy;
7162
7163   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7164   {
7165     TestIfBadThingTouchesOtherBadThing(x, y);
7166
7167     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7168       MovDir[x][y] = right_dir;
7169     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7170       MovDir[x][y] = left_dir;
7171
7172     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7173       MovDelay[x][y] = 9;
7174     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7175       MovDelay[x][y] = 1;
7176   }
7177   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7178   {
7179     TestIfBadThingTouchesOtherBadThing(x, y);
7180
7181     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7182       MovDir[x][y] = left_dir;
7183     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7184       MovDir[x][y] = right_dir;
7185
7186     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7187       MovDelay[x][y] = 9;
7188     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7189       MovDelay[x][y] = 1;
7190   }
7191   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7192   {
7193     TestIfBadThingTouchesOtherBadThing(x, y);
7194
7195     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7196       MovDir[x][y] = left_dir;
7197     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7198       MovDir[x][y] = right_dir;
7199
7200     if (MovDir[x][y] != old_move_dir)
7201       MovDelay[x][y] = 9;
7202   }
7203   else if (element == EL_YAMYAM)
7204   {
7205     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7206     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7207
7208     if (can_turn_left && can_turn_right)
7209       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7210     else if (can_turn_left)
7211       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7212     else if (can_turn_right)
7213       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7214     else
7215       MovDir[x][y] = back_dir;
7216
7217     MovDelay[x][y] = 16 + 16 * RND(3);
7218   }
7219   else if (element == EL_DARK_YAMYAM)
7220   {
7221     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7222                                                          left_x, left_y);
7223     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7224                                                          right_x, right_y);
7225
7226     if (can_turn_left && can_turn_right)
7227       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7228     else if (can_turn_left)
7229       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7230     else if (can_turn_right)
7231       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7232     else
7233       MovDir[x][y] = back_dir;
7234
7235     MovDelay[x][y] = 16 + 16 * RND(3);
7236   }
7237   else if (element == EL_PACMAN)
7238   {
7239     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7240     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7241
7242     if (can_turn_left && can_turn_right)
7243       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7244     else if (can_turn_left)
7245       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7246     else if (can_turn_right)
7247       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7248     else
7249       MovDir[x][y] = back_dir;
7250
7251     MovDelay[x][y] = 6 + RND(40);
7252   }
7253   else if (element == EL_PIG)
7254   {
7255     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7256     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7257     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7258     boolean should_turn_left, should_turn_right, should_move_on;
7259     int rnd_value = 24;
7260     int rnd = RND(rnd_value);
7261
7262     should_turn_left = (can_turn_left &&
7263                         (!can_move_on ||
7264                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7265                                                    y + back_dy + left_dy)));
7266     should_turn_right = (can_turn_right &&
7267                          (!can_move_on ||
7268                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7269                                                     y + back_dy + right_dy)));
7270     should_move_on = (can_move_on &&
7271                       (!can_turn_left ||
7272                        !can_turn_right ||
7273                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7274                                                  y + move_dy + left_dy) ||
7275                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7276                                                  y + move_dy + right_dy)));
7277
7278     if (should_turn_left || should_turn_right || should_move_on)
7279     {
7280       if (should_turn_left && should_turn_right && should_move_on)
7281         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7282                         rnd < 2 * rnd_value / 3 ? right_dir :
7283                         old_move_dir);
7284       else if (should_turn_left && should_turn_right)
7285         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7286       else if (should_turn_left && should_move_on)
7287         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7288       else if (should_turn_right && should_move_on)
7289         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7290       else if (should_turn_left)
7291         MovDir[x][y] = left_dir;
7292       else if (should_turn_right)
7293         MovDir[x][y] = right_dir;
7294       else if (should_move_on)
7295         MovDir[x][y] = old_move_dir;
7296     }
7297     else if (can_move_on && rnd > rnd_value / 8)
7298       MovDir[x][y] = old_move_dir;
7299     else if (can_turn_left && can_turn_right)
7300       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7301     else if (can_turn_left && rnd > rnd_value / 8)
7302       MovDir[x][y] = left_dir;
7303     else if (can_turn_right && rnd > rnd_value/8)
7304       MovDir[x][y] = right_dir;
7305     else
7306       MovDir[x][y] = back_dir;
7307
7308     xx = x + move_xy[MovDir[x][y]].dx;
7309     yy = y + move_xy[MovDir[x][y]].dy;
7310
7311     if (!IN_LEV_FIELD(xx, yy) ||
7312         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7313       MovDir[x][y] = old_move_dir;
7314
7315     MovDelay[x][y] = 0;
7316   }
7317   else if (element == EL_DRAGON)
7318   {
7319     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7320     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7321     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7322     int rnd_value = 24;
7323     int rnd = RND(rnd_value);
7324
7325     if (can_move_on && rnd > rnd_value / 8)
7326       MovDir[x][y] = old_move_dir;
7327     else if (can_turn_left && can_turn_right)
7328       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7329     else if (can_turn_left && rnd > rnd_value / 8)
7330       MovDir[x][y] = left_dir;
7331     else if (can_turn_right && rnd > rnd_value / 8)
7332       MovDir[x][y] = right_dir;
7333     else
7334       MovDir[x][y] = back_dir;
7335
7336     xx = x + move_xy[MovDir[x][y]].dx;
7337     yy = y + move_xy[MovDir[x][y]].dy;
7338
7339     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7340       MovDir[x][y] = old_move_dir;
7341
7342     MovDelay[x][y] = 0;
7343   }
7344   else if (element == EL_MOLE)
7345   {
7346     boolean can_move_on =
7347       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7348                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7349                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7350     if (!can_move_on)
7351     {
7352       boolean can_turn_left =
7353         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7354                               IS_AMOEBOID(Tile[left_x][left_y])));
7355
7356       boolean can_turn_right =
7357         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7358                               IS_AMOEBOID(Tile[right_x][right_y])));
7359
7360       if (can_turn_left && can_turn_right)
7361         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7362       else if (can_turn_left)
7363         MovDir[x][y] = left_dir;
7364       else
7365         MovDir[x][y] = right_dir;
7366     }
7367
7368     if (MovDir[x][y] != old_move_dir)
7369       MovDelay[x][y] = 9;
7370   }
7371   else if (element == EL_BALLOON)
7372   {
7373     MovDir[x][y] = game.wind_direction;
7374     MovDelay[x][y] = 0;
7375   }
7376   else if (element == EL_SPRING)
7377   {
7378     if (MovDir[x][y] & MV_HORIZONTAL)
7379     {
7380       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7381           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7382       {
7383         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7384         ResetGfxAnimation(move_x, move_y);
7385         TEST_DrawLevelField(move_x, move_y);
7386
7387         MovDir[x][y] = back_dir;
7388       }
7389       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7390                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7391         MovDir[x][y] = MV_NONE;
7392     }
7393
7394     MovDelay[x][y] = 0;
7395   }
7396   else if (element == EL_ROBOT ||
7397            element == EL_SATELLITE ||
7398            element == EL_PENGUIN ||
7399            element == EL_EMC_ANDROID)
7400   {
7401     int attr_x = -1, attr_y = -1;
7402
7403     if (game.all_players_gone)
7404     {
7405       attr_x = game.exit_x;
7406       attr_y = game.exit_y;
7407     }
7408     else
7409     {
7410       int i;
7411
7412       for (i = 0; i < MAX_PLAYERS; i++)
7413       {
7414         struct PlayerInfo *player = &stored_player[i];
7415         int jx = player->jx, jy = player->jy;
7416
7417         if (!player->active)
7418           continue;
7419
7420         if (attr_x == -1 ||
7421             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7422         {
7423           attr_x = jx;
7424           attr_y = jy;
7425         }
7426       }
7427     }
7428
7429     if (element == EL_ROBOT &&
7430         game.robot_wheel_x >= 0 &&
7431         game.robot_wheel_y >= 0 &&
7432         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7433          game.engine_version < VERSION_IDENT(3,1,0,0)))
7434     {
7435       attr_x = game.robot_wheel_x;
7436       attr_y = game.robot_wheel_y;
7437     }
7438
7439     if (element == EL_PENGUIN)
7440     {
7441       int i;
7442       struct XY *xy = xy_topdown;
7443
7444       for (i = 0; i < NUM_DIRECTIONS; i++)
7445       {
7446         int ex = x + xy[i].x;
7447         int ey = y + xy[i].y;
7448
7449         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7450                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7451                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7452                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7453         {
7454           attr_x = ex;
7455           attr_y = ey;
7456           break;
7457         }
7458       }
7459     }
7460
7461     MovDir[x][y] = MV_NONE;
7462     if (attr_x < x)
7463       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7464     else if (attr_x > x)
7465       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7466     if (attr_y < y)
7467       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7468     else if (attr_y > y)
7469       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7470
7471     if (element == EL_ROBOT)
7472     {
7473       int newx, newy;
7474
7475       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7476         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7477       Moving2Blocked(x, y, &newx, &newy);
7478
7479       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7480         MovDelay[x][y] = 8 + 8 * !RND(3);
7481       else
7482         MovDelay[x][y] = 16;
7483     }
7484     else if (element == EL_PENGUIN)
7485     {
7486       int newx, newy;
7487
7488       MovDelay[x][y] = 1;
7489
7490       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7491       {
7492         boolean first_horiz = RND(2);
7493         int new_move_dir = MovDir[x][y];
7494
7495         MovDir[x][y] =
7496           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7497         Moving2Blocked(x, y, &newx, &newy);
7498
7499         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7500           return;
7501
7502         MovDir[x][y] =
7503           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7504         Moving2Blocked(x, y, &newx, &newy);
7505
7506         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7507           return;
7508
7509         MovDir[x][y] = old_move_dir;
7510         return;
7511       }
7512     }
7513     else if (element == EL_SATELLITE)
7514     {
7515       int newx, newy;
7516
7517       MovDelay[x][y] = 1;
7518
7519       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7520       {
7521         boolean first_horiz = RND(2);
7522         int new_move_dir = MovDir[x][y];
7523
7524         MovDir[x][y] =
7525           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7526         Moving2Blocked(x, y, &newx, &newy);
7527
7528         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7529           return;
7530
7531         MovDir[x][y] =
7532           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7533         Moving2Blocked(x, y, &newx, &newy);
7534
7535         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7536           return;
7537
7538         MovDir[x][y] = old_move_dir;
7539         return;
7540       }
7541     }
7542     else if (element == EL_EMC_ANDROID)
7543     {
7544       static int check_pos[16] =
7545       {
7546         -1,             //  0 => (invalid)
7547         7,              //  1 => MV_LEFT
7548         3,              //  2 => MV_RIGHT
7549         -1,             //  3 => (invalid)
7550         1,              //  4 =>            MV_UP
7551         0,              //  5 => MV_LEFT  | MV_UP
7552         2,              //  6 => MV_RIGHT | MV_UP
7553         -1,             //  7 => (invalid)
7554         5,              //  8 =>            MV_DOWN
7555         6,              //  9 => MV_LEFT  | MV_DOWN
7556         4,              // 10 => MV_RIGHT | MV_DOWN
7557         -1,             // 11 => (invalid)
7558         -1,             // 12 => (invalid)
7559         -1,             // 13 => (invalid)
7560         -1,             // 14 => (invalid)
7561         -1,             // 15 => (invalid)
7562       };
7563       static struct
7564       {
7565         int dx, dy;
7566         int dir;
7567       } check_xy[8] =
7568       {
7569         { -1, -1,       MV_LEFT  | MV_UP   },
7570         {  0, -1,                  MV_UP   },
7571         { +1, -1,       MV_RIGHT | MV_UP   },
7572         { +1,  0,       MV_RIGHT           },
7573         { +1, +1,       MV_RIGHT | MV_DOWN },
7574         {  0, +1,                  MV_DOWN },
7575         { -1, +1,       MV_LEFT  | MV_DOWN },
7576         { -1,  0,       MV_LEFT            },
7577       };
7578       int start_pos, check_order;
7579       boolean can_clone = FALSE;
7580       int i;
7581
7582       // check if there is any free field around current position
7583       for (i = 0; i < 8; i++)
7584       {
7585         int newx = x + check_xy[i].dx;
7586         int newy = y + check_xy[i].dy;
7587
7588         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7589         {
7590           can_clone = TRUE;
7591
7592           break;
7593         }
7594       }
7595
7596       if (can_clone)            // randomly find an element to clone
7597       {
7598         can_clone = FALSE;
7599
7600         start_pos = check_pos[RND(8)];
7601         check_order = (RND(2) ? -1 : +1);
7602
7603         for (i = 0; i < 8; i++)
7604         {
7605           int pos_raw = start_pos + i * check_order;
7606           int pos = (pos_raw + 8) % 8;
7607           int newx = x + check_xy[pos].dx;
7608           int newy = y + check_xy[pos].dy;
7609
7610           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7611           {
7612             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7613             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7614
7615             Store[x][y] = Tile[newx][newy];
7616
7617             can_clone = TRUE;
7618
7619             break;
7620           }
7621         }
7622       }
7623
7624       if (can_clone)            // randomly find a direction to move
7625       {
7626         can_clone = FALSE;
7627
7628         start_pos = check_pos[RND(8)];
7629         check_order = (RND(2) ? -1 : +1);
7630
7631         for (i = 0; i < 8; i++)
7632         {
7633           int pos_raw = start_pos + i * check_order;
7634           int pos = (pos_raw + 8) % 8;
7635           int newx = x + check_xy[pos].dx;
7636           int newy = y + check_xy[pos].dy;
7637           int new_move_dir = check_xy[pos].dir;
7638
7639           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7640           {
7641             MovDir[x][y] = new_move_dir;
7642             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7643
7644             can_clone = TRUE;
7645
7646             break;
7647           }
7648         }
7649       }
7650
7651       if (can_clone)            // cloning and moving successful
7652         return;
7653
7654       // cannot clone -- try to move towards player
7655
7656       start_pos = check_pos[MovDir[x][y] & 0x0f];
7657       check_order = (RND(2) ? -1 : +1);
7658
7659       for (i = 0; i < 3; i++)
7660       {
7661         // first check start_pos, then previous/next or (next/previous) pos
7662         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7663         int pos = (pos_raw + 8) % 8;
7664         int newx = x + check_xy[pos].dx;
7665         int newy = y + check_xy[pos].dy;
7666         int new_move_dir = check_xy[pos].dir;
7667
7668         if (IS_PLAYER(newx, newy))
7669           break;
7670
7671         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7672         {
7673           MovDir[x][y] = new_move_dir;
7674           MovDelay[x][y] = level.android_move_time * 8 + 1;
7675
7676           break;
7677         }
7678       }
7679     }
7680   }
7681   else if (move_pattern == MV_TURNING_LEFT ||
7682            move_pattern == MV_TURNING_RIGHT ||
7683            move_pattern == MV_TURNING_LEFT_RIGHT ||
7684            move_pattern == MV_TURNING_RIGHT_LEFT ||
7685            move_pattern == MV_TURNING_RANDOM ||
7686            move_pattern == MV_ALL_DIRECTIONS)
7687   {
7688     boolean can_turn_left =
7689       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7690     boolean can_turn_right =
7691       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7692
7693     if (element_info[element].move_stepsize == 0)       // "not moving"
7694       return;
7695
7696     if (move_pattern == MV_TURNING_LEFT)
7697       MovDir[x][y] = left_dir;
7698     else if (move_pattern == MV_TURNING_RIGHT)
7699       MovDir[x][y] = right_dir;
7700     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7701       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7702     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7703       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7704     else if (move_pattern == MV_TURNING_RANDOM)
7705       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7706                       can_turn_right && !can_turn_left ? right_dir :
7707                       RND(2) ? left_dir : right_dir);
7708     else if (can_turn_left && can_turn_right)
7709       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7710     else if (can_turn_left)
7711       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7712     else if (can_turn_right)
7713       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7714     else
7715       MovDir[x][y] = back_dir;
7716
7717     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7718   }
7719   else if (move_pattern == MV_HORIZONTAL ||
7720            move_pattern == MV_VERTICAL)
7721   {
7722     if (move_pattern & old_move_dir)
7723       MovDir[x][y] = back_dir;
7724     else if (move_pattern == MV_HORIZONTAL)
7725       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7726     else if (move_pattern == MV_VERTICAL)
7727       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7728
7729     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7730   }
7731   else if (move_pattern & MV_ANY_DIRECTION)
7732   {
7733     MovDir[x][y] = move_pattern;
7734     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7735   }
7736   else if (move_pattern & MV_WIND_DIRECTION)
7737   {
7738     MovDir[x][y] = game.wind_direction;
7739     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7740   }
7741   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7742   {
7743     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7744       MovDir[x][y] = left_dir;
7745     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7746       MovDir[x][y] = right_dir;
7747
7748     if (MovDir[x][y] != old_move_dir)
7749       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7750   }
7751   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7752   {
7753     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7754       MovDir[x][y] = right_dir;
7755     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7756       MovDir[x][y] = left_dir;
7757
7758     if (MovDir[x][y] != old_move_dir)
7759       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7760   }
7761   else if (move_pattern == MV_TOWARDS_PLAYER ||
7762            move_pattern == MV_AWAY_FROM_PLAYER)
7763   {
7764     int attr_x = -1, attr_y = -1;
7765     int newx, newy;
7766     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7767
7768     if (game.all_players_gone)
7769     {
7770       attr_x = game.exit_x;
7771       attr_y = game.exit_y;
7772     }
7773     else
7774     {
7775       int i;
7776
7777       for (i = 0; i < MAX_PLAYERS; i++)
7778       {
7779         struct PlayerInfo *player = &stored_player[i];
7780         int jx = player->jx, jy = player->jy;
7781
7782         if (!player->active)
7783           continue;
7784
7785         if (attr_x == -1 ||
7786             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7787         {
7788           attr_x = jx;
7789           attr_y = jy;
7790         }
7791       }
7792     }
7793
7794     MovDir[x][y] = MV_NONE;
7795     if (attr_x < x)
7796       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7797     else if (attr_x > x)
7798       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7799     if (attr_y < y)
7800       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7801     else if (attr_y > y)
7802       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7803
7804     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7805
7806     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7807     {
7808       boolean first_horiz = RND(2);
7809       int new_move_dir = MovDir[x][y];
7810
7811       if (element_info[element].move_stepsize == 0)     // "not moving"
7812       {
7813         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7814         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7815
7816         return;
7817       }
7818
7819       MovDir[x][y] =
7820         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7821       Moving2Blocked(x, y, &newx, &newy);
7822
7823       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7824         return;
7825
7826       MovDir[x][y] =
7827         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7828       Moving2Blocked(x, y, &newx, &newy);
7829
7830       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7831         return;
7832
7833       MovDir[x][y] = old_move_dir;
7834     }
7835   }
7836   else if (move_pattern == MV_WHEN_PUSHED ||
7837            move_pattern == MV_WHEN_DROPPED)
7838   {
7839     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7840       MovDir[x][y] = MV_NONE;
7841
7842     MovDelay[x][y] = 0;
7843   }
7844   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7845   {
7846     struct XY *test_xy = xy_topdown;
7847     static int test_dir[4] =
7848     {
7849       MV_UP,
7850       MV_LEFT,
7851       MV_RIGHT,
7852       MV_DOWN
7853     };
7854     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7855     int move_preference = -1000000;     // start with very low preference
7856     int new_move_dir = MV_NONE;
7857     int start_test = RND(4);
7858     int i;
7859
7860     for (i = 0; i < NUM_DIRECTIONS; i++)
7861     {
7862       int j = (start_test + i) % 4;
7863       int move_dir = test_dir[j];
7864       int move_dir_preference;
7865
7866       xx = x + test_xy[j].x;
7867       yy = y + test_xy[j].y;
7868
7869       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7870           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7871       {
7872         new_move_dir = move_dir;
7873
7874         break;
7875       }
7876
7877       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7878         continue;
7879
7880       move_dir_preference = -1 * RunnerVisit[xx][yy];
7881       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7882         move_dir_preference = PlayerVisit[xx][yy];
7883
7884       if (move_dir_preference > move_preference)
7885       {
7886         // prefer field that has not been visited for the longest time
7887         move_preference = move_dir_preference;
7888         new_move_dir = move_dir;
7889       }
7890       else if (move_dir_preference == move_preference &&
7891                move_dir == old_move_dir)
7892       {
7893         // prefer last direction when all directions are preferred equally
7894         move_preference = move_dir_preference;
7895         new_move_dir = move_dir;
7896       }
7897     }
7898
7899     MovDir[x][y] = new_move_dir;
7900     if (old_move_dir != new_move_dir)
7901       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7902   }
7903 }
7904
7905 static void TurnRound(int x, int y)
7906 {
7907   int direction = MovDir[x][y];
7908
7909   TurnRoundExt(x, y);
7910
7911   GfxDir[x][y] = MovDir[x][y];
7912
7913   if (direction != MovDir[x][y])
7914     GfxFrame[x][y] = 0;
7915
7916   if (MovDelay[x][y])
7917     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7918
7919   ResetGfxFrame(x, y);
7920 }
7921
7922 static boolean JustBeingPushed(int x, int y)
7923 {
7924   int i;
7925
7926   for (i = 0; i < MAX_PLAYERS; i++)
7927   {
7928     struct PlayerInfo *player = &stored_player[i];
7929
7930     if (player->active && player->is_pushing && player->MovPos)
7931     {
7932       int next_jx = player->jx + (player->jx - player->last_jx);
7933       int next_jy = player->jy + (player->jy - player->last_jy);
7934
7935       if (x == next_jx && y == next_jy)
7936         return TRUE;
7937     }
7938   }
7939
7940   return FALSE;
7941 }
7942
7943 static void StartMoving(int x, int y)
7944 {
7945   boolean started_moving = FALSE;       // some elements can fall _and_ move
7946   int element = Tile[x][y];
7947
7948   if (Stop[x][y])
7949     return;
7950
7951   if (MovDelay[x][y] == 0)
7952     GfxAction[x][y] = ACTION_DEFAULT;
7953
7954   if (CAN_FALL(element) && y < lev_fieldy - 1)
7955   {
7956     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7957         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7958       if (JustBeingPushed(x, y))
7959         return;
7960
7961     if (element == EL_QUICKSAND_FULL)
7962     {
7963       if (IS_FREE(x, y + 1))
7964       {
7965         InitMovingField(x, y, MV_DOWN);
7966         started_moving = TRUE;
7967
7968         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7969 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7970         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7971           Store[x][y] = EL_ROCK;
7972 #else
7973         Store[x][y] = EL_ROCK;
7974 #endif
7975
7976         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7977       }
7978       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7979       {
7980         if (!MovDelay[x][y])
7981         {
7982           MovDelay[x][y] = TILEY + 1;
7983
7984           ResetGfxAnimation(x, y);
7985           ResetGfxAnimation(x, y + 1);
7986         }
7987
7988         if (MovDelay[x][y])
7989         {
7990           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7991           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7992
7993           MovDelay[x][y]--;
7994           if (MovDelay[x][y])
7995             return;
7996         }
7997
7998         Tile[x][y] = EL_QUICKSAND_EMPTY;
7999         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8000         Store[x][y + 1] = Store[x][y];
8001         Store[x][y] = 0;
8002
8003         PlayLevelSoundAction(x, y, ACTION_FILLING);
8004       }
8005       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8006       {
8007         if (!MovDelay[x][y])
8008         {
8009           MovDelay[x][y] = TILEY + 1;
8010
8011           ResetGfxAnimation(x, y);
8012           ResetGfxAnimation(x, y + 1);
8013         }
8014
8015         if (MovDelay[x][y])
8016         {
8017           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8018           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8019
8020           MovDelay[x][y]--;
8021           if (MovDelay[x][y])
8022             return;
8023         }
8024
8025         Tile[x][y] = EL_QUICKSAND_EMPTY;
8026         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8027         Store[x][y + 1] = Store[x][y];
8028         Store[x][y] = 0;
8029
8030         PlayLevelSoundAction(x, y, ACTION_FILLING);
8031       }
8032     }
8033     else if (element == EL_QUICKSAND_FAST_FULL)
8034     {
8035       if (IS_FREE(x, y + 1))
8036       {
8037         InitMovingField(x, y, MV_DOWN);
8038         started_moving = TRUE;
8039
8040         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8041 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8042         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8043           Store[x][y] = EL_ROCK;
8044 #else
8045         Store[x][y] = EL_ROCK;
8046 #endif
8047
8048         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8049       }
8050       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8051       {
8052         if (!MovDelay[x][y])
8053         {
8054           MovDelay[x][y] = TILEY + 1;
8055
8056           ResetGfxAnimation(x, y);
8057           ResetGfxAnimation(x, y + 1);
8058         }
8059
8060         if (MovDelay[x][y])
8061         {
8062           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8063           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8064
8065           MovDelay[x][y]--;
8066           if (MovDelay[x][y])
8067             return;
8068         }
8069
8070         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8071         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8072         Store[x][y + 1] = Store[x][y];
8073         Store[x][y] = 0;
8074
8075         PlayLevelSoundAction(x, y, ACTION_FILLING);
8076       }
8077       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8078       {
8079         if (!MovDelay[x][y])
8080         {
8081           MovDelay[x][y] = TILEY + 1;
8082
8083           ResetGfxAnimation(x, y);
8084           ResetGfxAnimation(x, y + 1);
8085         }
8086
8087         if (MovDelay[x][y])
8088         {
8089           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8090           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8091
8092           MovDelay[x][y]--;
8093           if (MovDelay[x][y])
8094             return;
8095         }
8096
8097         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8098         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8099         Store[x][y + 1] = Store[x][y];
8100         Store[x][y] = 0;
8101
8102         PlayLevelSoundAction(x, y, ACTION_FILLING);
8103       }
8104     }
8105     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8106              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8107     {
8108       InitMovingField(x, y, MV_DOWN);
8109       started_moving = TRUE;
8110
8111       Tile[x][y] = EL_QUICKSAND_FILLING;
8112       Store[x][y] = element;
8113
8114       PlayLevelSoundAction(x, y, ACTION_FILLING);
8115     }
8116     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8117              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8118     {
8119       InitMovingField(x, y, MV_DOWN);
8120       started_moving = TRUE;
8121
8122       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8123       Store[x][y] = element;
8124
8125       PlayLevelSoundAction(x, y, ACTION_FILLING);
8126     }
8127     else if (element == EL_MAGIC_WALL_FULL)
8128     {
8129       if (IS_FREE(x, y + 1))
8130       {
8131         InitMovingField(x, y, MV_DOWN);
8132         started_moving = TRUE;
8133
8134         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8135         Store[x][y] = EL_CHANGED(Store[x][y]);
8136       }
8137       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8138       {
8139         if (!MovDelay[x][y])
8140           MovDelay[x][y] = TILEY / 4 + 1;
8141
8142         if (MovDelay[x][y])
8143         {
8144           MovDelay[x][y]--;
8145           if (MovDelay[x][y])
8146             return;
8147         }
8148
8149         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8150         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8151         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8152         Store[x][y] = 0;
8153       }
8154     }
8155     else if (element == EL_BD_MAGIC_WALL_FULL)
8156     {
8157       if (IS_FREE(x, y + 1))
8158       {
8159         InitMovingField(x, y, MV_DOWN);
8160         started_moving = TRUE;
8161
8162         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8163         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8164       }
8165       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8166       {
8167         if (!MovDelay[x][y])
8168           MovDelay[x][y] = TILEY / 4 + 1;
8169
8170         if (MovDelay[x][y])
8171         {
8172           MovDelay[x][y]--;
8173           if (MovDelay[x][y])
8174             return;
8175         }
8176
8177         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8178         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8179         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8180         Store[x][y] = 0;
8181       }
8182     }
8183     else if (element == EL_DC_MAGIC_WALL_FULL)
8184     {
8185       if (IS_FREE(x, y + 1))
8186       {
8187         InitMovingField(x, y, MV_DOWN);
8188         started_moving = TRUE;
8189
8190         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8191         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8192       }
8193       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8194       {
8195         if (!MovDelay[x][y])
8196           MovDelay[x][y] = TILEY / 4 + 1;
8197
8198         if (MovDelay[x][y])
8199         {
8200           MovDelay[x][y]--;
8201           if (MovDelay[x][y])
8202             return;
8203         }
8204
8205         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8206         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8207         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8208         Store[x][y] = 0;
8209       }
8210     }
8211     else if ((CAN_PASS_MAGIC_WALL(element) &&
8212               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8213                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8214              (CAN_PASS_DC_MAGIC_WALL(element) &&
8215               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8216
8217     {
8218       InitMovingField(x, y, MV_DOWN);
8219       started_moving = TRUE;
8220
8221       Tile[x][y] =
8222         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8223          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8224          EL_DC_MAGIC_WALL_FILLING);
8225       Store[x][y] = element;
8226     }
8227     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8228     {
8229       SplashAcid(x, y + 1);
8230
8231       InitMovingField(x, y, MV_DOWN);
8232       started_moving = TRUE;
8233
8234       Store[x][y] = EL_ACID;
8235     }
8236     else if (
8237              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8238               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8239              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8240               CAN_FALL(element) && WasJustFalling[x][y] &&
8241               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8242
8243              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8244               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8245               (Tile[x][y + 1] == EL_BLOCKED)))
8246     {
8247       /* this is needed for a special case not covered by calling "Impact()"
8248          from "ContinueMoving()": if an element moves to a tile directly below
8249          another element which was just falling on that tile (which was empty
8250          in the previous frame), the falling element above would just stop
8251          instead of smashing the element below (in previous version, the above
8252          element was just checked for "moving" instead of "falling", resulting
8253          in incorrect smashes caused by horizontal movement of the above
8254          element; also, the case of the player being the element to smash was
8255          simply not covered here... :-/ ) */
8256
8257       CheckCollision[x][y] = 0;
8258       CheckImpact[x][y] = 0;
8259
8260       Impact(x, y);
8261     }
8262     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8263     {
8264       if (MovDir[x][y] == MV_NONE)
8265       {
8266         InitMovingField(x, y, MV_DOWN);
8267         started_moving = TRUE;
8268       }
8269     }
8270     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8271     {
8272       if (WasJustFalling[x][y]) // prevent animation from being restarted
8273         MovDir[x][y] = MV_DOWN;
8274
8275       InitMovingField(x, y, MV_DOWN);
8276       started_moving = TRUE;
8277     }
8278     else if (element == EL_AMOEBA_DROP)
8279     {
8280       Tile[x][y] = EL_AMOEBA_GROWING;
8281       Store[x][y] = EL_AMOEBA_WET;
8282     }
8283     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8284               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8285              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8286              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8287     {
8288       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8289                                 (IS_FREE(x - 1, y + 1) ||
8290                                  Tile[x - 1][y + 1] == EL_ACID));
8291       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8292                                 (IS_FREE(x + 1, y + 1) ||
8293                                  Tile[x + 1][y + 1] == EL_ACID));
8294       boolean can_fall_any  = (can_fall_left || can_fall_right);
8295       boolean can_fall_both = (can_fall_left && can_fall_right);
8296       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8297
8298       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8299       {
8300         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8301           can_fall_right = FALSE;
8302         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8303           can_fall_left = FALSE;
8304         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8305           can_fall_right = FALSE;
8306         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8307           can_fall_left = FALSE;
8308
8309         can_fall_any  = (can_fall_left || can_fall_right);
8310         can_fall_both = FALSE;
8311       }
8312
8313       if (can_fall_both)
8314       {
8315         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8316           can_fall_right = FALSE;       // slip down on left side
8317         else
8318           can_fall_left = !(can_fall_right = RND(2));
8319
8320         can_fall_both = FALSE;
8321       }
8322
8323       if (can_fall_any)
8324       {
8325         // if not determined otherwise, prefer left side for slipping down
8326         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8327         started_moving = TRUE;
8328       }
8329     }
8330     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8331     {
8332       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8333       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8334       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8335       int belt_dir = game.belt_dir[belt_nr];
8336
8337       if ((belt_dir == MV_LEFT  && left_is_free) ||
8338           (belt_dir == MV_RIGHT && right_is_free))
8339       {
8340         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8341
8342         InitMovingField(x, y, belt_dir);
8343         started_moving = TRUE;
8344
8345         Pushed[x][y] = TRUE;
8346         Pushed[nextx][y] = TRUE;
8347
8348         GfxAction[x][y] = ACTION_DEFAULT;
8349       }
8350       else
8351       {
8352         MovDir[x][y] = 0;       // if element was moving, stop it
8353       }
8354     }
8355   }
8356
8357   // not "else if" because of elements that can fall and move (EL_SPRING)
8358   if (CAN_MOVE(element) && !started_moving)
8359   {
8360     int move_pattern = element_info[element].move_pattern;
8361     int newx, newy;
8362
8363     Moving2Blocked(x, y, &newx, &newy);
8364
8365     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8366       return;
8367
8368     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8369         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8370     {
8371       WasJustMoving[x][y] = 0;
8372       CheckCollision[x][y] = 0;
8373
8374       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8375
8376       if (Tile[x][y] != element)        // element has changed
8377         return;
8378     }
8379
8380     if (!MovDelay[x][y])        // start new movement phase
8381     {
8382       // all objects that can change their move direction after each step
8383       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8384
8385       if (element != EL_YAMYAM &&
8386           element != EL_DARK_YAMYAM &&
8387           element != EL_PACMAN &&
8388           !(move_pattern & MV_ANY_DIRECTION) &&
8389           move_pattern != MV_TURNING_LEFT &&
8390           move_pattern != MV_TURNING_RIGHT &&
8391           move_pattern != MV_TURNING_LEFT_RIGHT &&
8392           move_pattern != MV_TURNING_RIGHT_LEFT &&
8393           move_pattern != MV_TURNING_RANDOM)
8394       {
8395         TurnRound(x, y);
8396
8397         if (MovDelay[x][y] && (element == EL_BUG ||
8398                                element == EL_SPACESHIP ||
8399                                element == EL_SP_SNIKSNAK ||
8400                                element == EL_SP_ELECTRON ||
8401                                element == EL_MOLE))
8402           TEST_DrawLevelField(x, y);
8403       }
8404     }
8405
8406     if (MovDelay[x][y])         // wait some time before next movement
8407     {
8408       MovDelay[x][y]--;
8409
8410       if (element == EL_ROBOT ||
8411           element == EL_YAMYAM ||
8412           element == EL_DARK_YAMYAM)
8413       {
8414         DrawLevelElementAnimationIfNeeded(x, y, element);
8415         PlayLevelSoundAction(x, y, ACTION_WAITING);
8416       }
8417       else if (element == EL_SP_ELECTRON)
8418         DrawLevelElementAnimationIfNeeded(x, y, element);
8419       else if (element == EL_DRAGON)
8420       {
8421         int i;
8422         int dir = MovDir[x][y];
8423         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8424         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8425         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8426                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8427                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8428                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8429         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8430
8431         GfxAction[x][y] = ACTION_ATTACKING;
8432
8433         if (IS_PLAYER(x, y))
8434           DrawPlayerField(x, y);
8435         else
8436           TEST_DrawLevelField(x, y);
8437
8438         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8439
8440         for (i = 1; i <= 3; i++)
8441         {
8442           int xx = x + i * dx;
8443           int yy = y + i * dy;
8444           int sx = SCREENX(xx);
8445           int sy = SCREENY(yy);
8446           int flame_graphic = graphic + (i - 1);
8447
8448           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8449             break;
8450
8451           if (MovDelay[x][y])
8452           {
8453             int flamed = MovingOrBlocked2Element(xx, yy);
8454
8455             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8456               Bang(xx, yy);
8457             else
8458               RemoveMovingField(xx, yy);
8459
8460             ChangeDelay[xx][yy] = 0;
8461
8462             Tile[xx][yy] = EL_FLAMES;
8463
8464             if (IN_SCR_FIELD(sx, sy))
8465             {
8466               TEST_DrawLevelFieldCrumbled(xx, yy);
8467               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8468             }
8469           }
8470           else
8471           {
8472             if (Tile[xx][yy] == EL_FLAMES)
8473               Tile[xx][yy] = EL_EMPTY;
8474             TEST_DrawLevelField(xx, yy);
8475           }
8476         }
8477       }
8478
8479       if (MovDelay[x][y])       // element still has to wait some time
8480       {
8481         PlayLevelSoundAction(x, y, ACTION_WAITING);
8482
8483         return;
8484       }
8485     }
8486
8487     // now make next step
8488
8489     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8490
8491     if (DONT_COLLIDE_WITH(element) &&
8492         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8493         !PLAYER_ENEMY_PROTECTED(newx, newy))
8494     {
8495       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8496
8497       return;
8498     }
8499
8500     else if (CAN_MOVE_INTO_ACID(element) &&
8501              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8502              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8503              (MovDir[x][y] == MV_DOWN ||
8504               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8505     {
8506       SplashAcid(newx, newy);
8507       Store[x][y] = EL_ACID;
8508     }
8509     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8510     {
8511       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8512           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8513           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8514           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8515       {
8516         RemoveField(x, y);
8517         TEST_DrawLevelField(x, y);
8518
8519         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8520         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8521           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8522
8523         game.friends_still_needed--;
8524         if (!game.friends_still_needed &&
8525             !game.GameOver &&
8526             game.all_players_gone)
8527           LevelSolved();
8528
8529         return;
8530       }
8531       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8532       {
8533         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8534           TEST_DrawLevelField(newx, newy);
8535         else
8536           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8537       }
8538       else if (!IS_FREE(newx, newy))
8539       {
8540         GfxAction[x][y] = ACTION_WAITING;
8541
8542         if (IS_PLAYER(x, y))
8543           DrawPlayerField(x, y);
8544         else
8545           TEST_DrawLevelField(x, y);
8546
8547         return;
8548       }
8549     }
8550     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8551     {
8552       if (IS_FOOD_PIG(Tile[newx][newy]))
8553       {
8554         if (IS_MOVING(newx, newy))
8555           RemoveMovingField(newx, newy);
8556         else
8557         {
8558           Tile[newx][newy] = EL_EMPTY;
8559           TEST_DrawLevelField(newx, newy);
8560         }
8561
8562         PlayLevelSound(x, y, SND_PIG_DIGGING);
8563       }
8564       else if (!IS_FREE(newx, newy))
8565       {
8566         if (IS_PLAYER(x, y))
8567           DrawPlayerField(x, y);
8568         else
8569           TEST_DrawLevelField(x, y);
8570
8571         return;
8572       }
8573     }
8574     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8575     {
8576       if (Store[x][y] != EL_EMPTY)
8577       {
8578         boolean can_clone = FALSE;
8579         int xx, yy;
8580
8581         // check if element to clone is still there
8582         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8583         {
8584           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8585           {
8586             can_clone = TRUE;
8587
8588             break;
8589           }
8590         }
8591
8592         // cannot clone or target field not free anymore -- do not clone
8593         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8594           Store[x][y] = EL_EMPTY;
8595       }
8596
8597       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8598       {
8599         if (IS_MV_DIAGONAL(MovDir[x][y]))
8600         {
8601           int diagonal_move_dir = MovDir[x][y];
8602           int stored = Store[x][y];
8603           int change_delay = 8;
8604           int graphic;
8605
8606           // android is moving diagonally
8607
8608           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8609
8610           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8611           GfxElement[x][y] = EL_EMC_ANDROID;
8612           GfxAction[x][y] = ACTION_SHRINKING;
8613           GfxDir[x][y] = diagonal_move_dir;
8614           ChangeDelay[x][y] = change_delay;
8615
8616           if (Store[x][y] == EL_EMPTY)
8617             Store[x][y] = GfxElementEmpty[x][y];
8618
8619           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8620                                    GfxDir[x][y]);
8621
8622           DrawLevelGraphicAnimation(x, y, graphic);
8623           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8624
8625           if (Tile[newx][newy] == EL_ACID)
8626           {
8627             SplashAcid(newx, newy);
8628
8629             return;
8630           }
8631
8632           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8633
8634           Store[newx][newy] = EL_EMC_ANDROID;
8635           GfxElement[newx][newy] = EL_EMC_ANDROID;
8636           GfxAction[newx][newy] = ACTION_GROWING;
8637           GfxDir[newx][newy] = diagonal_move_dir;
8638           ChangeDelay[newx][newy] = change_delay;
8639
8640           graphic = el_act_dir2img(GfxElement[newx][newy],
8641                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8642
8643           DrawLevelGraphicAnimation(newx, newy, graphic);
8644           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8645
8646           return;
8647         }
8648         else
8649         {
8650           Tile[newx][newy] = EL_EMPTY;
8651           TEST_DrawLevelField(newx, newy);
8652
8653           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8654         }
8655       }
8656       else if (!IS_FREE(newx, newy))
8657       {
8658         return;
8659       }
8660     }
8661     else if (IS_CUSTOM_ELEMENT(element) &&
8662              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8663     {
8664       if (!DigFieldByCE(newx, newy, element))
8665         return;
8666
8667       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8668       {
8669         RunnerVisit[x][y] = FrameCounter;
8670         PlayerVisit[x][y] /= 8;         // expire player visit path
8671       }
8672     }
8673     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8674     {
8675       if (!IS_FREE(newx, newy))
8676       {
8677         if (IS_PLAYER(x, y))
8678           DrawPlayerField(x, y);
8679         else
8680           TEST_DrawLevelField(x, y);
8681
8682         return;
8683       }
8684       else
8685       {
8686         boolean wanna_flame = !RND(10);
8687         int dx = newx - x, dy = newy - y;
8688         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8689         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8690         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8691                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8692         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8693                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8694
8695         if ((wanna_flame ||
8696              IS_CLASSIC_ENEMY(element1) ||
8697              IS_CLASSIC_ENEMY(element2)) &&
8698             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8699             element1 != EL_FLAMES && element2 != EL_FLAMES)
8700         {
8701           ResetGfxAnimation(x, y);
8702           GfxAction[x][y] = ACTION_ATTACKING;
8703
8704           if (IS_PLAYER(x, y))
8705             DrawPlayerField(x, y);
8706           else
8707             TEST_DrawLevelField(x, y);
8708
8709           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8710
8711           MovDelay[x][y] = 50;
8712
8713           Tile[newx][newy] = EL_FLAMES;
8714           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8715             Tile[newx1][newy1] = EL_FLAMES;
8716           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8717             Tile[newx2][newy2] = EL_FLAMES;
8718
8719           return;
8720         }
8721       }
8722     }
8723     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8724              Tile[newx][newy] == EL_DIAMOND)
8725     {
8726       if (IS_MOVING(newx, newy))
8727         RemoveMovingField(newx, newy);
8728       else
8729       {
8730         Tile[newx][newy] = EL_EMPTY;
8731         TEST_DrawLevelField(newx, newy);
8732       }
8733
8734       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8735     }
8736     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8737              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8738     {
8739       if (AmoebaNr[newx][newy])
8740       {
8741         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8742         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8743             Tile[newx][newy] == EL_BD_AMOEBA)
8744           AmoebaCnt[AmoebaNr[newx][newy]]--;
8745       }
8746
8747       if (IS_MOVING(newx, newy))
8748       {
8749         RemoveMovingField(newx, newy);
8750       }
8751       else
8752       {
8753         Tile[newx][newy] = EL_EMPTY;
8754         TEST_DrawLevelField(newx, newy);
8755       }
8756
8757       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8758     }
8759     else if ((element == EL_PACMAN || element == EL_MOLE)
8760              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8761     {
8762       if (AmoebaNr[newx][newy])
8763       {
8764         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8765         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8766             Tile[newx][newy] == EL_BD_AMOEBA)
8767           AmoebaCnt[AmoebaNr[newx][newy]]--;
8768       }
8769
8770       if (element == EL_MOLE)
8771       {
8772         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8773         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8774
8775         ResetGfxAnimation(x, y);
8776         GfxAction[x][y] = ACTION_DIGGING;
8777         TEST_DrawLevelField(x, y);
8778
8779         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8780
8781         return;                         // wait for shrinking amoeba
8782       }
8783       else      // element == EL_PACMAN
8784       {
8785         Tile[newx][newy] = EL_EMPTY;
8786         TEST_DrawLevelField(newx, newy);
8787         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8788       }
8789     }
8790     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8791              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8792               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8793     {
8794       // wait for shrinking amoeba to completely disappear
8795       return;
8796     }
8797     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8798     {
8799       // object was running against a wall
8800
8801       TurnRound(x, y);
8802
8803       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8804         DrawLevelElementAnimation(x, y, element);
8805
8806       if (DONT_TOUCH(element))
8807         TestIfBadThingTouchesPlayer(x, y);
8808
8809       return;
8810     }
8811
8812     InitMovingField(x, y, MovDir[x][y]);
8813
8814     PlayLevelSoundAction(x, y, ACTION_MOVING);
8815   }
8816
8817   if (MovDir[x][y])
8818     ContinueMoving(x, y);
8819 }
8820
8821 void ContinueMoving(int x, int y)
8822 {
8823   int element = Tile[x][y];
8824   struct ElementInfo *ei = &element_info[element];
8825   int direction = MovDir[x][y];
8826   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8827   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8828   int newx = x + dx, newy = y + dy;
8829   int stored = Store[x][y];
8830   int stored_new = Store[newx][newy];
8831   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8832   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8833   boolean last_line = (newy == lev_fieldy - 1);
8834   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8835
8836   if (pushed_by_player)         // special case: moving object pushed by player
8837   {
8838     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8839   }
8840   else if (use_step_delay)      // special case: moving object has step delay
8841   {
8842     if (!MovDelay[x][y])
8843       MovPos[x][y] += getElementMoveStepsize(x, y);
8844
8845     if (MovDelay[x][y])
8846       MovDelay[x][y]--;
8847     else
8848       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8849
8850     if (MovDelay[x][y])
8851     {
8852       TEST_DrawLevelField(x, y);
8853
8854       return;   // element is still waiting
8855     }
8856   }
8857   else                          // normal case: generically moving object
8858   {
8859     MovPos[x][y] += getElementMoveStepsize(x, y);
8860   }
8861
8862   if (ABS(MovPos[x][y]) < TILEX)
8863   {
8864     TEST_DrawLevelField(x, y);
8865
8866     return;     // element is still moving
8867   }
8868
8869   // element reached destination field
8870
8871   Tile[x][y] = EL_EMPTY;
8872   Tile[newx][newy] = element;
8873   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8874
8875   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8876   {
8877     element = Tile[newx][newy] = EL_ACID;
8878   }
8879   else if (element == EL_MOLE)
8880   {
8881     Tile[x][y] = EL_SAND;
8882
8883     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8884   }
8885   else if (element == EL_QUICKSAND_FILLING)
8886   {
8887     element = Tile[newx][newy] = get_next_element(element);
8888     Store[newx][newy] = Store[x][y];
8889   }
8890   else if (element == EL_QUICKSAND_EMPTYING)
8891   {
8892     Tile[x][y] = get_next_element(element);
8893     element = Tile[newx][newy] = Store[x][y];
8894   }
8895   else if (element == EL_QUICKSAND_FAST_FILLING)
8896   {
8897     element = Tile[newx][newy] = get_next_element(element);
8898     Store[newx][newy] = Store[x][y];
8899   }
8900   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8901   {
8902     Tile[x][y] = get_next_element(element);
8903     element = Tile[newx][newy] = Store[x][y];
8904   }
8905   else if (element == EL_MAGIC_WALL_FILLING)
8906   {
8907     element = Tile[newx][newy] = get_next_element(element);
8908     if (!game.magic_wall_active)
8909       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8910     Store[newx][newy] = Store[x][y];
8911   }
8912   else if (element == EL_MAGIC_WALL_EMPTYING)
8913   {
8914     Tile[x][y] = get_next_element(element);
8915     if (!game.magic_wall_active)
8916       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8917     element = Tile[newx][newy] = Store[x][y];
8918
8919     InitField(newx, newy, FALSE);
8920   }
8921   else if (element == EL_BD_MAGIC_WALL_FILLING)
8922   {
8923     element = Tile[newx][newy] = get_next_element(element);
8924     if (!game.magic_wall_active)
8925       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8926     Store[newx][newy] = Store[x][y];
8927   }
8928   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8929   {
8930     Tile[x][y] = get_next_element(element);
8931     if (!game.magic_wall_active)
8932       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8933     element = Tile[newx][newy] = Store[x][y];
8934
8935     InitField(newx, newy, FALSE);
8936   }
8937   else if (element == EL_DC_MAGIC_WALL_FILLING)
8938   {
8939     element = Tile[newx][newy] = get_next_element(element);
8940     if (!game.magic_wall_active)
8941       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8942     Store[newx][newy] = Store[x][y];
8943   }
8944   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8945   {
8946     Tile[x][y] = get_next_element(element);
8947     if (!game.magic_wall_active)
8948       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8949     element = Tile[newx][newy] = Store[x][y];
8950
8951     InitField(newx, newy, FALSE);
8952   }
8953   else if (element == EL_AMOEBA_DROPPING)
8954   {
8955     Tile[x][y] = get_next_element(element);
8956     element = Tile[newx][newy] = Store[x][y];
8957   }
8958   else if (element == EL_SOKOBAN_OBJECT)
8959   {
8960     if (Back[x][y])
8961       Tile[x][y] = Back[x][y];
8962
8963     if (Back[newx][newy])
8964       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8965
8966     Back[x][y] = Back[newx][newy] = 0;
8967   }
8968
8969   Store[x][y] = EL_EMPTY;
8970   MovPos[x][y] = 0;
8971   MovDir[x][y] = 0;
8972   MovDelay[x][y] = 0;
8973
8974   MovDelay[newx][newy] = 0;
8975
8976   if (CAN_CHANGE_OR_HAS_ACTION(element))
8977   {
8978     // copy element change control values to new field
8979     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8980     ChangePage[newx][newy]  = ChangePage[x][y];
8981     ChangeCount[newx][newy] = ChangeCount[x][y];
8982     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8983   }
8984
8985   CustomValue[newx][newy] = CustomValue[x][y];
8986
8987   ChangeDelay[x][y] = 0;
8988   ChangePage[x][y] = -1;
8989   ChangeCount[x][y] = 0;
8990   ChangeEvent[x][y] = -1;
8991
8992   CustomValue[x][y] = 0;
8993
8994   // copy animation control values to new field
8995   GfxFrame[newx][newy]  = GfxFrame[x][y];
8996   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8997   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8998   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8999
9000   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9001
9002   // some elements can leave other elements behind after moving
9003   if (ei->move_leave_element != EL_EMPTY &&
9004       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9005       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9006   {
9007     int move_leave_element = ei->move_leave_element;
9008
9009     // this makes it possible to leave the removed element again
9010     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9011       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9012
9013     Tile[x][y] = move_leave_element;
9014
9015     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9016       MovDir[x][y] = direction;
9017
9018     InitField(x, y, FALSE);
9019
9020     if (GFX_CRUMBLED(Tile[x][y]))
9021       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9022
9023     if (IS_PLAYER_ELEMENT(move_leave_element))
9024       RelocatePlayer(x, y, move_leave_element);
9025   }
9026
9027   // do this after checking for left-behind element
9028   ResetGfxAnimation(x, y);      // reset animation values for old field
9029
9030   if (!CAN_MOVE(element) ||
9031       (CAN_FALL(element) && direction == MV_DOWN &&
9032        (element == EL_SPRING ||
9033         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9034         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9035     GfxDir[x][y] = MovDir[newx][newy] = 0;
9036
9037   TEST_DrawLevelField(x, y);
9038   TEST_DrawLevelField(newx, newy);
9039
9040   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9041
9042   // prevent pushed element from moving on in pushed direction
9043   if (pushed_by_player && CAN_MOVE(element) &&
9044       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9045       !(element_info[element].move_pattern & direction))
9046     TurnRound(newx, newy);
9047
9048   // prevent elements on conveyor belt from moving on in last direction
9049   if (pushed_by_conveyor && CAN_FALL(element) &&
9050       direction & MV_HORIZONTAL)
9051     MovDir[newx][newy] = 0;
9052
9053   if (!pushed_by_player)
9054   {
9055     int nextx = newx + dx, nexty = newy + dy;
9056     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9057
9058     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9059
9060     if (CAN_FALL(element) && direction == MV_DOWN)
9061       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9062
9063     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9064       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9065
9066     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9067       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9068   }
9069
9070   if (DONT_TOUCH(element))      // object may be nasty to player or others
9071   {
9072     TestIfBadThingTouchesPlayer(newx, newy);
9073     TestIfBadThingTouchesFriend(newx, newy);
9074
9075     if (!IS_CUSTOM_ELEMENT(element))
9076       TestIfBadThingTouchesOtherBadThing(newx, newy);
9077   }
9078   else if (element == EL_PENGUIN)
9079     TestIfFriendTouchesBadThing(newx, newy);
9080
9081   if (DONT_GET_HIT_BY(element))
9082   {
9083     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9084   }
9085
9086   // give the player one last chance (one more frame) to move away
9087   if (CAN_FALL(element) && direction == MV_DOWN &&
9088       (last_line || (!IS_FREE(x, newy + 1) &&
9089                      (!IS_PLAYER(x, newy + 1) ||
9090                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9091     Impact(x, newy);
9092
9093   if (pushed_by_player && !game.use_change_when_pushing_bug)
9094   {
9095     int push_side = MV_DIR_OPPOSITE(direction);
9096     struct PlayerInfo *player = PLAYERINFO(x, y);
9097
9098     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9099                                player->index_bit, push_side);
9100     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9101                                         player->index_bit, push_side);
9102   }
9103
9104   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9105     MovDelay[newx][newy] = 1;
9106
9107   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9108
9109   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9110   TestIfElementHitsCustomElement(newx, newy, direction);
9111   TestIfPlayerTouchesCustomElement(newx, newy);
9112   TestIfElementTouchesCustomElement(newx, newy);
9113
9114   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9115       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9116     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9117                              MV_DIR_OPPOSITE(direction));
9118 }
9119
9120 int AmoebaNeighbourNr(int ax, int ay)
9121 {
9122   int i;
9123   int element = Tile[ax][ay];
9124   int group_nr = 0;
9125   struct XY *xy = xy_topdown;
9126
9127   for (i = 0; i < NUM_DIRECTIONS; i++)
9128   {
9129     int x = ax + xy[i].x;
9130     int y = ay + xy[i].y;
9131
9132     if (!IN_LEV_FIELD(x, y))
9133       continue;
9134
9135     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9136       group_nr = AmoebaNr[x][y];
9137   }
9138
9139   return group_nr;
9140 }
9141
9142 static void AmoebaMerge(int ax, int ay)
9143 {
9144   int i, x, y, xx, yy;
9145   int new_group_nr = AmoebaNr[ax][ay];
9146   struct XY *xy = xy_topdown;
9147
9148   if (new_group_nr == 0)
9149     return;
9150
9151   for (i = 0; i < NUM_DIRECTIONS; i++)
9152   {
9153     x = ax + xy[i].x;
9154     y = ay + xy[i].y;
9155
9156     if (!IN_LEV_FIELD(x, y))
9157       continue;
9158
9159     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9160          Tile[x][y] == EL_BD_AMOEBA ||
9161          Tile[x][y] == EL_AMOEBA_DEAD) &&
9162         AmoebaNr[x][y] != new_group_nr)
9163     {
9164       int old_group_nr = AmoebaNr[x][y];
9165
9166       if (old_group_nr == 0)
9167         return;
9168
9169       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9170       AmoebaCnt[old_group_nr] = 0;
9171       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9172       AmoebaCnt2[old_group_nr] = 0;
9173
9174       SCAN_PLAYFIELD(xx, yy)
9175       {
9176         if (AmoebaNr[xx][yy] == old_group_nr)
9177           AmoebaNr[xx][yy] = new_group_nr;
9178       }
9179     }
9180   }
9181 }
9182
9183 void AmoebaToDiamond(int ax, int ay)
9184 {
9185   int i, x, y;
9186
9187   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9188   {
9189     int group_nr = AmoebaNr[ax][ay];
9190
9191 #ifdef DEBUG
9192     if (group_nr == 0)
9193     {
9194       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9195       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9196
9197       return;
9198     }
9199 #endif
9200
9201     SCAN_PLAYFIELD(x, y)
9202     {
9203       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9204       {
9205         AmoebaNr[x][y] = 0;
9206         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9207       }
9208     }
9209
9210     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9211                             SND_AMOEBA_TURNING_TO_GEM :
9212                             SND_AMOEBA_TURNING_TO_ROCK));
9213     Bang(ax, ay);
9214   }
9215   else
9216   {
9217     struct XY *xy = xy_topdown;
9218
9219     for (i = 0; i < NUM_DIRECTIONS; i++)
9220     {
9221       x = ax + xy[i].x;
9222       y = ay + xy[i].y;
9223
9224       if (!IN_LEV_FIELD(x, y))
9225         continue;
9226
9227       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9228       {
9229         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9230                               SND_AMOEBA_TURNING_TO_GEM :
9231                               SND_AMOEBA_TURNING_TO_ROCK));
9232         Bang(x, y);
9233       }
9234     }
9235   }
9236 }
9237
9238 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9239 {
9240   int x, y;
9241   int group_nr = AmoebaNr[ax][ay];
9242   boolean done = FALSE;
9243
9244 #ifdef DEBUG
9245   if (group_nr == 0)
9246   {
9247     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9248     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9249
9250     return;
9251   }
9252 #endif
9253
9254   SCAN_PLAYFIELD(x, y)
9255   {
9256     if (AmoebaNr[x][y] == group_nr &&
9257         (Tile[x][y] == EL_AMOEBA_DEAD ||
9258          Tile[x][y] == EL_BD_AMOEBA ||
9259          Tile[x][y] == EL_AMOEBA_GROWING))
9260     {
9261       AmoebaNr[x][y] = 0;
9262       Tile[x][y] = new_element;
9263       InitField(x, y, FALSE);
9264       TEST_DrawLevelField(x, y);
9265       done = TRUE;
9266     }
9267   }
9268
9269   if (done)
9270     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9271                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9272                             SND_BD_AMOEBA_TURNING_TO_GEM));
9273 }
9274
9275 static void AmoebaGrowing(int x, int y)
9276 {
9277   static DelayCounter sound_delay = { 0 };
9278
9279   if (!MovDelay[x][y])          // start new growing cycle
9280   {
9281     MovDelay[x][y] = 7;
9282
9283     if (DelayReached(&sound_delay))
9284     {
9285       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9286       sound_delay.value = 30;
9287     }
9288   }
9289
9290   if (MovDelay[x][y])           // wait some time before growing bigger
9291   {
9292     MovDelay[x][y]--;
9293     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9294     {
9295       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9296                                            6 - MovDelay[x][y]);
9297
9298       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9299     }
9300
9301     if (!MovDelay[x][y])
9302     {
9303       Tile[x][y] = Store[x][y];
9304       Store[x][y] = 0;
9305       TEST_DrawLevelField(x, y);
9306     }
9307   }
9308 }
9309
9310 static void AmoebaShrinking(int x, int y)
9311 {
9312   static DelayCounter sound_delay = { 0 };
9313
9314   if (!MovDelay[x][y])          // start new shrinking cycle
9315   {
9316     MovDelay[x][y] = 7;
9317
9318     if (DelayReached(&sound_delay))
9319       sound_delay.value = 30;
9320   }
9321
9322   if (MovDelay[x][y])           // wait some time before shrinking
9323   {
9324     MovDelay[x][y]--;
9325     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9326     {
9327       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9328                                            6 - MovDelay[x][y]);
9329
9330       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9331     }
9332
9333     if (!MovDelay[x][y])
9334     {
9335       Tile[x][y] = EL_EMPTY;
9336       TEST_DrawLevelField(x, y);
9337
9338       // don't let mole enter this field in this cycle;
9339       // (give priority to objects falling to this field from above)
9340       Stop[x][y] = TRUE;
9341     }
9342   }
9343 }
9344
9345 static void AmoebaReproduce(int ax, int ay)
9346 {
9347   int i;
9348   int element = Tile[ax][ay];
9349   int graphic = el2img(element);
9350   int newax = ax, neway = ay;
9351   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9352   struct XY *xy = xy_topdown;
9353
9354   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9355   {
9356     Tile[ax][ay] = EL_AMOEBA_DEAD;
9357     TEST_DrawLevelField(ax, ay);
9358     return;
9359   }
9360
9361   if (IS_ANIMATED(graphic))
9362     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9363
9364   if (!MovDelay[ax][ay])        // start making new amoeba field
9365     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9366
9367   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9368   {
9369     MovDelay[ax][ay]--;
9370     if (MovDelay[ax][ay])
9371       return;
9372   }
9373
9374   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9375   {
9376     int start = RND(4);
9377     int x = ax + xy[start].x;
9378     int y = ay + xy[start].y;
9379
9380     if (!IN_LEV_FIELD(x, y))
9381       return;
9382
9383     if (IS_FREE(x, y) ||
9384         CAN_GROW_INTO(Tile[x][y]) ||
9385         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9386         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9387     {
9388       newax = x;
9389       neway = y;
9390     }
9391
9392     if (newax == ax && neway == ay)
9393       return;
9394   }
9395   else                          // normal or "filled" (BD style) amoeba
9396   {
9397     int start = RND(4);
9398     boolean waiting_for_player = FALSE;
9399
9400     for (i = 0; i < NUM_DIRECTIONS; i++)
9401     {
9402       int j = (start + i) % 4;
9403       int x = ax + xy[j].x;
9404       int y = ay + xy[j].y;
9405
9406       if (!IN_LEV_FIELD(x, y))
9407         continue;
9408
9409       if (IS_FREE(x, y) ||
9410           CAN_GROW_INTO(Tile[x][y]) ||
9411           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9412           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9413       {
9414         newax = x;
9415         neway = y;
9416         break;
9417       }
9418       else if (IS_PLAYER(x, y))
9419         waiting_for_player = TRUE;
9420     }
9421
9422     if (newax == ax && neway == ay)             // amoeba cannot grow
9423     {
9424       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9425       {
9426         Tile[ax][ay] = EL_AMOEBA_DEAD;
9427         TEST_DrawLevelField(ax, ay);
9428         AmoebaCnt[AmoebaNr[ax][ay]]--;
9429
9430         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9431         {
9432           if (element == EL_AMOEBA_FULL)
9433             AmoebaToDiamond(ax, ay);
9434           else if (element == EL_BD_AMOEBA)
9435             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9436         }
9437       }
9438       return;
9439     }
9440     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9441     {
9442       // amoeba gets larger by growing in some direction
9443
9444       int new_group_nr = AmoebaNr[ax][ay];
9445
9446 #ifdef DEBUG
9447   if (new_group_nr == 0)
9448   {
9449     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9450           newax, neway);
9451     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9452
9453     return;
9454   }
9455 #endif
9456
9457       AmoebaNr[newax][neway] = new_group_nr;
9458       AmoebaCnt[new_group_nr]++;
9459       AmoebaCnt2[new_group_nr]++;
9460
9461       // if amoeba touches other amoeba(s) after growing, unify them
9462       AmoebaMerge(newax, neway);
9463
9464       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9465       {
9466         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9467         return;
9468       }
9469     }
9470   }
9471
9472   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9473       (neway == lev_fieldy - 1 && newax != ax))
9474   {
9475     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9476     Store[newax][neway] = element;
9477   }
9478   else if (neway == ay || element == EL_EMC_DRIPPER)
9479   {
9480     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9481
9482     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9483   }
9484   else
9485   {
9486     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9487     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9488     Store[ax][ay] = EL_AMOEBA_DROP;
9489     ContinueMoving(ax, ay);
9490     return;
9491   }
9492
9493   TEST_DrawLevelField(newax, neway);
9494 }
9495
9496 static void Life(int ax, int ay)
9497 {
9498   int x1, y1, x2, y2;
9499   int life_time = 40;
9500   int element = Tile[ax][ay];
9501   int graphic = el2img(element);
9502   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9503                          level.biomaze);
9504   boolean changed = FALSE;
9505
9506   if (IS_ANIMATED(graphic))
9507     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9508
9509   if (Stop[ax][ay])
9510     return;
9511
9512   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9513     MovDelay[ax][ay] = life_time;
9514
9515   if (MovDelay[ax][ay])         // wait some time before next cycle
9516   {
9517     MovDelay[ax][ay]--;
9518     if (MovDelay[ax][ay])
9519       return;
9520   }
9521
9522   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9523   {
9524     int xx = ax + x1, yy = ay + y1;
9525     int old_element = Tile[xx][yy];
9526     int num_neighbours = 0;
9527
9528     if (!IN_LEV_FIELD(xx, yy))
9529       continue;
9530
9531     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9532     {
9533       int x = xx + x2, y = yy + y2;
9534
9535       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9536         continue;
9537
9538       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9539       boolean is_neighbour = FALSE;
9540
9541       if (level.use_life_bugs)
9542         is_neighbour =
9543           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9544            (IS_FREE(x, y)                             &&  Stop[x][y]));
9545       else
9546         is_neighbour =
9547           (Last[x][y] == element || is_player_cell);
9548
9549       if (is_neighbour)
9550         num_neighbours++;
9551     }
9552
9553     boolean is_free = FALSE;
9554
9555     if (level.use_life_bugs)
9556       is_free = (IS_FREE(xx, yy));
9557     else
9558       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9559
9560     if (xx == ax && yy == ay)           // field in the middle
9561     {
9562       if (num_neighbours < life_parameter[0] ||
9563           num_neighbours > life_parameter[1])
9564       {
9565         Tile[xx][yy] = EL_EMPTY;
9566         if (Tile[xx][yy] != old_element)
9567           TEST_DrawLevelField(xx, yy);
9568         Stop[xx][yy] = TRUE;
9569         changed = TRUE;
9570       }
9571     }
9572     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9573     {                                   // free border field
9574       if (num_neighbours >= life_parameter[2] &&
9575           num_neighbours <= life_parameter[3])
9576       {
9577         Tile[xx][yy] = element;
9578         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9579         if (Tile[xx][yy] != old_element)
9580           TEST_DrawLevelField(xx, yy);
9581         Stop[xx][yy] = TRUE;
9582         changed = TRUE;
9583       }
9584     }
9585   }
9586
9587   if (changed)
9588     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9589                    SND_GAME_OF_LIFE_GROWING);
9590 }
9591
9592 static void InitRobotWheel(int x, int y)
9593 {
9594   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9595 }
9596
9597 static void RunRobotWheel(int x, int y)
9598 {
9599   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9600 }
9601
9602 static void StopRobotWheel(int x, int y)
9603 {
9604   if (game.robot_wheel_x == x &&
9605       game.robot_wheel_y == y)
9606   {
9607     game.robot_wheel_x = -1;
9608     game.robot_wheel_y = -1;
9609     game.robot_wheel_active = FALSE;
9610   }
9611 }
9612
9613 static void InitTimegateWheel(int x, int y)
9614 {
9615   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9616 }
9617
9618 static void RunTimegateWheel(int x, int y)
9619 {
9620   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9621 }
9622
9623 static void InitMagicBallDelay(int x, int y)
9624 {
9625   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9626 }
9627
9628 static void ActivateMagicBall(int bx, int by)
9629 {
9630   int x, y;
9631
9632   if (level.ball_random)
9633   {
9634     int pos_border = RND(8);    // select one of the eight border elements
9635     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9636     int xx = pos_content % 3;
9637     int yy = pos_content / 3;
9638
9639     x = bx - 1 + xx;
9640     y = by - 1 + yy;
9641
9642     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9643       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9644   }
9645   else
9646   {
9647     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9648     {
9649       int xx = x - bx + 1;
9650       int yy = y - by + 1;
9651
9652       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9653         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9654     }
9655   }
9656
9657   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9658 }
9659
9660 static void CheckExit(int x, int y)
9661 {
9662   if (game.gems_still_needed > 0 ||
9663       game.sokoban_fields_still_needed > 0 ||
9664       game.sokoban_objects_still_needed > 0 ||
9665       game.lights_still_needed > 0)
9666   {
9667     int element = Tile[x][y];
9668     int graphic = el2img(element);
9669
9670     if (IS_ANIMATED(graphic))
9671       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9672
9673     return;
9674   }
9675
9676   // do not re-open exit door closed after last player
9677   if (game.all_players_gone)
9678     return;
9679
9680   Tile[x][y] = EL_EXIT_OPENING;
9681
9682   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9683 }
9684
9685 static void CheckExitEM(int x, int y)
9686 {
9687   if (game.gems_still_needed > 0 ||
9688       game.sokoban_fields_still_needed > 0 ||
9689       game.sokoban_objects_still_needed > 0 ||
9690       game.lights_still_needed > 0)
9691   {
9692     int element = Tile[x][y];
9693     int graphic = el2img(element);
9694
9695     if (IS_ANIMATED(graphic))
9696       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9697
9698     return;
9699   }
9700
9701   // do not re-open exit door closed after last player
9702   if (game.all_players_gone)
9703     return;
9704
9705   Tile[x][y] = EL_EM_EXIT_OPENING;
9706
9707   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9708 }
9709
9710 static void CheckExitSteel(int x, int y)
9711 {
9712   if (game.gems_still_needed > 0 ||
9713       game.sokoban_fields_still_needed > 0 ||
9714       game.sokoban_objects_still_needed > 0 ||
9715       game.lights_still_needed > 0)
9716   {
9717     int element = Tile[x][y];
9718     int graphic = el2img(element);
9719
9720     if (IS_ANIMATED(graphic))
9721       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9722
9723     return;
9724   }
9725
9726   // do not re-open exit door closed after last player
9727   if (game.all_players_gone)
9728     return;
9729
9730   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9731
9732   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9733 }
9734
9735 static void CheckExitSteelEM(int x, int y)
9736 {
9737   if (game.gems_still_needed > 0 ||
9738       game.sokoban_fields_still_needed > 0 ||
9739       game.sokoban_objects_still_needed > 0 ||
9740       game.lights_still_needed > 0)
9741   {
9742     int element = Tile[x][y];
9743     int graphic = el2img(element);
9744
9745     if (IS_ANIMATED(graphic))
9746       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9747
9748     return;
9749   }
9750
9751   // do not re-open exit door closed after last player
9752   if (game.all_players_gone)
9753     return;
9754
9755   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9756
9757   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9758 }
9759
9760 static void CheckExitSP(int x, int y)
9761 {
9762   if (game.gems_still_needed > 0)
9763   {
9764     int element = Tile[x][y];
9765     int graphic = el2img(element);
9766
9767     if (IS_ANIMATED(graphic))
9768       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9769
9770     return;
9771   }
9772
9773   // do not re-open exit door closed after last player
9774   if (game.all_players_gone)
9775     return;
9776
9777   Tile[x][y] = EL_SP_EXIT_OPENING;
9778
9779   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9780 }
9781
9782 static void CloseAllOpenTimegates(void)
9783 {
9784   int x, y;
9785
9786   SCAN_PLAYFIELD(x, y)
9787   {
9788     int element = Tile[x][y];
9789
9790     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9791     {
9792       Tile[x][y] = EL_TIMEGATE_CLOSING;
9793
9794       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9795     }
9796   }
9797 }
9798
9799 static void DrawTwinkleOnField(int x, int y)
9800 {
9801   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9802     return;
9803
9804   if (Tile[x][y] == EL_BD_DIAMOND)
9805     return;
9806
9807   if (MovDelay[x][y] == 0)      // next animation frame
9808     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9809
9810   if (MovDelay[x][y] != 0)      // wait some time before next frame
9811   {
9812     MovDelay[x][y]--;
9813
9814     DrawLevelElementAnimation(x, y, Tile[x][y]);
9815
9816     if (MovDelay[x][y] != 0)
9817     {
9818       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9819                                            10 - MovDelay[x][y]);
9820
9821       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9822     }
9823   }
9824 }
9825
9826 static void WallGrowing(int x, int y)
9827 {
9828   int delay = 6;
9829
9830   if (!MovDelay[x][y])          // next animation frame
9831     MovDelay[x][y] = 3 * delay;
9832
9833   if (MovDelay[x][y])           // wait some time before next frame
9834   {
9835     MovDelay[x][y]--;
9836
9837     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9838     {
9839       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9840       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9841
9842       DrawLevelGraphic(x, y, graphic, frame);
9843     }
9844
9845     if (!MovDelay[x][y])
9846     {
9847       if (MovDir[x][y] == MV_LEFT)
9848       {
9849         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9850           TEST_DrawLevelField(x - 1, y);
9851       }
9852       else if (MovDir[x][y] == MV_RIGHT)
9853       {
9854         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9855           TEST_DrawLevelField(x + 1, y);
9856       }
9857       else if (MovDir[x][y] == MV_UP)
9858       {
9859         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9860           TEST_DrawLevelField(x, y - 1);
9861       }
9862       else
9863       {
9864         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9865           TEST_DrawLevelField(x, y + 1);
9866       }
9867
9868       Tile[x][y] = Store[x][y];
9869       Store[x][y] = 0;
9870       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9871       TEST_DrawLevelField(x, y);
9872     }
9873   }
9874 }
9875
9876 static void CheckWallGrowing(int ax, int ay)
9877 {
9878   int element = Tile[ax][ay];
9879   int graphic = el2img(element);
9880   boolean free_top    = FALSE;
9881   boolean free_bottom = FALSE;
9882   boolean free_left   = FALSE;
9883   boolean free_right  = FALSE;
9884   boolean stop_top    = FALSE;
9885   boolean stop_bottom = FALSE;
9886   boolean stop_left   = FALSE;
9887   boolean stop_right  = FALSE;
9888   boolean new_wall    = FALSE;
9889
9890   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9891                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9892                            element == EL_EXPANDABLE_STEELWALL_ANY);
9893
9894   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9895                              element == EL_EXPANDABLE_WALL_ANY ||
9896                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9897                              element == EL_EXPANDABLE_STEELWALL_ANY);
9898
9899   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9900                              element == EL_EXPANDABLE_WALL_ANY ||
9901                              element == EL_EXPANDABLE_WALL ||
9902                              element == EL_BD_EXPANDABLE_WALL ||
9903                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9904                              element == EL_EXPANDABLE_STEELWALL_ANY);
9905
9906   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9907                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9908
9909   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9910                              element == EL_EXPANDABLE_WALL ||
9911                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9912
9913   int wall_growing = (is_steelwall ?
9914                       EL_EXPANDABLE_STEELWALL_GROWING :
9915                       EL_EXPANDABLE_WALL_GROWING);
9916
9917   int gfx_wall_growing_up    = (is_steelwall ?
9918                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9919                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9920   int gfx_wall_growing_down  = (is_steelwall ?
9921                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9922                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9923   int gfx_wall_growing_left  = (is_steelwall ?
9924                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9925                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9926   int gfx_wall_growing_right = (is_steelwall ?
9927                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9928                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9929
9930   if (IS_ANIMATED(graphic))
9931     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9932
9933   if (!MovDelay[ax][ay])        // start building new wall
9934     MovDelay[ax][ay] = 6;
9935
9936   if (MovDelay[ax][ay])         // wait some time before building new wall
9937   {
9938     MovDelay[ax][ay]--;
9939     if (MovDelay[ax][ay])
9940       return;
9941   }
9942
9943   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9944     free_top = TRUE;
9945   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9946     free_bottom = TRUE;
9947   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9948     free_left = TRUE;
9949   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9950     free_right = TRUE;
9951
9952   if (grow_vertical)
9953   {
9954     if (free_top)
9955     {
9956       Tile[ax][ay - 1] = wall_growing;
9957       Store[ax][ay - 1] = element;
9958       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9959
9960       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9961         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9962
9963       new_wall = TRUE;
9964     }
9965
9966     if (free_bottom)
9967     {
9968       Tile[ax][ay + 1] = wall_growing;
9969       Store[ax][ay + 1] = element;
9970       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9971
9972       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9973         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9974
9975       new_wall = TRUE;
9976     }
9977   }
9978
9979   if (grow_horizontal)
9980   {
9981     if (free_left)
9982     {
9983       Tile[ax - 1][ay] = wall_growing;
9984       Store[ax - 1][ay] = element;
9985       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9986
9987       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9988         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9989
9990       new_wall = TRUE;
9991     }
9992
9993     if (free_right)
9994     {
9995       Tile[ax + 1][ay] = wall_growing;
9996       Store[ax + 1][ay] = element;
9997       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9998
9999       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10000         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10001
10002       new_wall = TRUE;
10003     }
10004   }
10005
10006   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10007     TEST_DrawLevelField(ax, ay);
10008
10009   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10010     stop_top = TRUE;
10011   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10012     stop_bottom = TRUE;
10013   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10014     stop_left = TRUE;
10015   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10016     stop_right = TRUE;
10017
10018   if (((stop_top && stop_bottom) || stop_horizontal) &&
10019       ((stop_left && stop_right) || stop_vertical))
10020     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10021
10022   if (new_wall)
10023     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10024 }
10025
10026 static void CheckForDragon(int x, int y)
10027 {
10028   int i, j;
10029   boolean dragon_found = FALSE;
10030   struct XY *xy = xy_topdown;
10031
10032   for (i = 0; i < NUM_DIRECTIONS; i++)
10033   {
10034     for (j = 0; j < 4; j++)
10035     {
10036       int xx = x + j * xy[i].x;
10037       int yy = y + j * xy[i].y;
10038
10039       if (IN_LEV_FIELD(xx, yy) &&
10040           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10041       {
10042         if (Tile[xx][yy] == EL_DRAGON)
10043           dragon_found = TRUE;
10044       }
10045       else
10046         break;
10047     }
10048   }
10049
10050   if (!dragon_found)
10051   {
10052     for (i = 0; i < NUM_DIRECTIONS; i++)
10053     {
10054       for (j = 0; j < 3; j++)
10055       {
10056         int xx = x + j * xy[i].x;
10057         int yy = y + j * xy[i].y;
10058
10059         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10060         {
10061           Tile[xx][yy] = EL_EMPTY;
10062           TEST_DrawLevelField(xx, yy);
10063         }
10064         else
10065           break;
10066       }
10067     }
10068   }
10069 }
10070
10071 static void InitBuggyBase(int x, int y)
10072 {
10073   int element = Tile[x][y];
10074   int activating_delay = FRAMES_PER_SECOND / 4;
10075
10076   ChangeDelay[x][y] =
10077     (element == EL_SP_BUGGY_BASE ?
10078      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10079      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10080      activating_delay :
10081      element == EL_SP_BUGGY_BASE_ACTIVE ?
10082      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10083 }
10084
10085 static void WarnBuggyBase(int x, int y)
10086 {
10087   int i;
10088   struct XY *xy = xy_topdown;
10089
10090   for (i = 0; i < NUM_DIRECTIONS; i++)
10091   {
10092     int xx = x + xy[i].x;
10093     int yy = y + xy[i].y;
10094
10095     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10096     {
10097       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10098
10099       break;
10100     }
10101   }
10102 }
10103
10104 static void InitTrap(int x, int y)
10105 {
10106   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10107 }
10108
10109 static void ActivateTrap(int x, int y)
10110 {
10111   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10112 }
10113
10114 static void ChangeActiveTrap(int x, int y)
10115 {
10116   int graphic = IMG_TRAP_ACTIVE;
10117
10118   // if new animation frame was drawn, correct crumbled sand border
10119   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10120     TEST_DrawLevelFieldCrumbled(x, y);
10121 }
10122
10123 static int getSpecialActionElement(int element, int number, int base_element)
10124 {
10125   return (element != EL_EMPTY ? element :
10126           number != -1 ? base_element + number - 1 :
10127           EL_EMPTY);
10128 }
10129
10130 static int getModifiedActionNumber(int value_old, int operator, int operand,
10131                                    int value_min, int value_max)
10132 {
10133   int value_new = (operator == CA_MODE_SET      ? operand :
10134                    operator == CA_MODE_ADD      ? value_old + operand :
10135                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10136                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10137                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10138                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10139                    value_old);
10140
10141   return (value_new < value_min ? value_min :
10142           value_new > value_max ? value_max :
10143           value_new);
10144 }
10145
10146 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10147 {
10148   struct ElementInfo *ei = &element_info[element];
10149   struct ElementChangeInfo *change = &ei->change_page[page];
10150   int target_element = change->target_element;
10151   int action_type = change->action_type;
10152   int action_mode = change->action_mode;
10153   int action_arg = change->action_arg;
10154   int action_element = change->action_element;
10155   int i;
10156
10157   if (!change->has_action)
10158     return;
10159
10160   // ---------- determine action paramater values -----------------------------
10161
10162   int level_time_value =
10163     (level.time > 0 ? TimeLeft :
10164      TimePlayed);
10165
10166   int action_arg_element_raw =
10167     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10168      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10169      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10170      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10171      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10172      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10173      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10174      EL_EMPTY);
10175   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10176
10177   int action_arg_direction =
10178     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10179      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10180      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10181      change->actual_trigger_side :
10182      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10183      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10184      MV_NONE);
10185
10186   int action_arg_number_min =
10187     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10188      CA_ARG_MIN);
10189
10190   int action_arg_number_max =
10191     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10192      action_type == CA_SET_LEVEL_GEMS ? 999 :
10193      action_type == CA_SET_LEVEL_TIME ? 9999 :
10194      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10195      action_type == CA_SET_CE_VALUE ? 9999 :
10196      action_type == CA_SET_CE_SCORE ? 9999 :
10197      CA_ARG_MAX);
10198
10199   int action_arg_number_reset =
10200     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10201      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10202      action_type == CA_SET_LEVEL_TIME ? level.time :
10203      action_type == CA_SET_LEVEL_SCORE ? 0 :
10204      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10205      action_type == CA_SET_CE_SCORE ? 0 :
10206      0);
10207
10208   int action_arg_number =
10209     (action_arg <= CA_ARG_MAX ? action_arg :
10210      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10211      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10212      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10213      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10214      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10215      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10216      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10217      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10218      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10219      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10220      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10221      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10222      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10223      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10224      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10225      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10226      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10227      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10228      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10229      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10230      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10231      -1);
10232
10233   int action_arg_number_old =
10234     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10235      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10236      action_type == CA_SET_LEVEL_SCORE ? game.score :
10237      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10238      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10239      0);
10240
10241   int action_arg_number_new =
10242     getModifiedActionNumber(action_arg_number_old,
10243                             action_mode, action_arg_number,
10244                             action_arg_number_min, action_arg_number_max);
10245
10246   int trigger_player_bits =
10247     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10248      change->actual_trigger_player_bits : change->trigger_player);
10249
10250   int action_arg_player_bits =
10251     (action_arg >= CA_ARG_PLAYER_1 &&
10252      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10253      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10254      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10255      PLAYER_BITS_ANY);
10256
10257   // ---------- execute action  -----------------------------------------------
10258
10259   switch (action_type)
10260   {
10261     case CA_NO_ACTION:
10262     {
10263       return;
10264     }
10265
10266     // ---------- level actions  ----------------------------------------------
10267
10268     case CA_RESTART_LEVEL:
10269     {
10270       game.restart_level = TRUE;
10271
10272       break;
10273     }
10274
10275     case CA_SHOW_ENVELOPE:
10276     {
10277       int element = getSpecialActionElement(action_arg_element,
10278                                             action_arg_number, EL_ENVELOPE_1);
10279
10280       if (IS_ENVELOPE(element))
10281         local_player->show_envelope = element;
10282
10283       break;
10284     }
10285
10286     case CA_SET_LEVEL_TIME:
10287     {
10288       if (level.time > 0)       // only modify limited time value
10289       {
10290         TimeLeft = action_arg_number_new;
10291
10292         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10293
10294         DisplayGameControlValues();
10295
10296         if (!TimeLeft && game.time_limit)
10297           for (i = 0; i < MAX_PLAYERS; i++)
10298             KillPlayer(&stored_player[i]);
10299       }
10300
10301       break;
10302     }
10303
10304     case CA_SET_LEVEL_SCORE:
10305     {
10306       game.score = action_arg_number_new;
10307
10308       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10309
10310       DisplayGameControlValues();
10311
10312       break;
10313     }
10314
10315     case CA_SET_LEVEL_GEMS:
10316     {
10317       game.gems_still_needed = action_arg_number_new;
10318
10319       game.snapshot.collected_item = TRUE;
10320
10321       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10322
10323       DisplayGameControlValues();
10324
10325       break;
10326     }
10327
10328     case CA_SET_LEVEL_WIND:
10329     {
10330       game.wind_direction = action_arg_direction;
10331
10332       break;
10333     }
10334
10335     case CA_SET_LEVEL_RANDOM_SEED:
10336     {
10337       // ensure that setting a new random seed while playing is predictable
10338       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10339
10340       break;
10341     }
10342
10343     // ---------- player actions  ---------------------------------------------
10344
10345     case CA_MOVE_PLAYER:
10346     case CA_MOVE_PLAYER_NEW:
10347     {
10348       // automatically move to the next field in specified direction
10349       for (i = 0; i < MAX_PLAYERS; i++)
10350         if (trigger_player_bits & (1 << i))
10351           if (action_type == CA_MOVE_PLAYER ||
10352               stored_player[i].MovPos == 0)
10353             stored_player[i].programmed_action = action_arg_direction;
10354
10355       break;
10356     }
10357
10358     case CA_EXIT_PLAYER:
10359     {
10360       for (i = 0; i < MAX_PLAYERS; i++)
10361         if (action_arg_player_bits & (1 << i))
10362           ExitPlayer(&stored_player[i]);
10363
10364       if (game.players_still_needed == 0)
10365         LevelSolved();
10366
10367       break;
10368     }
10369
10370     case CA_KILL_PLAYER:
10371     {
10372       for (i = 0; i < MAX_PLAYERS; i++)
10373         if (action_arg_player_bits & (1 << i))
10374           KillPlayer(&stored_player[i]);
10375
10376       break;
10377     }
10378
10379     case CA_SET_PLAYER_KEYS:
10380     {
10381       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10382       int element = getSpecialActionElement(action_arg_element,
10383                                             action_arg_number, EL_KEY_1);
10384
10385       if (IS_KEY(element))
10386       {
10387         for (i = 0; i < MAX_PLAYERS; i++)
10388         {
10389           if (trigger_player_bits & (1 << i))
10390           {
10391             stored_player[i].key[KEY_NR(element)] = key_state;
10392
10393             DrawGameDoorValues();
10394           }
10395         }
10396       }
10397
10398       break;
10399     }
10400
10401     case CA_SET_PLAYER_SPEED:
10402     {
10403       for (i = 0; i < MAX_PLAYERS; i++)
10404       {
10405         if (trigger_player_bits & (1 << i))
10406         {
10407           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10408
10409           if (action_arg == CA_ARG_SPEED_FASTER &&
10410               stored_player[i].cannot_move)
10411           {
10412             action_arg_number = STEPSIZE_VERY_SLOW;
10413           }
10414           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10415                    action_arg == CA_ARG_SPEED_FASTER)
10416           {
10417             action_arg_number = 2;
10418             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10419                            CA_MODE_MULTIPLY);
10420           }
10421           else if (action_arg == CA_ARG_NUMBER_RESET)
10422           {
10423             action_arg_number = level.initial_player_stepsize[i];
10424           }
10425
10426           move_stepsize =
10427             getModifiedActionNumber(move_stepsize,
10428                                     action_mode,
10429                                     action_arg_number,
10430                                     action_arg_number_min,
10431                                     action_arg_number_max);
10432
10433           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10434         }
10435       }
10436
10437       break;
10438     }
10439
10440     case CA_SET_PLAYER_SHIELD:
10441     {
10442       for (i = 0; i < MAX_PLAYERS; i++)
10443       {
10444         if (trigger_player_bits & (1 << i))
10445         {
10446           if (action_arg == CA_ARG_SHIELD_OFF)
10447           {
10448             stored_player[i].shield_normal_time_left = 0;
10449             stored_player[i].shield_deadly_time_left = 0;
10450           }
10451           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10452           {
10453             stored_player[i].shield_normal_time_left = 999999;
10454           }
10455           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10456           {
10457             stored_player[i].shield_normal_time_left = 999999;
10458             stored_player[i].shield_deadly_time_left = 999999;
10459           }
10460         }
10461       }
10462
10463       break;
10464     }
10465
10466     case CA_SET_PLAYER_GRAVITY:
10467     {
10468       for (i = 0; i < MAX_PLAYERS; i++)
10469       {
10470         if (trigger_player_bits & (1 << i))
10471         {
10472           stored_player[i].gravity =
10473             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10474              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10475              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10476              stored_player[i].gravity);
10477         }
10478       }
10479
10480       break;
10481     }
10482
10483     case CA_SET_PLAYER_ARTWORK:
10484     {
10485       for (i = 0; i < MAX_PLAYERS; i++)
10486       {
10487         if (trigger_player_bits & (1 << i))
10488         {
10489           int artwork_element = action_arg_element;
10490
10491           if (action_arg == CA_ARG_ELEMENT_RESET)
10492             artwork_element =
10493               (level.use_artwork_element[i] ? level.artwork_element[i] :
10494                stored_player[i].element_nr);
10495
10496           if (stored_player[i].artwork_element != artwork_element)
10497             stored_player[i].Frame = 0;
10498
10499           stored_player[i].artwork_element = artwork_element;
10500
10501           SetPlayerWaiting(&stored_player[i], FALSE);
10502
10503           // set number of special actions for bored and sleeping animation
10504           stored_player[i].num_special_action_bored =
10505             get_num_special_action(artwork_element,
10506                                    ACTION_BORING_1, ACTION_BORING_LAST);
10507           stored_player[i].num_special_action_sleeping =
10508             get_num_special_action(artwork_element,
10509                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10510         }
10511       }
10512
10513       break;
10514     }
10515
10516     case CA_SET_PLAYER_INVENTORY:
10517     {
10518       for (i = 0; i < MAX_PLAYERS; i++)
10519       {
10520         struct PlayerInfo *player = &stored_player[i];
10521         int j, k;
10522
10523         if (trigger_player_bits & (1 << i))
10524         {
10525           int inventory_element = action_arg_element;
10526
10527           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10528               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10529               action_arg == CA_ARG_ELEMENT_ACTION)
10530           {
10531             int element = inventory_element;
10532             int collect_count = element_info[element].collect_count_initial;
10533
10534             if (!IS_CUSTOM_ELEMENT(element))
10535               collect_count = 1;
10536
10537             if (collect_count == 0)
10538               player->inventory_infinite_element = element;
10539             else
10540               for (k = 0; k < collect_count; k++)
10541                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10542                   player->inventory_element[player->inventory_size++] =
10543                     element;
10544           }
10545           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10546                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10547                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10548           {
10549             if (player->inventory_infinite_element != EL_UNDEFINED &&
10550                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10551                                      action_arg_element_raw))
10552               player->inventory_infinite_element = EL_UNDEFINED;
10553
10554             for (k = 0, j = 0; j < player->inventory_size; j++)
10555             {
10556               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10557                                         action_arg_element_raw))
10558                 player->inventory_element[k++] = player->inventory_element[j];
10559             }
10560
10561             player->inventory_size = k;
10562           }
10563           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10564           {
10565             if (player->inventory_size > 0)
10566             {
10567               for (j = 0; j < player->inventory_size - 1; j++)
10568                 player->inventory_element[j] = player->inventory_element[j + 1];
10569
10570               player->inventory_size--;
10571             }
10572           }
10573           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10574           {
10575             if (player->inventory_size > 0)
10576               player->inventory_size--;
10577           }
10578           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10579           {
10580             player->inventory_infinite_element = EL_UNDEFINED;
10581             player->inventory_size = 0;
10582           }
10583           else if (action_arg == CA_ARG_INVENTORY_RESET)
10584           {
10585             player->inventory_infinite_element = EL_UNDEFINED;
10586             player->inventory_size = 0;
10587
10588             if (level.use_initial_inventory[i])
10589             {
10590               for (j = 0; j < level.initial_inventory_size[i]; j++)
10591               {
10592                 int element = level.initial_inventory_content[i][j];
10593                 int collect_count = element_info[element].collect_count_initial;
10594
10595                 if (!IS_CUSTOM_ELEMENT(element))
10596                   collect_count = 1;
10597
10598                 if (collect_count == 0)
10599                   player->inventory_infinite_element = element;
10600                 else
10601                   for (k = 0; k < collect_count; k++)
10602                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10603                       player->inventory_element[player->inventory_size++] =
10604                         element;
10605               }
10606             }
10607           }
10608         }
10609       }
10610
10611       break;
10612     }
10613
10614     // ---------- CE actions  -------------------------------------------------
10615
10616     case CA_SET_CE_VALUE:
10617     {
10618       int last_ce_value = CustomValue[x][y];
10619
10620       CustomValue[x][y] = action_arg_number_new;
10621
10622       if (CustomValue[x][y] != last_ce_value)
10623       {
10624         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10625         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10626
10627         if (CustomValue[x][y] == 0)
10628         {
10629           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10630           ChangeCount[x][y] = 0;        // allow at least one more change
10631
10632           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10633           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10634         }
10635       }
10636
10637       break;
10638     }
10639
10640     case CA_SET_CE_SCORE:
10641     {
10642       int last_ce_score = ei->collect_score;
10643
10644       ei->collect_score = action_arg_number_new;
10645
10646       if (ei->collect_score != last_ce_score)
10647       {
10648         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10649         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10650
10651         if (ei->collect_score == 0)
10652         {
10653           int xx, yy;
10654
10655           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10656           ChangeCount[x][y] = 0;        // allow at least one more change
10657
10658           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10659           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10660
10661           /*
10662             This is a very special case that seems to be a mixture between
10663             CheckElementChange() and CheckTriggeredElementChange(): while
10664             the first one only affects single elements that are triggered
10665             directly, the second one affects multiple elements in the playfield
10666             that are triggered indirectly by another element. This is a third
10667             case: Changing the CE score always affects multiple identical CEs,
10668             so every affected CE must be checked, not only the single CE for
10669             which the CE score was changed in the first place (as every instance
10670             of that CE shares the same CE score, and therefore also can change)!
10671           */
10672           SCAN_PLAYFIELD(xx, yy)
10673           {
10674             if (Tile[xx][yy] == element)
10675               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10676                                  CE_SCORE_GETS_ZERO);
10677           }
10678         }
10679       }
10680
10681       break;
10682     }
10683
10684     case CA_SET_CE_ARTWORK:
10685     {
10686       int artwork_element = action_arg_element;
10687       boolean reset_frame = FALSE;
10688       int xx, yy;
10689
10690       if (action_arg == CA_ARG_ELEMENT_RESET)
10691         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10692                            element);
10693
10694       if (ei->gfx_element != artwork_element)
10695         reset_frame = TRUE;
10696
10697       ei->gfx_element = artwork_element;
10698
10699       SCAN_PLAYFIELD(xx, yy)
10700       {
10701         if (Tile[xx][yy] == element)
10702         {
10703           if (reset_frame)
10704           {
10705             ResetGfxAnimation(xx, yy);
10706             ResetRandomAnimationValue(xx, yy);
10707           }
10708
10709           TEST_DrawLevelField(xx, yy);
10710         }
10711       }
10712
10713       break;
10714     }
10715
10716     // ---------- engine actions  ---------------------------------------------
10717
10718     case CA_SET_ENGINE_SCAN_MODE:
10719     {
10720       InitPlayfieldScanMode(action_arg);
10721
10722       break;
10723     }
10724
10725     default:
10726       break;
10727   }
10728 }
10729
10730 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10731 {
10732   int old_element = Tile[x][y];
10733   int new_element = GetElementFromGroupElement(element);
10734   int previous_move_direction = MovDir[x][y];
10735   int last_ce_value = CustomValue[x][y];
10736   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10737   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10738   boolean add_player_onto_element = (new_element_is_player &&
10739                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10740                                      IS_WALKABLE(old_element));
10741
10742   if (!add_player_onto_element)
10743   {
10744     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10745       RemoveMovingField(x, y);
10746     else
10747       RemoveField(x, y);
10748
10749     Tile[x][y] = new_element;
10750
10751     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10752       MovDir[x][y] = previous_move_direction;
10753
10754     if (element_info[new_element].use_last_ce_value)
10755       CustomValue[x][y] = last_ce_value;
10756
10757     InitField_WithBug1(x, y, FALSE);
10758
10759     new_element = Tile[x][y];   // element may have changed
10760
10761     ResetGfxAnimation(x, y);
10762     ResetRandomAnimationValue(x, y);
10763
10764     TEST_DrawLevelField(x, y);
10765
10766     if (GFX_CRUMBLED(new_element))
10767       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10768
10769     if (old_element == EL_EXPLOSION)
10770     {
10771       Store[x][y] = Store2[x][y] = 0;
10772
10773       // check if new element replaces an exploding player, requiring cleanup
10774       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10775         StorePlayer[x][y] = 0;
10776     }
10777
10778     // check if element under the player changes from accessible to unaccessible
10779     // (needed for special case of dropping element which then changes)
10780     // (must be checked after creating new element for walkable group elements)
10781     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10782         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10783     {
10784       KillPlayer(PLAYERINFO(x, y));
10785
10786       return;
10787     }
10788   }
10789
10790   // "ChangeCount" not set yet to allow "entered by player" change one time
10791   if (new_element_is_player)
10792     RelocatePlayer(x, y, new_element);
10793
10794   if (is_change)
10795     ChangeCount[x][y]++;        // count number of changes in the same frame
10796
10797   TestIfBadThingTouchesPlayer(x, y);
10798   TestIfPlayerTouchesCustomElement(x, y);
10799   TestIfElementTouchesCustomElement(x, y);
10800 }
10801
10802 static void CreateField(int x, int y, int element)
10803 {
10804   CreateFieldExt(x, y, element, FALSE);
10805 }
10806
10807 static void CreateElementFromChange(int x, int y, int element)
10808 {
10809   element = GET_VALID_RUNTIME_ELEMENT(element);
10810
10811   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10812   {
10813     int old_element = Tile[x][y];
10814
10815     // prevent changed element from moving in same engine frame
10816     // unless both old and new element can either fall or move
10817     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10818         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10819       Stop[x][y] = TRUE;
10820   }
10821
10822   CreateFieldExt(x, y, element, TRUE);
10823 }
10824
10825 static boolean ChangeElement(int x, int y, int element, int page)
10826 {
10827   struct ElementInfo *ei = &element_info[element];
10828   struct ElementChangeInfo *change = &ei->change_page[page];
10829   int ce_value = CustomValue[x][y];
10830   int ce_score = ei->collect_score;
10831   int target_element;
10832   int old_element = Tile[x][y];
10833
10834   // always use default change event to prevent running into a loop
10835   if (ChangeEvent[x][y] == -1)
10836     ChangeEvent[x][y] = CE_DELAY;
10837
10838   if (ChangeEvent[x][y] == CE_DELAY)
10839   {
10840     // reset actual trigger element, trigger player and action element
10841     change->actual_trigger_element = EL_EMPTY;
10842     change->actual_trigger_player = EL_EMPTY;
10843     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10844     change->actual_trigger_side = CH_SIDE_NONE;
10845     change->actual_trigger_ce_value = 0;
10846     change->actual_trigger_ce_score = 0;
10847     change->actual_trigger_x = -1;
10848     change->actual_trigger_y = -1;
10849   }
10850
10851   // do not change elements more than a specified maximum number of changes
10852   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10853     return FALSE;
10854
10855   ChangeCount[x][y]++;          // count number of changes in the same frame
10856
10857   if (ei->has_anim_event)
10858     HandleGlobalAnimEventByElementChange(element, page, x, y,
10859                                          change->actual_trigger_x,
10860                                          change->actual_trigger_y);
10861
10862   if (change->explode)
10863   {
10864     Bang(x, y);
10865
10866     return TRUE;
10867   }
10868
10869   if (change->use_target_content)
10870   {
10871     boolean complete_replace = TRUE;
10872     boolean can_replace[3][3];
10873     int xx, yy;
10874
10875     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10876     {
10877       boolean is_empty;
10878       boolean is_walkable;
10879       boolean is_diggable;
10880       boolean is_collectible;
10881       boolean is_removable;
10882       boolean is_destructible;
10883       int ex = x + xx - 1;
10884       int ey = y + yy - 1;
10885       int content_element = change->target_content.e[xx][yy];
10886       int e;
10887
10888       can_replace[xx][yy] = TRUE;
10889
10890       if (ex == x && ey == y)   // do not check changing element itself
10891         continue;
10892
10893       if (content_element == EL_EMPTY_SPACE)
10894       {
10895         can_replace[xx][yy] = FALSE;    // do not replace border with space
10896
10897         continue;
10898       }
10899
10900       if (!IN_LEV_FIELD(ex, ey))
10901       {
10902         can_replace[xx][yy] = FALSE;
10903         complete_replace = FALSE;
10904
10905         continue;
10906       }
10907
10908       e = Tile[ex][ey];
10909
10910       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10911         e = MovingOrBlocked2Element(ex, ey);
10912
10913       is_empty = (IS_FREE(ex, ey) ||
10914                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10915
10916       is_walkable     = (is_empty || IS_WALKABLE(e));
10917       is_diggable     = (is_empty || IS_DIGGABLE(e));
10918       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10919       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10920       is_removable    = (is_diggable || is_collectible);
10921
10922       can_replace[xx][yy] =
10923         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10924           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10925           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10926           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10927           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10928           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10929          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10930
10931       if (!can_replace[xx][yy])
10932         complete_replace = FALSE;
10933     }
10934
10935     if (!change->only_if_complete || complete_replace)
10936     {
10937       boolean something_has_changed = FALSE;
10938
10939       if (change->only_if_complete && change->use_random_replace &&
10940           RND(100) < change->random_percentage)
10941         return FALSE;
10942
10943       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10944       {
10945         int ex = x + xx - 1;
10946         int ey = y + yy - 1;
10947         int content_element;
10948
10949         if (can_replace[xx][yy] && (!change->use_random_replace ||
10950                                     RND(100) < change->random_percentage))
10951         {
10952           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10953             RemoveMovingField(ex, ey);
10954
10955           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10956
10957           content_element = change->target_content.e[xx][yy];
10958           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10959                                               ce_value, ce_score);
10960
10961           CreateElementFromChange(ex, ey, target_element);
10962
10963           something_has_changed = TRUE;
10964
10965           // for symmetry reasons, freeze newly created border elements
10966           if (ex != x || ey != y)
10967             Stop[ex][ey] = TRUE;        // no more moving in this frame
10968         }
10969       }
10970
10971       if (something_has_changed)
10972       {
10973         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10974         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10975       }
10976     }
10977   }
10978   else
10979   {
10980     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10981                                         ce_value, ce_score);
10982
10983     if (element == EL_DIAGONAL_GROWING ||
10984         element == EL_DIAGONAL_SHRINKING)
10985     {
10986       target_element = Store[x][y];
10987
10988       Store[x][y] = EL_EMPTY;
10989     }
10990
10991     // special case: element changes to player (and may be kept if walkable)
10992     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10993       CreateElementFromChange(x, y, EL_EMPTY);
10994
10995     CreateElementFromChange(x, y, target_element);
10996
10997     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10998     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10999   }
11000
11001   // this uses direct change before indirect change
11002   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11003
11004   return TRUE;
11005 }
11006
11007 static void HandleElementChange(int x, int y, int page)
11008 {
11009   int element = MovingOrBlocked2Element(x, y);
11010   struct ElementInfo *ei = &element_info[element];
11011   struct ElementChangeInfo *change = &ei->change_page[page];
11012   boolean handle_action_before_change = FALSE;
11013
11014 #ifdef DEBUG
11015   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11016       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11017   {
11018     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11019           x, y, element, element_info[element].token_name);
11020     Debug("game:playing:HandleElementChange", "This should never happen!");
11021   }
11022 #endif
11023
11024   // this can happen with classic bombs on walkable, changing elements
11025   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11026   {
11027     return;
11028   }
11029
11030   if (ChangeDelay[x][y] == 0)           // initialize element change
11031   {
11032     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11033
11034     if (change->can_change)
11035     {
11036       // !!! not clear why graphic animation should be reset at all here !!!
11037       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11038       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11039
11040       /*
11041         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11042
11043         When using an animation frame delay of 1 (this only happens with
11044         "sp_zonk.moving.left/right" in the classic graphics), the default
11045         (non-moving) animation shows wrong animation frames (while the
11046         moving animation, like "sp_zonk.moving.left/right", is correct,
11047         so this graphical bug never shows up with the classic graphics).
11048         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11049         be drawn instead of the correct frames 0,1,2,3. This is caused by
11050         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11051         an element change: First when the change delay ("ChangeDelay[][]")
11052         counter has reached zero after decrementing, then a second time in
11053         the next frame (after "GfxFrame[][]" was already incremented) when
11054         "ChangeDelay[][]" is reset to the initial delay value again.
11055
11056         This causes frame 0 to be drawn twice, while the last frame won't
11057         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11058
11059         As some animations may already be cleverly designed around this bug
11060         (at least the "Snake Bite" snake tail animation does this), it cannot
11061         simply be fixed here without breaking such existing animations.
11062         Unfortunately, it cannot easily be detected if a graphics set was
11063         designed "before" or "after" the bug was fixed. As a workaround,
11064         a new graphics set option "game.graphics_engine_version" was added
11065         to be able to specify the game's major release version for which the
11066         graphics set was designed, which can then be used to decide if the
11067         bugfix should be used (version 4 and above) or not (version 3 or
11068         below, or if no version was specified at all, as with old sets).
11069
11070         (The wrong/fixed animation frames can be tested with the test level set
11071         "test_gfxframe" and level "000", which contains a specially prepared
11072         custom element at level position (x/y) == (11/9) which uses the zonk
11073         animation mentioned above. Using "game.graphics_engine_version: 4"
11074         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11075         This can also be seen from the debug output for this test element.)
11076       */
11077
11078       // when a custom element is about to change (for example by change delay),
11079       // do not reset graphic animation when the custom element is moving
11080       if (game.graphics_engine_version < 4 &&
11081           !IS_MOVING(x, y))
11082       {
11083         ResetGfxAnimation(x, y);
11084         ResetRandomAnimationValue(x, y);
11085       }
11086
11087       if (change->pre_change_function)
11088         change->pre_change_function(x, y);
11089     }
11090   }
11091
11092   ChangeDelay[x][y]--;
11093
11094   if (ChangeDelay[x][y] != 0)           // continue element change
11095   {
11096     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11097
11098     // also needed if CE can not change, but has CE delay with CE action
11099     if (IS_ANIMATED(graphic))
11100       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11101
11102     if (change->can_change)
11103     {
11104       if (change->change_function)
11105         change->change_function(x, y);
11106     }
11107   }
11108   else                                  // finish element change
11109   {
11110     if (ChangePage[x][y] != -1)         // remember page from delayed change
11111     {
11112       page = ChangePage[x][y];
11113       ChangePage[x][y] = -1;
11114
11115       change = &ei->change_page[page];
11116     }
11117
11118     if (IS_MOVING(x, y))                // never change a running system ;-)
11119     {
11120       ChangeDelay[x][y] = 1;            // try change after next move step
11121       ChangePage[x][y] = page;          // remember page to use for change
11122
11123       return;
11124     }
11125
11126     // special case: set new level random seed before changing element
11127     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11128       handle_action_before_change = TRUE;
11129
11130     if (change->has_action && handle_action_before_change)
11131       ExecuteCustomElementAction(x, y, element, page);
11132
11133     if (change->can_change)
11134     {
11135       if (ChangeElement(x, y, element, page))
11136       {
11137         if (change->post_change_function)
11138           change->post_change_function(x, y);
11139       }
11140     }
11141
11142     if (change->has_action && !handle_action_before_change)
11143       ExecuteCustomElementAction(x, y, element, page);
11144   }
11145 }
11146
11147 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11148                                               int trigger_element,
11149                                               int trigger_event,
11150                                               int trigger_player,
11151                                               int trigger_side,
11152                                               int trigger_page)
11153 {
11154   boolean change_done_any = FALSE;
11155   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11156   int i;
11157
11158   if (!(trigger_events[trigger_element][trigger_event]))
11159     return FALSE;
11160
11161   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11162
11163   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11164   {
11165     int element = EL_CUSTOM_START + i;
11166     boolean change_done = FALSE;
11167     int p;
11168
11169     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11170         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11171       continue;
11172
11173     for (p = 0; p < element_info[element].num_change_pages; p++)
11174     {
11175       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11176
11177       if (change->can_change_or_has_action &&
11178           change->has_event[trigger_event] &&
11179           change->trigger_side & trigger_side &&
11180           change->trigger_player & trigger_player &&
11181           change->trigger_page & trigger_page_bits &&
11182           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11183       {
11184         change->actual_trigger_element = trigger_element;
11185         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11186         change->actual_trigger_player_bits = trigger_player;
11187         change->actual_trigger_side = trigger_side;
11188         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11189         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11190         change->actual_trigger_x = trigger_x;
11191         change->actual_trigger_y = trigger_y;
11192
11193         if ((change->can_change && !change_done) || change->has_action)
11194         {
11195           int x, y;
11196
11197           SCAN_PLAYFIELD(x, y)
11198           {
11199             if (Tile[x][y] == element)
11200             {
11201               if (change->can_change && !change_done)
11202               {
11203                 // if element already changed in this frame, not only prevent
11204                 // another element change (checked in ChangeElement()), but
11205                 // also prevent additional element actions for this element
11206
11207                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11208                     !level.use_action_after_change_bug)
11209                   continue;
11210
11211                 ChangeDelay[x][y] = 1;
11212                 ChangeEvent[x][y] = trigger_event;
11213
11214                 HandleElementChange(x, y, p);
11215               }
11216               else if (change->has_action)
11217               {
11218                 // if element already changed in this frame, not only prevent
11219                 // another element change (checked in ChangeElement()), but
11220                 // also prevent additional element actions for this element
11221
11222                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11223                     !level.use_action_after_change_bug)
11224                   continue;
11225
11226                 ExecuteCustomElementAction(x, y, element, p);
11227                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11228               }
11229             }
11230           }
11231
11232           if (change->can_change)
11233           {
11234             change_done = TRUE;
11235             change_done_any = TRUE;
11236           }
11237         }
11238       }
11239     }
11240   }
11241
11242   RECURSION_LOOP_DETECTION_END();
11243
11244   return change_done_any;
11245 }
11246
11247 static boolean CheckElementChangeExt(int x, int y,
11248                                      int element,
11249                                      int trigger_element,
11250                                      int trigger_event,
11251                                      int trigger_player,
11252                                      int trigger_side)
11253 {
11254   boolean change_done = FALSE;
11255   int p;
11256
11257   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11258       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11259     return FALSE;
11260
11261   if (Tile[x][y] == EL_BLOCKED)
11262   {
11263     Blocked2Moving(x, y, &x, &y);
11264     element = Tile[x][y];
11265   }
11266
11267   // check if element has already changed or is about to change after moving
11268   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11269        Tile[x][y] != element) ||
11270
11271       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11272        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11273         ChangePage[x][y] != -1)))
11274     return FALSE;
11275
11276   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11277
11278   for (p = 0; p < element_info[element].num_change_pages; p++)
11279   {
11280     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11281
11282     /* check trigger element for all events where the element that is checked
11283        for changing interacts with a directly adjacent element -- this is
11284        different to element changes that affect other elements to change on the
11285        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11286     boolean check_trigger_element =
11287       (trigger_event == CE_NEXT_TO_X ||
11288        trigger_event == CE_TOUCHING_X ||
11289        trigger_event == CE_HITTING_X ||
11290        trigger_event == CE_HIT_BY_X ||
11291        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11292
11293     if (change->can_change_or_has_action &&
11294         change->has_event[trigger_event] &&
11295         change->trigger_side & trigger_side &&
11296         change->trigger_player & trigger_player &&
11297         (!check_trigger_element ||
11298          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11299     {
11300       change->actual_trigger_element = trigger_element;
11301       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11302       change->actual_trigger_player_bits = trigger_player;
11303       change->actual_trigger_side = trigger_side;
11304       change->actual_trigger_ce_value = CustomValue[x][y];
11305       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11306       change->actual_trigger_x = x;
11307       change->actual_trigger_y = y;
11308
11309       // special case: trigger element not at (x,y) position for some events
11310       if (check_trigger_element)
11311       {
11312         static struct
11313         {
11314           int dx, dy;
11315         } move_xy[] =
11316           {
11317             {  0,  0 },
11318             { -1,  0 },
11319             { +1,  0 },
11320             {  0,  0 },
11321             {  0, -1 },
11322             {  0,  0 }, { 0, 0 }, { 0, 0 },
11323             {  0, +1 }
11324           };
11325
11326         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11327         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11328
11329         change->actual_trigger_ce_value = CustomValue[xx][yy];
11330         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11331         change->actual_trigger_x = xx;
11332         change->actual_trigger_y = yy;
11333       }
11334
11335       if (change->can_change && !change_done)
11336       {
11337         ChangeDelay[x][y] = 1;
11338         ChangeEvent[x][y] = trigger_event;
11339
11340         HandleElementChange(x, y, p);
11341
11342         change_done = TRUE;
11343       }
11344       else if (change->has_action)
11345       {
11346         ExecuteCustomElementAction(x, y, element, p);
11347         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11348       }
11349     }
11350   }
11351
11352   RECURSION_LOOP_DETECTION_END();
11353
11354   return change_done;
11355 }
11356
11357 static void PlayPlayerSound(struct PlayerInfo *player)
11358 {
11359   int jx = player->jx, jy = player->jy;
11360   int sound_element = player->artwork_element;
11361   int last_action = player->last_action_waiting;
11362   int action = player->action_waiting;
11363
11364   if (player->is_waiting)
11365   {
11366     if (action != last_action)
11367       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11368     else
11369       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11370   }
11371   else
11372   {
11373     if (action != last_action)
11374       StopSound(element_info[sound_element].sound[last_action]);
11375
11376     if (last_action == ACTION_SLEEPING)
11377       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11378   }
11379 }
11380
11381 static void PlayAllPlayersSound(void)
11382 {
11383   int i;
11384
11385   for (i = 0; i < MAX_PLAYERS; i++)
11386     if (stored_player[i].active)
11387       PlayPlayerSound(&stored_player[i]);
11388 }
11389
11390 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11391 {
11392   boolean last_waiting = player->is_waiting;
11393   int move_dir = player->MovDir;
11394
11395   player->dir_waiting = move_dir;
11396   player->last_action_waiting = player->action_waiting;
11397
11398   if (is_waiting)
11399   {
11400     if (!last_waiting)          // not waiting -> waiting
11401     {
11402       player->is_waiting = TRUE;
11403
11404       player->frame_counter_bored =
11405         FrameCounter +
11406         game.player_boring_delay_fixed +
11407         GetSimpleRandom(game.player_boring_delay_random);
11408       player->frame_counter_sleeping =
11409         FrameCounter +
11410         game.player_sleeping_delay_fixed +
11411         GetSimpleRandom(game.player_sleeping_delay_random);
11412
11413       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11414     }
11415
11416     if (game.player_sleeping_delay_fixed +
11417         game.player_sleeping_delay_random > 0 &&
11418         player->anim_delay_counter == 0 &&
11419         player->post_delay_counter == 0 &&
11420         FrameCounter >= player->frame_counter_sleeping)
11421       player->is_sleeping = TRUE;
11422     else if (game.player_boring_delay_fixed +
11423              game.player_boring_delay_random > 0 &&
11424              FrameCounter >= player->frame_counter_bored)
11425       player->is_bored = TRUE;
11426
11427     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11428                               player->is_bored ? ACTION_BORING :
11429                               ACTION_WAITING);
11430
11431     if (player->is_sleeping && player->use_murphy)
11432     {
11433       // special case for sleeping Murphy when leaning against non-free tile
11434
11435       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11436           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11437            !IS_MOVING(player->jx - 1, player->jy)))
11438         move_dir = MV_LEFT;
11439       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11440                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11441                 !IS_MOVING(player->jx + 1, player->jy)))
11442         move_dir = MV_RIGHT;
11443       else
11444         player->is_sleeping = FALSE;
11445
11446       player->dir_waiting = move_dir;
11447     }
11448
11449     if (player->is_sleeping)
11450     {
11451       if (player->num_special_action_sleeping > 0)
11452       {
11453         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11454         {
11455           int last_special_action = player->special_action_sleeping;
11456           int num_special_action = player->num_special_action_sleeping;
11457           int special_action =
11458             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11459              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11460              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11461              last_special_action + 1 : ACTION_SLEEPING);
11462           int special_graphic =
11463             el_act_dir2img(player->artwork_element, special_action, move_dir);
11464
11465           player->anim_delay_counter =
11466             graphic_info[special_graphic].anim_delay_fixed +
11467             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11468           player->post_delay_counter =
11469             graphic_info[special_graphic].post_delay_fixed +
11470             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11471
11472           player->special_action_sleeping = special_action;
11473         }
11474
11475         if (player->anim_delay_counter > 0)
11476         {
11477           player->action_waiting = player->special_action_sleeping;
11478           player->anim_delay_counter--;
11479         }
11480         else if (player->post_delay_counter > 0)
11481         {
11482           player->post_delay_counter--;
11483         }
11484       }
11485     }
11486     else if (player->is_bored)
11487     {
11488       if (player->num_special_action_bored > 0)
11489       {
11490         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11491         {
11492           int special_action =
11493             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11494           int special_graphic =
11495             el_act_dir2img(player->artwork_element, special_action, move_dir);
11496
11497           player->anim_delay_counter =
11498             graphic_info[special_graphic].anim_delay_fixed +
11499             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11500           player->post_delay_counter =
11501             graphic_info[special_graphic].post_delay_fixed +
11502             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11503
11504           player->special_action_bored = special_action;
11505         }
11506
11507         if (player->anim_delay_counter > 0)
11508         {
11509           player->action_waiting = player->special_action_bored;
11510           player->anim_delay_counter--;
11511         }
11512         else if (player->post_delay_counter > 0)
11513         {
11514           player->post_delay_counter--;
11515         }
11516       }
11517     }
11518   }
11519   else if (last_waiting)        // waiting -> not waiting
11520   {
11521     player->is_waiting = FALSE;
11522     player->is_bored = FALSE;
11523     player->is_sleeping = FALSE;
11524
11525     player->frame_counter_bored = -1;
11526     player->frame_counter_sleeping = -1;
11527
11528     player->anim_delay_counter = 0;
11529     player->post_delay_counter = 0;
11530
11531     player->dir_waiting = player->MovDir;
11532     player->action_waiting = ACTION_DEFAULT;
11533
11534     player->special_action_bored = ACTION_DEFAULT;
11535     player->special_action_sleeping = ACTION_DEFAULT;
11536   }
11537 }
11538
11539 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11540 {
11541   if ((!player->is_moving  && player->was_moving) ||
11542       (player->MovPos == 0 && player->was_moving) ||
11543       (player->is_snapping && !player->was_snapping) ||
11544       (player->is_dropping && !player->was_dropping))
11545   {
11546     if (!CheckSaveEngineSnapshotToList())
11547       return;
11548
11549     player->was_moving = FALSE;
11550     player->was_snapping = TRUE;
11551     player->was_dropping = TRUE;
11552   }
11553   else
11554   {
11555     if (player->is_moving)
11556       player->was_moving = TRUE;
11557
11558     if (!player->is_snapping)
11559       player->was_snapping = FALSE;
11560
11561     if (!player->is_dropping)
11562       player->was_dropping = FALSE;
11563   }
11564
11565   static struct MouseActionInfo mouse_action_last = { 0 };
11566   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11567   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11568
11569   if (new_released)
11570     CheckSaveEngineSnapshotToList();
11571
11572   mouse_action_last = mouse_action;
11573 }
11574
11575 static void CheckSingleStepMode(struct PlayerInfo *player)
11576 {
11577   if (tape.single_step && tape.recording && !tape.pausing)
11578   {
11579     // as it is called "single step mode", just return to pause mode when the
11580     // player stopped moving after one tile (or never starts moving at all)
11581     // (reverse logic needed here in case single step mode used in team mode)
11582     if (player->is_moving ||
11583         player->is_pushing ||
11584         player->is_dropping_pressed ||
11585         player->effective_mouse_action.button)
11586       game.enter_single_step_mode = FALSE;
11587   }
11588
11589   CheckSaveEngineSnapshot(player);
11590 }
11591
11592 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11593 {
11594   int left      = player_action & JOY_LEFT;
11595   int right     = player_action & JOY_RIGHT;
11596   int up        = player_action & JOY_UP;
11597   int down      = player_action & JOY_DOWN;
11598   int button1   = player_action & JOY_BUTTON_1;
11599   int button2   = player_action & JOY_BUTTON_2;
11600   int dx        = (left ? -1 : right ? 1 : 0);
11601   int dy        = (up   ? -1 : down  ? 1 : 0);
11602
11603   if (!player->active || tape.pausing)
11604     return 0;
11605
11606   if (player_action)
11607   {
11608     if (button1)
11609       SnapField(player, dx, dy);
11610     else
11611     {
11612       if (button2)
11613         DropElement(player);
11614
11615       MovePlayer(player, dx, dy);
11616     }
11617
11618     CheckSingleStepMode(player);
11619
11620     SetPlayerWaiting(player, FALSE);
11621
11622     return player_action;
11623   }
11624   else
11625   {
11626     // no actions for this player (no input at player's configured device)
11627
11628     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11629     SnapField(player, 0, 0);
11630     CheckGravityMovementWhenNotMoving(player);
11631
11632     if (player->MovPos == 0)
11633       SetPlayerWaiting(player, TRUE);
11634
11635     if (player->MovPos == 0)    // needed for tape.playing
11636       player->is_moving = FALSE;
11637
11638     player->is_dropping = FALSE;
11639     player->is_dropping_pressed = FALSE;
11640     player->drop_pressed_delay = 0;
11641
11642     CheckSingleStepMode(player);
11643
11644     return 0;
11645   }
11646 }
11647
11648 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11649                                          byte *tape_action)
11650 {
11651   if (!tape.use_mouse_actions)
11652     return;
11653
11654   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11655   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11656   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11657 }
11658
11659 static void SetTapeActionFromMouseAction(byte *tape_action,
11660                                          struct MouseActionInfo *mouse_action)
11661 {
11662   if (!tape.use_mouse_actions)
11663     return;
11664
11665   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11666   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11667   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11668 }
11669
11670 static void CheckLevelSolved(void)
11671 {
11672   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11673   {
11674     if (game_bd.level_solved &&
11675         !game_bd.game_over)                             // game won
11676     {
11677       LevelSolved();
11678
11679       game_bd.game_over = TRUE;
11680
11681       game.all_players_gone = TRUE;
11682     }
11683
11684     if (game_bd.game_over)                              // game lost
11685       game.all_players_gone = TRUE;
11686   }
11687   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11688   {
11689     if (game_em.level_solved &&
11690         !game_em.game_over)                             // game won
11691     {
11692       LevelSolved();
11693
11694       game_em.game_over = TRUE;
11695
11696       game.all_players_gone = TRUE;
11697     }
11698
11699     if (game_em.game_over)                              // game lost
11700       game.all_players_gone = TRUE;
11701   }
11702   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11703   {
11704     if (game_sp.level_solved &&
11705         !game_sp.game_over)                             // game won
11706     {
11707       LevelSolved();
11708
11709       game_sp.game_over = TRUE;
11710
11711       game.all_players_gone = TRUE;
11712     }
11713
11714     if (game_sp.game_over)                              // game lost
11715       game.all_players_gone = TRUE;
11716   }
11717   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11718   {
11719     if (game_mm.level_solved &&
11720         !game_mm.game_over)                             // game won
11721     {
11722       LevelSolved();
11723
11724       game_mm.game_over = TRUE;
11725
11726       game.all_players_gone = TRUE;
11727     }
11728
11729     if (game_mm.game_over)                              // game lost
11730       game.all_players_gone = TRUE;
11731   }
11732 }
11733
11734 static void PlayTimeoutSound(int seconds_left)
11735 {
11736   // will be played directly by BD engine (for classic bonus time sounds)
11737   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11738     return;
11739
11740   // try to use individual "running out of time" sound for each second left
11741   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11742
11743   // if special sound per second not defined, use default sound
11744   if (getSoundInfoEntryFilename(sound) == NULL)
11745     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11746
11747   // if out of time, but player still alive, play special "timeout" sound, if defined
11748   if (seconds_left == 0 && !checkGameFailed())
11749     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11750       sound = SND_GAME_TIMEOUT;
11751
11752   PlaySound(sound);
11753 }
11754
11755 static void CheckLevelTime_StepCounter(void)
11756 {
11757   int i;
11758
11759   TimePlayed++;
11760
11761   if (TimeLeft > 0)
11762   {
11763     TimeLeft--;
11764
11765     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11766       PlayTimeoutSound(TimeLeft);
11767
11768     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11769
11770     DisplayGameControlValues();
11771
11772     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11773       for (i = 0; i < MAX_PLAYERS; i++)
11774         KillPlayer(&stored_player[i]);
11775   }
11776   else if (game.no_level_time_limit && !game.all_players_gone)
11777   {
11778     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11779
11780     DisplayGameControlValues();
11781   }
11782 }
11783
11784 static void CheckLevelTime(void)
11785 {
11786   int frames_per_second = FRAMES_PER_SECOND;
11787   int i;
11788
11789   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11790   {
11791     // level time may be running slower in native BD engine
11792     frames_per_second = getFramesPerSecond_BD();
11793
11794     // if native engine time changed, force main engine time change
11795     if (getTimeLeft_BD() < TimeLeft)
11796       TimeFrames = frames_per_second;
11797
11798     // if last second running, wait for native engine time to exactly reach zero
11799     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11800       TimeFrames = frames_per_second - 1;
11801   }
11802
11803   if (TimeFrames >= frames_per_second)
11804   {
11805     TimeFrames = 0;
11806
11807     for (i = 0; i < MAX_PLAYERS; i++)
11808     {
11809       struct PlayerInfo *player = &stored_player[i];
11810
11811       if (SHIELD_ON(player))
11812       {
11813         player->shield_normal_time_left--;
11814
11815         if (player->shield_deadly_time_left > 0)
11816           player->shield_deadly_time_left--;
11817       }
11818     }
11819
11820     if (!game.LevelSolved && !level.use_step_counter)
11821     {
11822       TimePlayed++;
11823
11824       if (TimeLeft > 0)
11825       {
11826         TimeLeft--;
11827
11828         if (TimeLeft <= 10 && game.time_limit)
11829           PlayTimeoutSound(TimeLeft);
11830
11831         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11832            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11833
11834         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11835
11836         if (!TimeLeft && game.time_limit)
11837         {
11838           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11839           {
11840             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11841               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11842           }
11843           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11844           {
11845             game_em.lev->killed_out_of_time = TRUE;
11846           }
11847           else
11848           {
11849             for (i = 0; i < MAX_PLAYERS; i++)
11850               KillPlayer(&stored_player[i]);
11851           }
11852         }
11853       }
11854       else if (game.no_level_time_limit && !game.all_players_gone)
11855       {
11856         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11857       }
11858
11859       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11860     }
11861   }
11862
11863   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11864   {
11865     TapeTimeFrames = 0;
11866     TapeTime++;
11867
11868     if (tape.recording || tape.playing)
11869       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11870   }
11871
11872   if (tape.recording || tape.playing)
11873     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11874
11875   UpdateAndDisplayGameControlValues();
11876 }
11877
11878 void AdvanceFrameAndPlayerCounters(int player_nr)
11879 {
11880   int i;
11881
11882   // handle game and tape time differently for native BD game engine
11883
11884   // tape time is running in native BD engine even if player is not hatched yet
11885   if (!checkGameRunning())
11886     return;
11887
11888   // advance frame counters (global frame counter and tape time frame counter)
11889   FrameCounter++;
11890   TapeTimeFrames++;
11891
11892   // level time is running in native BD engine after player is being hatched
11893   if (!checkGamePlaying())
11894     return;
11895
11896   // advance time frame counter (used to control available time to solve level)
11897   TimeFrames++;
11898
11899   // advance player counters (counters for move delay, move animation etc.)
11900   for (i = 0; i < MAX_PLAYERS; i++)
11901   {
11902     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11903     int move_delay_value = stored_player[i].move_delay_value;
11904     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11905
11906     if (!advance_player_counters)       // not all players may be affected
11907       continue;
11908
11909     if (move_frames == 0)       // less than one move per game frame
11910     {
11911       int stepsize = TILEX / move_delay_value;
11912       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11913       int count = (stored_player[i].is_moving ?
11914                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11915
11916       if (count % delay == 0)
11917         move_frames = 1;
11918     }
11919
11920     stored_player[i].Frame += move_frames;
11921
11922     if (stored_player[i].MovPos != 0)
11923       stored_player[i].StepFrame += move_frames;
11924
11925     if (stored_player[i].move_delay > 0)
11926       stored_player[i].move_delay--;
11927
11928     // due to bugs in previous versions, counter must count up, not down
11929     if (stored_player[i].push_delay != -1)
11930       stored_player[i].push_delay++;
11931
11932     if (stored_player[i].drop_delay > 0)
11933       stored_player[i].drop_delay--;
11934
11935     if (stored_player[i].is_dropping_pressed)
11936       stored_player[i].drop_pressed_delay++;
11937   }
11938 }
11939
11940 void AdvanceFrameCounter(void)
11941 {
11942   FrameCounter++;
11943 }
11944
11945 void AdvanceGfxFrame(void)
11946 {
11947   int x, y;
11948
11949   SCAN_PLAYFIELD(x, y)
11950   {
11951     GfxFrame[x][y]++;
11952   }
11953 }
11954
11955 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11956                               struct MouseActionInfo *mouse_action_last)
11957 {
11958   if (mouse_action->button)
11959   {
11960     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11961     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11962     int x = mouse_action->lx;
11963     int y = mouse_action->ly;
11964     int element = Tile[x][y];
11965
11966     if (new_button)
11967     {
11968       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11969       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11970                                          ch_button);
11971     }
11972
11973     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11974     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11975                                        ch_button);
11976
11977     if (level.use_step_counter)
11978     {
11979       boolean counted_click = FALSE;
11980
11981       // element clicked that can change when clicked/pressed
11982       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11983           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11984            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11985         counted_click = TRUE;
11986
11987       // element clicked that can trigger change when clicked/pressed
11988       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11989           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11990         counted_click = TRUE;
11991
11992       if (new_button && counted_click)
11993         CheckLevelTime_StepCounter();
11994     }
11995   }
11996 }
11997
11998 void StartGameActions(boolean init_network_game, boolean record_tape,
11999                       int random_seed)
12000 {
12001   unsigned int new_random_seed = InitRND(random_seed);
12002
12003   if (record_tape)
12004     TapeStartRecording(new_random_seed);
12005
12006   if (setup.auto_pause_on_start && !tape.pausing)
12007     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12008
12009   if (init_network_game)
12010   {
12011     SendToServer_LevelFile();
12012     SendToServer_StartPlaying();
12013
12014     return;
12015   }
12016
12017   InitGame();
12018 }
12019
12020 static void GameActionsExt(void)
12021 {
12022 #if 0
12023   static unsigned int game_frame_delay = 0;
12024 #endif
12025   unsigned int game_frame_delay_value;
12026   byte *recorded_player_action;
12027   byte summarized_player_action = 0;
12028   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12029   int i;
12030
12031   // detect endless loops, caused by custom element programming
12032   if (recursion_loop_detected && recursion_loop_depth == 0)
12033   {
12034     char *message = getStringCat3("Internal Error! Element ",
12035                                   EL_NAME(recursion_loop_element),
12036                                   " caused endless loop! Quit the game?");
12037
12038     Warn("element '%s' caused endless loop in game engine",
12039          EL_NAME(recursion_loop_element));
12040
12041     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12042
12043     recursion_loop_detected = FALSE;    // if game should be continued
12044
12045     free(message);
12046
12047     return;
12048   }
12049
12050   if (game.restart_level)
12051     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12052
12053   CheckLevelSolved();
12054
12055   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12056     GameWon();
12057
12058   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12059     TapeStop();
12060
12061   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12062     return;
12063
12064   game_frame_delay_value =
12065     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12066
12067   if (tape.playing && tape.warp_forward && !tape.pausing)
12068     game_frame_delay_value = 0;
12069
12070   SetVideoFrameDelay(game_frame_delay_value);
12071
12072   // (de)activate virtual buttons depending on current game status
12073   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12074   {
12075     if (game.all_players_gone)  // if no players there to be controlled anymore
12076       SetOverlayActive(FALSE);
12077     else if (!tape.playing)     // if game continues after tape stopped playing
12078       SetOverlayActive(TRUE);
12079   }
12080
12081 #if 0
12082 #if 0
12083   // ---------- main game synchronization point ----------
12084
12085   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12086
12087   Debug("game:playing:skip", "skip == %d", skip);
12088
12089 #else
12090   // ---------- main game synchronization point ----------
12091
12092   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12093 #endif
12094 #endif
12095
12096   if (network_playing && !network_player_action_received)
12097   {
12098     // try to get network player actions in time
12099
12100     // last chance to get network player actions without main loop delay
12101     HandleNetworking();
12102
12103     // game was quit by network peer
12104     if (game_status != GAME_MODE_PLAYING)
12105       return;
12106
12107     // check if network player actions still missing and game still running
12108     if (!network_player_action_received && !checkGameEnded())
12109       return;           // failed to get network player actions in time
12110
12111     // do not yet reset "network_player_action_received" (for tape.pausing)
12112   }
12113
12114   if (tape.pausing)
12115     return;
12116
12117   // at this point we know that we really continue executing the game
12118
12119   network_player_action_received = FALSE;
12120
12121   // when playing tape, read previously recorded player input from tape data
12122   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12123
12124   local_player->effective_mouse_action = local_player->mouse_action;
12125
12126   if (recorded_player_action != NULL)
12127     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12128                                  recorded_player_action);
12129
12130   // TapePlayAction() may return NULL when toggling to "pause before death"
12131   if (tape.pausing)
12132     return;
12133
12134   if (tape.set_centered_player)
12135   {
12136     game.centered_player_nr_next = tape.centered_player_nr_next;
12137     game.set_centered_player = TRUE;
12138   }
12139
12140   for (i = 0; i < MAX_PLAYERS; i++)
12141   {
12142     summarized_player_action |= stored_player[i].action;
12143
12144     if (!network_playing && (game.team_mode || tape.playing))
12145       stored_player[i].effective_action = stored_player[i].action;
12146   }
12147
12148   if (network_playing && !checkGameEnded())
12149     SendToServer_MovePlayer(summarized_player_action);
12150
12151   // summarize all actions at local players mapped input device position
12152   // (this allows using different input devices in single player mode)
12153   if (!network.enabled && !game.team_mode)
12154     stored_player[map_player_action[local_player->index_nr]].effective_action =
12155       summarized_player_action;
12156
12157   // summarize all actions at centered player in local team mode
12158   if (tape.recording &&
12159       setup.team_mode && !network.enabled &&
12160       setup.input_on_focus &&
12161       game.centered_player_nr != -1)
12162   {
12163     for (i = 0; i < MAX_PLAYERS; i++)
12164       stored_player[map_player_action[i]].effective_action =
12165         (i == game.centered_player_nr ? summarized_player_action : 0);
12166   }
12167
12168   if (recorded_player_action != NULL)
12169     for (i = 0; i < MAX_PLAYERS; i++)
12170       stored_player[i].effective_action = recorded_player_action[i];
12171
12172   for (i = 0; i < MAX_PLAYERS; i++)
12173   {
12174     tape_action[i] = stored_player[i].effective_action;
12175
12176     /* (this may happen in the RND game engine if a player was not present on
12177        the playfield on level start, but appeared later from a custom element */
12178     if (setup.team_mode &&
12179         tape.recording &&
12180         tape_action[i] &&
12181         !tape.player_participates[i])
12182       tape.player_participates[i] = TRUE;
12183   }
12184
12185   SetTapeActionFromMouseAction(tape_action,
12186                                &local_player->effective_mouse_action);
12187
12188   // only record actions from input devices, but not programmed actions
12189   if (tape.recording)
12190     TapeRecordAction(tape_action);
12191
12192   // remember if game was played (especially after tape stopped playing)
12193   if (!tape.playing && summarized_player_action && !checkGameFailed())
12194     game.GamePlayed = TRUE;
12195
12196 #if USE_NEW_PLAYER_ASSIGNMENTS
12197   // !!! also map player actions in single player mode !!!
12198   // if (game.team_mode)
12199   if (1)
12200   {
12201     byte mapped_action[MAX_PLAYERS];
12202
12203 #if DEBUG_PLAYER_ACTIONS
12204     for (i = 0; i < MAX_PLAYERS; i++)
12205       DebugContinued("", "%d, ", stored_player[i].effective_action);
12206 #endif
12207
12208     for (i = 0; i < MAX_PLAYERS; i++)
12209       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12210
12211     for (i = 0; i < MAX_PLAYERS; i++)
12212       stored_player[i].effective_action = mapped_action[i];
12213
12214 #if DEBUG_PLAYER_ACTIONS
12215     DebugContinued("", "=> ");
12216     for (i = 0; i < MAX_PLAYERS; i++)
12217       DebugContinued("", "%d, ", stored_player[i].effective_action);
12218     DebugContinued("game:playing:player", "\n");
12219 #endif
12220   }
12221 #if DEBUG_PLAYER_ACTIONS
12222   else
12223   {
12224     for (i = 0; i < MAX_PLAYERS; i++)
12225       DebugContinued("", "%d, ", stored_player[i].effective_action);
12226     DebugContinued("game:playing:player", "\n");
12227   }
12228 #endif
12229 #endif
12230
12231   for (i = 0; i < MAX_PLAYERS; i++)
12232   {
12233     // allow engine snapshot in case of changed movement attempt
12234     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12235         (stored_player[i].effective_action & KEY_MOTION))
12236       game.snapshot.changed_action = TRUE;
12237
12238     // allow engine snapshot in case of snapping/dropping attempt
12239     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12240         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12241       game.snapshot.changed_action = TRUE;
12242
12243     game.snapshot.last_action[i] = stored_player[i].effective_action;
12244   }
12245
12246   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12247   {
12248     GameActions_BD_Main();
12249   }
12250   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12251   {
12252     GameActions_EM_Main();
12253   }
12254   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12255   {
12256     GameActions_SP_Main();
12257   }
12258   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12259   {
12260     GameActions_MM_Main();
12261   }
12262   else
12263   {
12264     GameActions_RND_Main();
12265   }
12266
12267   BlitScreenToBitmap(backbuffer);
12268
12269   CheckLevelSolved();
12270   CheckLevelTime();
12271
12272   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12273
12274   if (global.show_frames_per_second)
12275   {
12276     static unsigned int fps_counter = 0;
12277     static int fps_frames = 0;
12278     unsigned int fps_delay_ms = Counter() - fps_counter;
12279
12280     fps_frames++;
12281
12282     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12283     {
12284       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12285
12286       fps_frames = 0;
12287       fps_counter = Counter();
12288
12289       // always draw FPS to screen after FPS value was updated
12290       redraw_mask |= REDRAW_FPS;
12291     }
12292
12293     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12294     if (GetDrawDeactivationMask() == REDRAW_NONE)
12295       redraw_mask |= REDRAW_FPS;
12296   }
12297 }
12298
12299 static void GameActions_CheckSaveEngineSnapshot(void)
12300 {
12301   if (!game.snapshot.save_snapshot)
12302     return;
12303
12304   // clear flag for saving snapshot _before_ saving snapshot
12305   game.snapshot.save_snapshot = FALSE;
12306
12307   SaveEngineSnapshotToList();
12308 }
12309
12310 void GameActions(void)
12311 {
12312   GameActionsExt();
12313
12314   GameActions_CheckSaveEngineSnapshot();
12315 }
12316
12317 void GameActions_BD_Main(void)
12318 {
12319   byte effective_action[MAX_PLAYERS];
12320   int i;
12321
12322   for (i = 0; i < MAX_PLAYERS; i++)
12323     effective_action[i] = stored_player[i].effective_action;
12324
12325   GameActions_BD(effective_action);
12326 }
12327
12328 void GameActions_EM_Main(void)
12329 {
12330   byte effective_action[MAX_PLAYERS];
12331   int i;
12332
12333   for (i = 0; i < MAX_PLAYERS; i++)
12334     effective_action[i] = stored_player[i].effective_action;
12335
12336   GameActions_EM(effective_action);
12337 }
12338
12339 void GameActions_SP_Main(void)
12340 {
12341   byte effective_action[MAX_PLAYERS];
12342   int i;
12343
12344   for (i = 0; i < MAX_PLAYERS; i++)
12345     effective_action[i] = stored_player[i].effective_action;
12346
12347   GameActions_SP(effective_action);
12348
12349   for (i = 0; i < MAX_PLAYERS; i++)
12350   {
12351     if (stored_player[i].force_dropping)
12352       stored_player[i].action |= KEY_BUTTON_DROP;
12353
12354     stored_player[i].force_dropping = FALSE;
12355   }
12356 }
12357
12358 void GameActions_MM_Main(void)
12359 {
12360   AdvanceGfxFrame();
12361
12362   GameActions_MM(local_player->effective_mouse_action);
12363 }
12364
12365 void GameActions_RND_Main(void)
12366 {
12367   GameActions_RND();
12368 }
12369
12370 void GameActions_RND(void)
12371 {
12372   static struct MouseActionInfo mouse_action_last = { 0 };
12373   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12374   int magic_wall_x = 0, magic_wall_y = 0;
12375   int i, x, y, element, graphic, last_gfx_frame;
12376
12377   InitPlayfieldScanModeVars();
12378
12379   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12380   {
12381     SCAN_PLAYFIELD(x, y)
12382     {
12383       ChangeCount[x][y] = 0;
12384       ChangeEvent[x][y] = -1;
12385     }
12386   }
12387
12388   if (game.set_centered_player)
12389   {
12390     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12391
12392     // switching to "all players" only possible if all players fit to screen
12393     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12394     {
12395       game.centered_player_nr_next = game.centered_player_nr;
12396       game.set_centered_player = FALSE;
12397     }
12398
12399     // do not switch focus to non-existing (or non-active) player
12400     if (game.centered_player_nr_next >= 0 &&
12401         !stored_player[game.centered_player_nr_next].active)
12402     {
12403       game.centered_player_nr_next = game.centered_player_nr;
12404       game.set_centered_player = FALSE;
12405     }
12406   }
12407
12408   if (game.set_centered_player &&
12409       ScreenMovPos == 0)        // screen currently aligned at tile position
12410   {
12411     int sx, sy;
12412
12413     if (game.centered_player_nr_next == -1)
12414     {
12415       setScreenCenteredToAllPlayers(&sx, &sy);
12416     }
12417     else
12418     {
12419       sx = stored_player[game.centered_player_nr_next].jx;
12420       sy = stored_player[game.centered_player_nr_next].jy;
12421     }
12422
12423     game.centered_player_nr = game.centered_player_nr_next;
12424     game.set_centered_player = FALSE;
12425
12426     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12427     DrawGameDoorValues();
12428   }
12429
12430   // check single step mode (set flag and clear again if any player is active)
12431   game.enter_single_step_mode =
12432     (tape.single_step && tape.recording && !tape.pausing);
12433
12434   for (i = 0; i < MAX_PLAYERS; i++)
12435   {
12436     int actual_player_action = stored_player[i].effective_action;
12437
12438 #if 1
12439     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12440        - rnd_equinox_tetrachloride 048
12441        - rnd_equinox_tetrachloride_ii 096
12442        - rnd_emanuel_schmieg 002
12443        - doctor_sloan_ww 001, 020
12444     */
12445     if (stored_player[i].MovPos == 0)
12446       CheckGravityMovement(&stored_player[i]);
12447 #endif
12448
12449     // overwrite programmed action with tape action
12450     if (stored_player[i].programmed_action)
12451       actual_player_action = stored_player[i].programmed_action;
12452
12453     PlayerActions(&stored_player[i], actual_player_action);
12454
12455     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12456   }
12457
12458   // single step pause mode may already have been toggled by "ScrollPlayer()"
12459   if (game.enter_single_step_mode && !tape.pausing)
12460     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12461
12462   ScrollScreen(NULL, SCROLL_GO_ON);
12463
12464   /* for backwards compatibility, the following code emulates a fixed bug that
12465      occured when pushing elements (causing elements that just made their last
12466      pushing step to already (if possible) make their first falling step in the
12467      same game frame, which is bad); this code is also needed to use the famous
12468      "spring push bug" which is used in older levels and might be wanted to be
12469      used also in newer levels, but in this case the buggy pushing code is only
12470      affecting the "spring" element and no other elements */
12471
12472   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12473   {
12474     for (i = 0; i < MAX_PLAYERS; i++)
12475     {
12476       struct PlayerInfo *player = &stored_player[i];
12477       int x = player->jx;
12478       int y = player->jy;
12479
12480       if (player->active && player->is_pushing && player->is_moving &&
12481           IS_MOVING(x, y) &&
12482           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12483            Tile[x][y] == EL_SPRING))
12484       {
12485         ContinueMoving(x, y);
12486
12487         // continue moving after pushing (this is actually a bug)
12488         if (!IS_MOVING(x, y))
12489           Stop[x][y] = FALSE;
12490       }
12491     }
12492   }
12493
12494   SCAN_PLAYFIELD(x, y)
12495   {
12496     Last[x][y] = Tile[x][y];
12497
12498     ChangeCount[x][y] = 0;
12499     ChangeEvent[x][y] = -1;
12500
12501     // this must be handled before main playfield loop
12502     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12503     {
12504       MovDelay[x][y]--;
12505       if (MovDelay[x][y] <= 0)
12506         RemoveField(x, y);
12507     }
12508
12509     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12510     {
12511       MovDelay[x][y]--;
12512       if (MovDelay[x][y] <= 0)
12513       {
12514         int element = Store[x][y];
12515         int move_direction = MovDir[x][y];
12516         int player_index_bit = Store2[x][y];
12517
12518         Store[x][y] = 0;
12519         Store2[x][y] = 0;
12520
12521         RemoveField(x, y);
12522         TEST_DrawLevelField(x, y);
12523
12524         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12525
12526         if (IS_ENVELOPE(element))
12527           local_player->show_envelope = element;
12528       }
12529     }
12530
12531 #if DEBUG
12532     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12533     {
12534       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12535             x, y);
12536       Debug("game:playing:GameActions_RND", "This should never happen!");
12537
12538       ChangePage[x][y] = -1;
12539     }
12540 #endif
12541
12542     Stop[x][y] = FALSE;
12543     if (WasJustMoving[x][y] > 0)
12544       WasJustMoving[x][y]--;
12545     if (WasJustFalling[x][y] > 0)
12546       WasJustFalling[x][y]--;
12547     if (CheckCollision[x][y] > 0)
12548       CheckCollision[x][y]--;
12549     if (CheckImpact[x][y] > 0)
12550       CheckImpact[x][y]--;
12551
12552     GfxFrame[x][y]++;
12553
12554     /* reset finished pushing action (not done in ContinueMoving() to allow
12555        continuous pushing animation for elements with zero push delay) */
12556     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12557     {
12558       ResetGfxAnimation(x, y);
12559       TEST_DrawLevelField(x, y);
12560     }
12561
12562 #if DEBUG
12563     if (IS_BLOCKED(x, y))
12564     {
12565       int oldx, oldy;
12566
12567       Blocked2Moving(x, y, &oldx, &oldy);
12568       if (!IS_MOVING(oldx, oldy))
12569       {
12570         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12571         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12572         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12573         Debug("game:playing:GameActions_RND", "This should never happen!");
12574       }
12575     }
12576 #endif
12577   }
12578
12579   HandleMouseAction(&mouse_action, &mouse_action_last);
12580
12581   SCAN_PLAYFIELD(x, y)
12582   {
12583     element = Tile[x][y];
12584     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12585     last_gfx_frame = GfxFrame[x][y];
12586
12587     if (element == EL_EMPTY)
12588       graphic = el2img(GfxElementEmpty[x][y]);
12589
12590     ResetGfxFrame(x, y);
12591
12592     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12593       DrawLevelGraphicAnimation(x, y, graphic);
12594
12595     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12596         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12597       ResetRandomAnimationValue(x, y);
12598
12599     SetRandomAnimationValue(x, y);
12600
12601     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12602
12603     if (IS_INACTIVE(element))
12604     {
12605       if (IS_ANIMATED(graphic))
12606         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12607
12608       continue;
12609     }
12610
12611     // this may take place after moving, so 'element' may have changed
12612     if (IS_CHANGING(x, y) &&
12613         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12614     {
12615       int page = element_info[element].event_page_nr[CE_DELAY];
12616
12617       HandleElementChange(x, y, page);
12618
12619       element = Tile[x][y];
12620       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12621     }
12622
12623     CheckNextToConditions(x, y);
12624
12625     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12626     {
12627       StartMoving(x, y);
12628
12629       element = Tile[x][y];
12630       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12631
12632       if (IS_ANIMATED(graphic) &&
12633           !IS_MOVING(x, y) &&
12634           !Stop[x][y])
12635         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12636
12637       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12638         TEST_DrawTwinkleOnField(x, y);
12639     }
12640     else if (element == EL_ACID)
12641     {
12642       if (!Stop[x][y])
12643         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12644     }
12645     else if ((element == EL_EXIT_OPEN ||
12646               element == EL_EM_EXIT_OPEN ||
12647               element == EL_SP_EXIT_OPEN ||
12648               element == EL_STEEL_EXIT_OPEN ||
12649               element == EL_EM_STEEL_EXIT_OPEN ||
12650               element == EL_SP_TERMINAL ||
12651               element == EL_SP_TERMINAL_ACTIVE ||
12652               element == EL_EXTRA_TIME ||
12653               element == EL_SHIELD_NORMAL ||
12654               element == EL_SHIELD_DEADLY) &&
12655              IS_ANIMATED(graphic))
12656       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12657     else if (IS_MOVING(x, y))
12658       ContinueMoving(x, y);
12659     else if (IS_ACTIVE_BOMB(element))
12660       CheckDynamite(x, y);
12661     else if (element == EL_AMOEBA_GROWING)
12662       AmoebaGrowing(x, y);
12663     else if (element == EL_AMOEBA_SHRINKING)
12664       AmoebaShrinking(x, y);
12665
12666 #if !USE_NEW_AMOEBA_CODE
12667     else if (IS_AMOEBALIVE(element))
12668       AmoebaReproduce(x, y);
12669 #endif
12670
12671     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12672       Life(x, y);
12673     else if (element == EL_EXIT_CLOSED)
12674       CheckExit(x, y);
12675     else if (element == EL_EM_EXIT_CLOSED)
12676       CheckExitEM(x, y);
12677     else if (element == EL_STEEL_EXIT_CLOSED)
12678       CheckExitSteel(x, y);
12679     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12680       CheckExitSteelEM(x, y);
12681     else if (element == EL_SP_EXIT_CLOSED)
12682       CheckExitSP(x, y);
12683     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12684              element == EL_EXPANDABLE_STEELWALL_GROWING)
12685       WallGrowing(x, y);
12686     else if (element == EL_EXPANDABLE_WALL ||
12687              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12688              element == EL_EXPANDABLE_WALL_VERTICAL ||
12689              element == EL_EXPANDABLE_WALL_ANY ||
12690              element == EL_BD_EXPANDABLE_WALL ||
12691              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12692              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12693              element == EL_EXPANDABLE_STEELWALL_ANY)
12694       CheckWallGrowing(x, y);
12695     else if (element == EL_FLAMES)
12696       CheckForDragon(x, y);
12697     else if (element == EL_EXPLOSION)
12698       ; // drawing of correct explosion animation is handled separately
12699     else if (element == EL_ELEMENT_SNAPPING ||
12700              element == EL_DIAGONAL_SHRINKING ||
12701              element == EL_DIAGONAL_GROWING)
12702     {
12703       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12704
12705       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12706     }
12707     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12708       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12709
12710     if (IS_BELT_ACTIVE(element))
12711       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12712
12713     if (game.magic_wall_active)
12714     {
12715       int jx = local_player->jx, jy = local_player->jy;
12716
12717       // play the element sound at the position nearest to the player
12718       if ((element == EL_MAGIC_WALL_FULL ||
12719            element == EL_MAGIC_WALL_ACTIVE ||
12720            element == EL_MAGIC_WALL_EMPTYING ||
12721            element == EL_BD_MAGIC_WALL_FULL ||
12722            element == EL_BD_MAGIC_WALL_ACTIVE ||
12723            element == EL_BD_MAGIC_WALL_EMPTYING ||
12724            element == EL_DC_MAGIC_WALL_FULL ||
12725            element == EL_DC_MAGIC_WALL_ACTIVE ||
12726            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12727           ABS(x - jx) + ABS(y - jy) <
12728           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12729       {
12730         magic_wall_x = x;
12731         magic_wall_y = y;
12732       }
12733     }
12734   }
12735
12736 #if USE_NEW_AMOEBA_CODE
12737   // new experimental amoeba growth stuff
12738   if (!(FrameCounter % 8))
12739   {
12740     static unsigned int random = 1684108901;
12741
12742     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12743     {
12744       x = RND(lev_fieldx);
12745       y = RND(lev_fieldy);
12746       element = Tile[x][y];
12747
12748       if (!IS_PLAYER(x, y) &&
12749           (element == EL_EMPTY ||
12750            CAN_GROW_INTO(element) ||
12751            element == EL_QUICKSAND_EMPTY ||
12752            element == EL_QUICKSAND_FAST_EMPTY ||
12753            element == EL_ACID_SPLASH_LEFT ||
12754            element == EL_ACID_SPLASH_RIGHT))
12755       {
12756         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12757             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12758             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12759             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12760           Tile[x][y] = EL_AMOEBA_DROP;
12761       }
12762
12763       random = random * 129 + 1;
12764     }
12765   }
12766 #endif
12767
12768   game.explosions_delayed = FALSE;
12769
12770   SCAN_PLAYFIELD(x, y)
12771   {
12772     element = Tile[x][y];
12773
12774     if (ExplodeField[x][y])
12775       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12776     else if (element == EL_EXPLOSION)
12777       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12778
12779     ExplodeField[x][y] = EX_TYPE_NONE;
12780   }
12781
12782   game.explosions_delayed = TRUE;
12783
12784   if (game.magic_wall_active)
12785   {
12786     if (!(game.magic_wall_time_left % 4))
12787     {
12788       int element = Tile[magic_wall_x][magic_wall_y];
12789
12790       if (element == EL_BD_MAGIC_WALL_FULL ||
12791           element == EL_BD_MAGIC_WALL_ACTIVE ||
12792           element == EL_BD_MAGIC_WALL_EMPTYING)
12793         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12794       else if (element == EL_DC_MAGIC_WALL_FULL ||
12795                element == EL_DC_MAGIC_WALL_ACTIVE ||
12796                element == EL_DC_MAGIC_WALL_EMPTYING)
12797         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12798       else
12799         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12800     }
12801
12802     if (game.magic_wall_time_left > 0)
12803     {
12804       game.magic_wall_time_left--;
12805
12806       if (!game.magic_wall_time_left)
12807       {
12808         SCAN_PLAYFIELD(x, y)
12809         {
12810           element = Tile[x][y];
12811
12812           if (element == EL_MAGIC_WALL_ACTIVE ||
12813               element == EL_MAGIC_WALL_FULL)
12814           {
12815             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12816             TEST_DrawLevelField(x, y);
12817           }
12818           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12819                    element == EL_BD_MAGIC_WALL_FULL)
12820           {
12821             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12822             TEST_DrawLevelField(x, y);
12823           }
12824           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12825                    element == EL_DC_MAGIC_WALL_FULL)
12826           {
12827             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12828             TEST_DrawLevelField(x, y);
12829           }
12830         }
12831
12832         game.magic_wall_active = FALSE;
12833       }
12834     }
12835   }
12836
12837   if (game.light_time_left > 0)
12838   {
12839     game.light_time_left--;
12840
12841     if (game.light_time_left == 0)
12842       RedrawAllLightSwitchesAndInvisibleElements();
12843   }
12844
12845   if (game.timegate_time_left > 0)
12846   {
12847     game.timegate_time_left--;
12848
12849     if (game.timegate_time_left == 0)
12850       CloseAllOpenTimegates();
12851   }
12852
12853   if (game.lenses_time_left > 0)
12854   {
12855     game.lenses_time_left--;
12856
12857     if (game.lenses_time_left == 0)
12858       RedrawAllInvisibleElementsForLenses();
12859   }
12860
12861   if (game.magnify_time_left > 0)
12862   {
12863     game.magnify_time_left--;
12864
12865     if (game.magnify_time_left == 0)
12866       RedrawAllInvisibleElementsForMagnifier();
12867   }
12868
12869   for (i = 0; i < MAX_PLAYERS; i++)
12870   {
12871     struct PlayerInfo *player = &stored_player[i];
12872
12873     if (SHIELD_ON(player))
12874     {
12875       if (player->shield_deadly_time_left)
12876         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12877       else if (player->shield_normal_time_left)
12878         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12879     }
12880   }
12881
12882 #if USE_DELAYED_GFX_REDRAW
12883   SCAN_PLAYFIELD(x, y)
12884   {
12885     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12886     {
12887       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12888          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12889
12890       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12891         DrawLevelField(x, y);
12892
12893       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12894         DrawLevelFieldCrumbled(x, y);
12895
12896       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12897         DrawLevelFieldCrumbledNeighbours(x, y);
12898
12899       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12900         DrawTwinkleOnField(x, y);
12901     }
12902
12903     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12904   }
12905 #endif
12906
12907   DrawAllPlayers();
12908   PlayAllPlayersSound();
12909
12910   for (i = 0; i < MAX_PLAYERS; i++)
12911   {
12912     struct PlayerInfo *player = &stored_player[i];
12913
12914     if (player->show_envelope != 0 && (!player->active ||
12915                                        player->MovPos == 0))
12916     {
12917       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12918
12919       player->show_envelope = 0;
12920     }
12921   }
12922
12923   // use random number generator in every frame to make it less predictable
12924   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12925     RND(1);
12926
12927   mouse_action_last = mouse_action;
12928 }
12929
12930 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12931 {
12932   int min_x = x, min_y = y, max_x = x, max_y = y;
12933   int scr_fieldx = getScreenFieldSizeX();
12934   int scr_fieldy = getScreenFieldSizeY();
12935   int i;
12936
12937   for (i = 0; i < MAX_PLAYERS; i++)
12938   {
12939     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12940
12941     if (!stored_player[i].active || &stored_player[i] == player)
12942       continue;
12943
12944     min_x = MIN(min_x, jx);
12945     min_y = MIN(min_y, jy);
12946     max_x = MAX(max_x, jx);
12947     max_y = MAX(max_y, jy);
12948   }
12949
12950   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12951 }
12952
12953 static boolean AllPlayersInVisibleScreen(void)
12954 {
12955   int i;
12956
12957   for (i = 0; i < MAX_PLAYERS; i++)
12958   {
12959     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12960
12961     if (!stored_player[i].active)
12962       continue;
12963
12964     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12965       return FALSE;
12966   }
12967
12968   return TRUE;
12969 }
12970
12971 void ScrollLevel(int dx, int dy)
12972 {
12973   int scroll_offset = 2 * TILEX_VAR;
12974   int x, y;
12975
12976   BlitBitmap(drawto_field, drawto_field,
12977              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12978              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12979              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12980              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12981              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12982              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12983
12984   if (dx != 0)
12985   {
12986     x = (dx == 1 ? BX1 : BX2);
12987     for (y = BY1; y <= BY2; y++)
12988       DrawScreenField(x, y);
12989   }
12990
12991   if (dy != 0)
12992   {
12993     y = (dy == 1 ? BY1 : BY2);
12994     for (x = BX1; x <= BX2; x++)
12995       DrawScreenField(x, y);
12996   }
12997
12998   redraw_mask |= REDRAW_FIELD;
12999 }
13000
13001 static boolean canFallDown(struct PlayerInfo *player)
13002 {
13003   int jx = player->jx, jy = player->jy;
13004
13005   return (IN_LEV_FIELD(jx, jy + 1) &&
13006           (IS_FREE(jx, jy + 1) ||
13007            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13008           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13009           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13010 }
13011
13012 static boolean canPassField(int x, int y, int move_dir)
13013 {
13014   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13015   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13016   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13017   int nextx = x + dx;
13018   int nexty = y + dy;
13019   int element = Tile[x][y];
13020
13021   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13022           !CAN_MOVE(element) &&
13023           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13024           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13025           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13026 }
13027
13028 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13029 {
13030   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13031   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13032   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13033   int newx = x + dx;
13034   int newy = y + dy;
13035
13036   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13037           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13038           (IS_DIGGABLE(Tile[newx][newy]) ||
13039            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13040            canPassField(newx, newy, move_dir)));
13041 }
13042
13043 static void CheckGravityMovement(struct PlayerInfo *player)
13044 {
13045   if (player->gravity && !player->programmed_action)
13046   {
13047     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13048     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13049     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13050     int jx = player->jx, jy = player->jy;
13051     boolean player_is_moving_to_valid_field =
13052       (!player_is_snapping &&
13053        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13054         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13055     boolean player_can_fall_down = canFallDown(player);
13056
13057     if (player_can_fall_down &&
13058         !player_is_moving_to_valid_field)
13059       player->programmed_action = MV_DOWN;
13060   }
13061 }
13062
13063 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13064 {
13065   return CheckGravityMovement(player);
13066
13067   if (player->gravity && !player->programmed_action)
13068   {
13069     int jx = player->jx, jy = player->jy;
13070     boolean field_under_player_is_free =
13071       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13072     boolean player_is_standing_on_valid_field =
13073       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13074        (IS_WALKABLE(Tile[jx][jy]) &&
13075         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13076
13077     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13078       player->programmed_action = MV_DOWN;
13079   }
13080 }
13081
13082 /*
13083   MovePlayerOneStep()
13084   -----------------------------------------------------------------------------
13085   dx, dy:               direction (non-diagonal) to try to move the player to
13086   real_dx, real_dy:     direction as read from input device (can be diagonal)
13087 */
13088
13089 boolean MovePlayerOneStep(struct PlayerInfo *player,
13090                           int dx, int dy, int real_dx, int real_dy)
13091 {
13092   int jx = player->jx, jy = player->jy;
13093   int new_jx = jx + dx, new_jy = jy + dy;
13094   int can_move;
13095   boolean player_can_move = !player->cannot_move;
13096
13097   if (!player->active || (!dx && !dy))
13098     return MP_NO_ACTION;
13099
13100   player->MovDir = (dx < 0 ? MV_LEFT :
13101                     dx > 0 ? MV_RIGHT :
13102                     dy < 0 ? MV_UP :
13103                     dy > 0 ? MV_DOWN :  MV_NONE);
13104
13105   if (!IN_LEV_FIELD(new_jx, new_jy))
13106     return MP_NO_ACTION;
13107
13108   if (!player_can_move)
13109   {
13110     if (player->MovPos == 0)
13111     {
13112       player->is_moving = FALSE;
13113       player->is_digging = FALSE;
13114       player->is_collecting = FALSE;
13115       player->is_snapping = FALSE;
13116       player->is_pushing = FALSE;
13117     }
13118   }
13119
13120   if (!network.enabled && game.centered_player_nr == -1 &&
13121       !AllPlayersInSight(player, new_jx, new_jy))
13122     return MP_NO_ACTION;
13123
13124   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13125   if (can_move != MP_MOVING)
13126     return can_move;
13127
13128   // check if DigField() has caused relocation of the player
13129   if (player->jx != jx || player->jy != jy)
13130     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13131
13132   StorePlayer[jx][jy] = 0;
13133   player->last_jx = jx;
13134   player->last_jy = jy;
13135   player->jx = new_jx;
13136   player->jy = new_jy;
13137   StorePlayer[new_jx][new_jy] = player->element_nr;
13138
13139   if (player->move_delay_value_next != -1)
13140   {
13141     player->move_delay_value = player->move_delay_value_next;
13142     player->move_delay_value_next = -1;
13143   }
13144
13145   player->MovPos =
13146     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13147
13148   player->step_counter++;
13149
13150   PlayerVisit[jx][jy] = FrameCounter;
13151
13152   player->is_moving = TRUE;
13153
13154 #if 1
13155   // should better be called in MovePlayer(), but this breaks some tapes
13156   ScrollPlayer(player, SCROLL_INIT);
13157 #endif
13158
13159   return MP_MOVING;
13160 }
13161
13162 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13163 {
13164   int jx = player->jx, jy = player->jy;
13165   int old_jx = jx, old_jy = jy;
13166   int moved = MP_NO_ACTION;
13167
13168   if (!player->active)
13169     return FALSE;
13170
13171   if (!dx && !dy)
13172   {
13173     if (player->MovPos == 0)
13174     {
13175       player->is_moving = FALSE;
13176       player->is_digging = FALSE;
13177       player->is_collecting = FALSE;
13178       player->is_snapping = FALSE;
13179       player->is_pushing = FALSE;
13180     }
13181
13182     return FALSE;
13183   }
13184
13185   if (player->move_delay > 0)
13186     return FALSE;
13187
13188   player->move_delay = -1;              // set to "uninitialized" value
13189
13190   // store if player is automatically moved to next field
13191   player->is_auto_moving = (player->programmed_action != MV_NONE);
13192
13193   // remove the last programmed player action
13194   player->programmed_action = 0;
13195
13196   if (player->MovPos)
13197   {
13198     // should only happen if pre-1.2 tape recordings are played
13199     // this is only for backward compatibility
13200
13201     int original_move_delay_value = player->move_delay_value;
13202
13203 #if DEBUG
13204     Debug("game:playing:MovePlayer",
13205           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13206           tape.counter);
13207 #endif
13208
13209     // scroll remaining steps with finest movement resolution
13210     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13211
13212     while (player->MovPos)
13213     {
13214       ScrollPlayer(player, SCROLL_GO_ON);
13215       ScrollScreen(NULL, SCROLL_GO_ON);
13216
13217       AdvanceFrameAndPlayerCounters(player->index_nr);
13218
13219       DrawAllPlayers();
13220       BackToFront_WithFrameDelay(0);
13221     }
13222
13223     player->move_delay_value = original_move_delay_value;
13224   }
13225
13226   player->is_active = FALSE;
13227
13228   if (player->last_move_dir & MV_HORIZONTAL)
13229   {
13230     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13231       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13232   }
13233   else
13234   {
13235     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13236       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13237   }
13238
13239   if (!moved && !player->is_active)
13240   {
13241     player->is_moving = FALSE;
13242     player->is_digging = FALSE;
13243     player->is_collecting = FALSE;
13244     player->is_snapping = FALSE;
13245     player->is_pushing = FALSE;
13246   }
13247
13248   jx = player->jx;
13249   jy = player->jy;
13250
13251   if (moved & MP_MOVING && !ScreenMovPos &&
13252       (player->index_nr == game.centered_player_nr ||
13253        game.centered_player_nr == -1))
13254   {
13255     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13256
13257     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13258     {
13259       // actual player has left the screen -- scroll in that direction
13260       if (jx != old_jx)         // player has moved horizontally
13261         scroll_x += (jx - old_jx);
13262       else                      // player has moved vertically
13263         scroll_y += (jy - old_jy);
13264     }
13265     else
13266     {
13267       int offset_raw = game.scroll_delay_value;
13268
13269       if (jx != old_jx)         // player has moved horizontally
13270       {
13271         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13272         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13273         int new_scroll_x = jx - MIDPOSX + offset_x;
13274
13275         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13276             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13277           scroll_x = new_scroll_x;
13278
13279         // don't scroll over playfield boundaries
13280         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13281
13282         // don't scroll more than one field at a time
13283         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13284
13285         // don't scroll against the player's moving direction
13286         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13287             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13288           scroll_x = old_scroll_x;
13289       }
13290       else                      // player has moved vertically
13291       {
13292         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13293         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13294         int new_scroll_y = jy - MIDPOSY + offset_y;
13295
13296         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13297             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13298           scroll_y = new_scroll_y;
13299
13300         // don't scroll over playfield boundaries
13301         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13302
13303         // don't scroll more than one field at a time
13304         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13305
13306         // don't scroll against the player's moving direction
13307         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13308             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13309           scroll_y = old_scroll_y;
13310       }
13311     }
13312
13313     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13314     {
13315       if (!network.enabled && game.centered_player_nr == -1 &&
13316           !AllPlayersInVisibleScreen())
13317       {
13318         scroll_x = old_scroll_x;
13319         scroll_y = old_scroll_y;
13320       }
13321       else
13322       {
13323         ScrollScreen(player, SCROLL_INIT);
13324         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13325       }
13326     }
13327   }
13328
13329   player->StepFrame = 0;
13330
13331   if (moved & MP_MOVING)
13332   {
13333     if (old_jx != jx && old_jy == jy)
13334       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13335     else if (old_jx == jx && old_jy != jy)
13336       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13337
13338     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13339
13340     player->last_move_dir = player->MovDir;
13341     player->is_moving = TRUE;
13342     player->is_snapping = FALSE;
13343     player->is_switching = FALSE;
13344     player->is_dropping = FALSE;
13345     player->is_dropping_pressed = FALSE;
13346     player->drop_pressed_delay = 0;
13347
13348 #if 0
13349     // should better be called here than above, but this breaks some tapes
13350     ScrollPlayer(player, SCROLL_INIT);
13351 #endif
13352   }
13353   else
13354   {
13355     CheckGravityMovementWhenNotMoving(player);
13356
13357     player->is_moving = FALSE;
13358
13359     /* at this point, the player is allowed to move, but cannot move right now
13360        (e.g. because of something blocking the way) -- ensure that the player
13361        is also allowed to move in the next frame (in old versions before 3.1.1,
13362        the player was forced to wait again for eight frames before next try) */
13363
13364     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13365       player->move_delay = 0;   // allow direct movement in the next frame
13366   }
13367
13368   if (player->move_delay == -1)         // not yet initialized by DigField()
13369     player->move_delay = player->move_delay_value;
13370
13371   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13372   {
13373     TestIfPlayerTouchesBadThing(jx, jy);
13374     TestIfPlayerTouchesCustomElement(jx, jy);
13375   }
13376
13377   if (!player->active)
13378     RemovePlayer(player);
13379
13380   return moved;
13381 }
13382
13383 void ScrollPlayer(struct PlayerInfo *player, int mode)
13384 {
13385   int jx = player->jx, jy = player->jy;
13386   int last_jx = player->last_jx, last_jy = player->last_jy;
13387   int move_stepsize = TILEX / player->move_delay_value;
13388
13389   if (!player->active)
13390     return;
13391
13392   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13393     return;
13394
13395   if (mode == SCROLL_INIT)
13396   {
13397     player->actual_frame_counter.count = FrameCounter;
13398     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13399
13400     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13401         Tile[last_jx][last_jy] == EL_EMPTY)
13402     {
13403       int last_field_block_delay = 0;   // start with no blocking at all
13404       int block_delay_adjustment = player->block_delay_adjustment;
13405
13406       // if player blocks last field, add delay for exactly one move
13407       if (player->block_last_field)
13408       {
13409         last_field_block_delay += player->move_delay_value;
13410
13411         // when blocking enabled, prevent moving up despite gravity
13412         if (player->gravity && player->MovDir == MV_UP)
13413           block_delay_adjustment = -1;
13414       }
13415
13416       // add block delay adjustment (also possible when not blocking)
13417       last_field_block_delay += block_delay_adjustment;
13418
13419       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13420       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13421     }
13422
13423     if (player->MovPos != 0)    // player has not yet reached destination
13424       return;
13425   }
13426   else if (!FrameReached(&player->actual_frame_counter))
13427     return;
13428
13429   if (player->MovPos != 0)
13430   {
13431     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13432     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13433
13434     // before DrawPlayer() to draw correct player graphic for this case
13435     if (player->MovPos == 0)
13436       CheckGravityMovement(player);
13437   }
13438
13439   if (player->MovPos == 0)      // player reached destination field
13440   {
13441     if (player->move_delay_reset_counter > 0)
13442     {
13443       player->move_delay_reset_counter--;
13444
13445       if (player->move_delay_reset_counter == 0)
13446       {
13447         // continue with normal speed after quickly moving through gate
13448         HALVE_PLAYER_SPEED(player);
13449
13450         // be able to make the next move without delay
13451         player->move_delay = 0;
13452       }
13453     }
13454
13455     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13456         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13457         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13458         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13459         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13460         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13461         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13462         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13463     {
13464       ExitPlayer(player);
13465
13466       if (game.players_still_needed == 0 &&
13467           (game.friends_still_needed == 0 ||
13468            IS_SP_ELEMENT(Tile[jx][jy])))
13469         LevelSolved();
13470     }
13471
13472     player->last_jx = jx;
13473     player->last_jy = jy;
13474
13475     // this breaks one level: "machine", level 000
13476     {
13477       int move_direction = player->MovDir;
13478       int enter_side = MV_DIR_OPPOSITE(move_direction);
13479       int leave_side = move_direction;
13480       int old_jx = last_jx;
13481       int old_jy = last_jy;
13482       int old_element = Tile[old_jx][old_jy];
13483       int new_element = Tile[jx][jy];
13484
13485       if (IS_CUSTOM_ELEMENT(old_element))
13486         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13487                                    CE_LEFT_BY_PLAYER,
13488                                    player->index_bit, leave_side);
13489
13490       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13491                                           CE_PLAYER_LEAVES_X,
13492                                           player->index_bit, leave_side);
13493
13494       // needed because pushed element has not yet reached its destination,
13495       // so it would trigger a change event at its previous field location
13496       if (!player->is_pushing)
13497       {
13498         if (IS_CUSTOM_ELEMENT(new_element))
13499           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13500                                      player->index_bit, enter_side);
13501
13502         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13503                                             CE_PLAYER_ENTERS_X,
13504                                             player->index_bit, enter_side);
13505       }
13506
13507       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13508                                         CE_MOVE_OF_X, move_direction);
13509     }
13510
13511     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13512     {
13513       TestIfPlayerTouchesBadThing(jx, jy);
13514       TestIfPlayerTouchesCustomElement(jx, jy);
13515
13516       // needed because pushed element has not yet reached its destination,
13517       // so it would trigger a change event at its previous field location
13518       if (!player->is_pushing)
13519         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13520
13521       if (level.finish_dig_collect &&
13522           (player->is_digging || player->is_collecting))
13523       {
13524         int last_element = player->last_removed_element;
13525         int move_direction = player->MovDir;
13526         int enter_side = MV_DIR_OPPOSITE(move_direction);
13527         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13528                             CE_PLAYER_COLLECTS_X);
13529
13530         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13531                                             player->index_bit, enter_side);
13532
13533         player->last_removed_element = EL_UNDEFINED;
13534       }
13535
13536       if (!player->active)
13537         RemovePlayer(player);
13538     }
13539
13540     if (level.use_step_counter)
13541       CheckLevelTime_StepCounter();
13542
13543     if (tape.single_step && tape.recording && !tape.pausing &&
13544         !player->programmed_action)
13545       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13546
13547     if (!player->programmed_action)
13548       CheckSaveEngineSnapshot(player);
13549   }
13550 }
13551
13552 void ScrollScreen(struct PlayerInfo *player, int mode)
13553 {
13554   static DelayCounter screen_frame_counter = { 0 };
13555
13556   if (mode == SCROLL_INIT)
13557   {
13558     // set scrolling step size according to actual player's moving speed
13559     ScrollStepSize = TILEX / player->move_delay_value;
13560
13561     screen_frame_counter.count = FrameCounter;
13562     screen_frame_counter.value = 1;
13563
13564     ScreenMovDir = player->MovDir;
13565     ScreenMovPos = player->MovPos;
13566     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13567     return;
13568   }
13569   else if (!FrameReached(&screen_frame_counter))
13570     return;
13571
13572   if (ScreenMovPos)
13573   {
13574     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13575     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13576     redraw_mask |= REDRAW_FIELD;
13577   }
13578   else
13579     ScreenMovDir = MV_NONE;
13580 }
13581
13582 void CheckNextToConditions(int x, int y)
13583 {
13584   int element = Tile[x][y];
13585
13586   if (IS_PLAYER(x, y))
13587     TestIfPlayerNextToCustomElement(x, y);
13588
13589   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13590       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13591     TestIfElementNextToCustomElement(x, y);
13592 }
13593
13594 void TestIfPlayerNextToCustomElement(int x, int y)
13595 {
13596   struct XY *xy = xy_topdown;
13597   static int trigger_sides[4][2] =
13598   {
13599     // center side       border side
13600     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13601     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13602     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13603     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13604   };
13605   int i;
13606
13607   if (!IS_PLAYER(x, y))
13608     return;
13609
13610   struct PlayerInfo *player = PLAYERINFO(x, y);
13611
13612   if (player->is_moving)
13613     return;
13614
13615   for (i = 0; i < NUM_DIRECTIONS; i++)
13616   {
13617     int xx = x + xy[i].x;
13618     int yy = y + xy[i].y;
13619     int border_side = trigger_sides[i][1];
13620     int border_element;
13621
13622     if (!IN_LEV_FIELD(xx, yy))
13623       continue;
13624
13625     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13626       continue;         // center and border element not connected
13627
13628     border_element = Tile[xx][yy];
13629
13630     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13631                                player->index_bit, border_side);
13632     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13633                                         CE_PLAYER_NEXT_TO_X,
13634                                         player->index_bit, border_side);
13635
13636     /* use player element that is initially defined in the level playfield,
13637        not the player element that corresponds to the runtime player number
13638        (example: a level that contains EL_PLAYER_3 as the only player would
13639        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13640
13641     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13642                              CE_NEXT_TO_X, border_side);
13643   }
13644 }
13645
13646 void TestIfPlayerTouchesCustomElement(int x, int y)
13647 {
13648   struct XY *xy = xy_topdown;
13649   static int trigger_sides[4][2] =
13650   {
13651     // center side       border side
13652     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13653     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13654     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13655     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13656   };
13657   static int touch_dir[4] =
13658   {
13659     MV_LEFT | MV_RIGHT,
13660     MV_UP   | MV_DOWN,
13661     MV_UP   | MV_DOWN,
13662     MV_LEFT | MV_RIGHT
13663   };
13664   int center_element = Tile[x][y];      // should always be non-moving!
13665   int i;
13666
13667   for (i = 0; i < NUM_DIRECTIONS; i++)
13668   {
13669     int xx = x + xy[i].x;
13670     int yy = y + xy[i].y;
13671     int center_side = trigger_sides[i][0];
13672     int border_side = trigger_sides[i][1];
13673     int border_element;
13674
13675     if (!IN_LEV_FIELD(xx, yy))
13676       continue;
13677
13678     if (IS_PLAYER(x, y))                // player found at center element
13679     {
13680       struct PlayerInfo *player = PLAYERINFO(x, y);
13681
13682       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13683         border_element = Tile[xx][yy];          // may be moving!
13684       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13685         border_element = Tile[xx][yy];
13686       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13687         border_element = MovingOrBlocked2Element(xx, yy);
13688       else
13689         continue;               // center and border element do not touch
13690
13691       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13692                                  player->index_bit, border_side);
13693       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13694                                           CE_PLAYER_TOUCHES_X,
13695                                           player->index_bit, border_side);
13696
13697       {
13698         /* use player element that is initially defined in the level playfield,
13699            not the player element that corresponds to the runtime player number
13700            (example: a level that contains EL_PLAYER_3 as the only player would
13701            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13702         int player_element = PLAYERINFO(x, y)->initial_element;
13703
13704         // as element "X" is the player here, check opposite (center) side
13705         CheckElementChangeBySide(xx, yy, border_element, player_element,
13706                                  CE_TOUCHING_X, center_side);
13707       }
13708     }
13709     else if (IS_PLAYER(xx, yy))         // player found at border element
13710     {
13711       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13712
13713       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13714       {
13715         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13716           continue;             // center and border element do not touch
13717       }
13718
13719       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13720                                  player->index_bit, center_side);
13721       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13722                                           CE_PLAYER_TOUCHES_X,
13723                                           player->index_bit, center_side);
13724
13725       {
13726         /* use player element that is initially defined in the level playfield,
13727            not the player element that corresponds to the runtime player number
13728            (example: a level that contains EL_PLAYER_3 as the only player would
13729            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13730         int player_element = PLAYERINFO(xx, yy)->initial_element;
13731
13732         // as element "X" is the player here, check opposite (border) side
13733         CheckElementChangeBySide(x, y, center_element, player_element,
13734                                  CE_TOUCHING_X, border_side);
13735       }
13736
13737       break;
13738     }
13739   }
13740 }
13741
13742 void TestIfElementNextToCustomElement(int x, int y)
13743 {
13744   struct XY *xy = xy_topdown;
13745   static int trigger_sides[4][2] =
13746   {
13747     // center side      border side
13748     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13749     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13750     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13751     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13752   };
13753   int center_element = Tile[x][y];      // should always be non-moving!
13754   int i;
13755
13756   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13757     return;
13758
13759   for (i = 0; i < NUM_DIRECTIONS; i++)
13760   {
13761     int xx = x + xy[i].x;
13762     int yy = y + xy[i].y;
13763     int border_side = trigger_sides[i][1];
13764     int border_element;
13765
13766     if (!IN_LEV_FIELD(xx, yy))
13767       continue;
13768
13769     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13770       continue;                 // center and border element not connected
13771
13772     border_element = Tile[xx][yy];
13773
13774     // check for change of center element (but change it only once)
13775     if (CheckElementChangeBySide(x, y, center_element, border_element,
13776                                  CE_NEXT_TO_X, border_side))
13777       break;
13778   }
13779 }
13780
13781 void TestIfElementTouchesCustomElement(int x, int y)
13782 {
13783   struct XY *xy = xy_topdown;
13784   static int trigger_sides[4][2] =
13785   {
13786     // center side      border side
13787     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13788     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13789     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13790     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13791   };
13792   static int touch_dir[4] =
13793   {
13794     MV_LEFT | MV_RIGHT,
13795     MV_UP   | MV_DOWN,
13796     MV_UP   | MV_DOWN,
13797     MV_LEFT | MV_RIGHT
13798   };
13799   boolean change_center_element = FALSE;
13800   int center_element = Tile[x][y];      // should always be non-moving!
13801   int border_element_old[NUM_DIRECTIONS];
13802   int i;
13803
13804   for (i = 0; i < NUM_DIRECTIONS; i++)
13805   {
13806     int xx = x + xy[i].x;
13807     int yy = y + xy[i].y;
13808     int border_element;
13809
13810     border_element_old[i] = -1;
13811
13812     if (!IN_LEV_FIELD(xx, yy))
13813       continue;
13814
13815     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13816       border_element = Tile[xx][yy];    // may be moving!
13817     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13818       border_element = Tile[xx][yy];
13819     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13820       border_element = MovingOrBlocked2Element(xx, yy);
13821     else
13822       continue;                 // center and border element do not touch
13823
13824     border_element_old[i] = border_element;
13825   }
13826
13827   for (i = 0; i < NUM_DIRECTIONS; i++)
13828   {
13829     int xx = x + xy[i].x;
13830     int yy = y + xy[i].y;
13831     int center_side = trigger_sides[i][0];
13832     int border_element = border_element_old[i];
13833
13834     if (border_element == -1)
13835       continue;
13836
13837     // check for change of border element
13838     CheckElementChangeBySide(xx, yy, border_element, center_element,
13839                              CE_TOUCHING_X, center_side);
13840
13841     // (center element cannot be player, so we don't have to check this here)
13842   }
13843
13844   for (i = 0; i < NUM_DIRECTIONS; i++)
13845   {
13846     int xx = x + xy[i].x;
13847     int yy = y + xy[i].y;
13848     int border_side = trigger_sides[i][1];
13849     int border_element = border_element_old[i];
13850
13851     if (border_element == -1)
13852       continue;
13853
13854     // check for change of center element (but change it only once)
13855     if (!change_center_element)
13856       change_center_element =
13857         CheckElementChangeBySide(x, y, center_element, border_element,
13858                                  CE_TOUCHING_X, border_side);
13859
13860     if (IS_PLAYER(xx, yy))
13861     {
13862       /* use player element that is initially defined in the level playfield,
13863          not the player element that corresponds to the runtime player number
13864          (example: a level that contains EL_PLAYER_3 as the only player would
13865          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13866       int player_element = PLAYERINFO(xx, yy)->initial_element;
13867
13868       // as element "X" is the player here, check opposite (border) side
13869       CheckElementChangeBySide(x, y, center_element, player_element,
13870                                CE_TOUCHING_X, border_side);
13871     }
13872   }
13873 }
13874
13875 void TestIfElementHitsCustomElement(int x, int y, int direction)
13876 {
13877   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13878   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13879   int hitx = x + dx, hity = y + dy;
13880   int hitting_element = Tile[x][y];
13881   int touched_element;
13882
13883   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13884     return;
13885
13886   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13887                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13888
13889   if (IN_LEV_FIELD(hitx, hity))
13890   {
13891     int opposite_direction = MV_DIR_OPPOSITE(direction);
13892     int hitting_side = direction;
13893     int touched_side = opposite_direction;
13894     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13895                           MovDir[hitx][hity] != direction ||
13896                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13897
13898     object_hit = TRUE;
13899
13900     if (object_hit)
13901     {
13902       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13903                                CE_HITTING_X, touched_side);
13904
13905       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13906                                CE_HIT_BY_X, hitting_side);
13907
13908       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13909                                CE_HIT_BY_SOMETHING, opposite_direction);
13910
13911       if (IS_PLAYER(hitx, hity))
13912       {
13913         /* use player element that is initially defined in the level playfield,
13914            not the player element that corresponds to the runtime player number
13915            (example: a level that contains EL_PLAYER_3 as the only player would
13916            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13917         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13918
13919         CheckElementChangeBySide(x, y, hitting_element, player_element,
13920                                  CE_HITTING_X, touched_side);
13921       }
13922     }
13923   }
13924
13925   // "hitting something" is also true when hitting the playfield border
13926   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13927                            CE_HITTING_SOMETHING, direction);
13928 }
13929
13930 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13931 {
13932   int i, kill_x = -1, kill_y = -1;
13933
13934   int bad_element = -1;
13935   struct XY *test_xy = xy_topdown;
13936   static int test_dir[4] =
13937   {
13938     MV_UP,
13939     MV_LEFT,
13940     MV_RIGHT,
13941     MV_DOWN
13942   };
13943
13944   for (i = 0; i < NUM_DIRECTIONS; i++)
13945   {
13946     int test_x, test_y, test_move_dir, test_element;
13947
13948     test_x = good_x + test_xy[i].x;
13949     test_y = good_y + test_xy[i].y;
13950
13951     if (!IN_LEV_FIELD(test_x, test_y))
13952       continue;
13953
13954     test_move_dir =
13955       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13956
13957     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13958
13959     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13960        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13961     */
13962     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13963         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13964     {
13965       kill_x = test_x;
13966       kill_y = test_y;
13967       bad_element = test_element;
13968
13969       break;
13970     }
13971   }
13972
13973   if (kill_x != -1 || kill_y != -1)
13974   {
13975     if (IS_PLAYER(good_x, good_y))
13976     {
13977       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13978
13979       if (player->shield_deadly_time_left > 0 &&
13980           !IS_INDESTRUCTIBLE(bad_element))
13981         Bang(kill_x, kill_y);
13982       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13983         KillPlayer(player);
13984     }
13985     else
13986       Bang(good_x, good_y);
13987   }
13988 }
13989
13990 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13991 {
13992   int i, kill_x = -1, kill_y = -1;
13993   int bad_element = Tile[bad_x][bad_y];
13994   struct XY *test_xy = xy_topdown;
13995   static int touch_dir[4] =
13996   {
13997     MV_LEFT | MV_RIGHT,
13998     MV_UP   | MV_DOWN,
13999     MV_UP   | MV_DOWN,
14000     MV_LEFT | MV_RIGHT
14001   };
14002   static int test_dir[4] =
14003   {
14004     MV_UP,
14005     MV_LEFT,
14006     MV_RIGHT,
14007     MV_DOWN
14008   };
14009
14010   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14011     return;
14012
14013   for (i = 0; i < NUM_DIRECTIONS; i++)
14014   {
14015     int test_x, test_y, test_move_dir, test_element;
14016
14017     test_x = bad_x + test_xy[i].x;
14018     test_y = bad_y + test_xy[i].y;
14019
14020     if (!IN_LEV_FIELD(test_x, test_y))
14021       continue;
14022
14023     test_move_dir =
14024       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14025
14026     test_element = Tile[test_x][test_y];
14027
14028     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14029        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14030     */
14031     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14032         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14033     {
14034       // good thing is player or penguin that does not move away
14035       if (IS_PLAYER(test_x, test_y))
14036       {
14037         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14038
14039         if (bad_element == EL_ROBOT && player->is_moving)
14040           continue;     // robot does not kill player if he is moving
14041
14042         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14043         {
14044           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14045             continue;           // center and border element do not touch
14046         }
14047
14048         kill_x = test_x;
14049         kill_y = test_y;
14050
14051         break;
14052       }
14053       else if (test_element == EL_PENGUIN)
14054       {
14055         kill_x = test_x;
14056         kill_y = test_y;
14057
14058         break;
14059       }
14060     }
14061   }
14062
14063   if (kill_x != -1 || kill_y != -1)
14064   {
14065     if (IS_PLAYER(kill_x, kill_y))
14066     {
14067       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14068
14069       if (player->shield_deadly_time_left > 0 &&
14070           !IS_INDESTRUCTIBLE(bad_element))
14071         Bang(bad_x, bad_y);
14072       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14073         KillPlayer(player);
14074     }
14075     else
14076       Bang(kill_x, kill_y);
14077   }
14078 }
14079
14080 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14081 {
14082   int bad_element = Tile[bad_x][bad_y];
14083   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14084   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14085   int test_x = bad_x + dx, test_y = bad_y + dy;
14086   int test_move_dir, test_element;
14087   int kill_x = -1, kill_y = -1;
14088
14089   if (!IN_LEV_FIELD(test_x, test_y))
14090     return;
14091
14092   test_move_dir =
14093     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14094
14095   test_element = Tile[test_x][test_y];
14096
14097   if (test_move_dir != bad_move_dir)
14098   {
14099     // good thing can be player or penguin that does not move away
14100     if (IS_PLAYER(test_x, test_y))
14101     {
14102       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14103
14104       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14105          player as being hit when he is moving towards the bad thing, because
14106          the "get hit by" condition would be lost after the player stops) */
14107       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14108         return;         // player moves away from bad thing
14109
14110       kill_x = test_x;
14111       kill_y = test_y;
14112     }
14113     else if (test_element == EL_PENGUIN)
14114     {
14115       kill_x = test_x;
14116       kill_y = test_y;
14117     }
14118   }
14119
14120   if (kill_x != -1 || kill_y != -1)
14121   {
14122     if (IS_PLAYER(kill_x, kill_y))
14123     {
14124       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14125
14126       if (player->shield_deadly_time_left > 0 &&
14127           !IS_INDESTRUCTIBLE(bad_element))
14128         Bang(bad_x, bad_y);
14129       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14130         KillPlayer(player);
14131     }
14132     else
14133       Bang(kill_x, kill_y);
14134   }
14135 }
14136
14137 void TestIfPlayerTouchesBadThing(int x, int y)
14138 {
14139   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14140 }
14141
14142 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14143 {
14144   TestIfGoodThingHitsBadThing(x, y, move_dir);
14145 }
14146
14147 void TestIfBadThingTouchesPlayer(int x, int y)
14148 {
14149   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14150 }
14151
14152 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14153 {
14154   TestIfBadThingHitsGoodThing(x, y, move_dir);
14155 }
14156
14157 void TestIfFriendTouchesBadThing(int x, int y)
14158 {
14159   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14160 }
14161
14162 void TestIfBadThingTouchesFriend(int x, int y)
14163 {
14164   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14165 }
14166
14167 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14168 {
14169   int i, kill_x = bad_x, kill_y = bad_y;
14170   struct XY *xy = xy_topdown;
14171
14172   for (i = 0; i < NUM_DIRECTIONS; i++)
14173   {
14174     int x, y, element;
14175
14176     x = bad_x + xy[i].x;
14177     y = bad_y + xy[i].y;
14178     if (!IN_LEV_FIELD(x, y))
14179       continue;
14180
14181     element = Tile[x][y];
14182     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14183         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14184     {
14185       kill_x = x;
14186       kill_y = y;
14187       break;
14188     }
14189   }
14190
14191   if (kill_x != bad_x || kill_y != bad_y)
14192     Bang(bad_x, bad_y);
14193 }
14194
14195 void KillPlayer(struct PlayerInfo *player)
14196 {
14197   int jx = player->jx, jy = player->jy;
14198
14199   if (!player->active)
14200     return;
14201
14202 #if 0
14203   Debug("game:playing:KillPlayer",
14204         "0: killed == %d, active == %d, reanimated == %d",
14205         player->killed, player->active, player->reanimated);
14206 #endif
14207
14208   /* the following code was introduced to prevent an infinite loop when calling
14209      -> Bang()
14210      -> CheckTriggeredElementChangeExt()
14211      -> ExecuteCustomElementAction()
14212      -> KillPlayer()
14213      -> (infinitely repeating the above sequence of function calls)
14214      which occurs when killing the player while having a CE with the setting
14215      "kill player X when explosion of <player X>"; the solution using a new
14216      field "player->killed" was chosen for backwards compatibility, although
14217      clever use of the fields "player->active" etc. would probably also work */
14218 #if 1
14219   if (player->killed)
14220     return;
14221 #endif
14222
14223   player->killed = TRUE;
14224
14225   // remove accessible field at the player's position
14226   RemoveField(jx, jy);
14227
14228   // deactivate shield (else Bang()/Explode() would not work right)
14229   player->shield_normal_time_left = 0;
14230   player->shield_deadly_time_left = 0;
14231
14232 #if 0
14233   Debug("game:playing:KillPlayer",
14234         "1: killed == %d, active == %d, reanimated == %d",
14235         player->killed, player->active, player->reanimated);
14236 #endif
14237
14238   Bang(jx, jy);
14239
14240 #if 0
14241   Debug("game:playing:KillPlayer",
14242         "2: killed == %d, active == %d, reanimated == %d",
14243         player->killed, player->active, player->reanimated);
14244 #endif
14245
14246   if (player->reanimated)       // killed player may have been reanimated
14247     player->killed = player->reanimated = FALSE;
14248   else
14249     BuryPlayer(player);
14250 }
14251
14252 static void KillPlayerUnlessEnemyProtected(int x, int y)
14253 {
14254   if (!PLAYER_ENEMY_PROTECTED(x, y))
14255     KillPlayer(PLAYERINFO(x, y));
14256 }
14257
14258 static void KillPlayerUnlessExplosionProtected(int x, int y)
14259 {
14260   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14261     KillPlayer(PLAYERINFO(x, y));
14262 }
14263
14264 void BuryPlayer(struct PlayerInfo *player)
14265 {
14266   int jx = player->jx, jy = player->jy;
14267
14268   if (!player->active)
14269     return;
14270
14271   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14272
14273   RemovePlayer(player);
14274
14275   player->buried = TRUE;
14276
14277   if (game.all_players_gone)
14278     game.GameOver = TRUE;
14279 }
14280
14281 void RemovePlayer(struct PlayerInfo *player)
14282 {
14283   int jx = player->jx, jy = player->jy;
14284   int i, found = FALSE;
14285
14286   player->present = FALSE;
14287   player->active = FALSE;
14288
14289   // required for some CE actions (even if the player is not active anymore)
14290   player->MovPos = 0;
14291
14292   if (!ExplodeField[jx][jy])
14293     StorePlayer[jx][jy] = 0;
14294
14295   if (player->is_moving)
14296     TEST_DrawLevelField(player->last_jx, player->last_jy);
14297
14298   for (i = 0; i < MAX_PLAYERS; i++)
14299     if (stored_player[i].active)
14300       found = TRUE;
14301
14302   if (!found)
14303   {
14304     game.all_players_gone = TRUE;
14305     game.GameOver = TRUE;
14306   }
14307
14308   game.exit_x = game.robot_wheel_x = jx;
14309   game.exit_y = game.robot_wheel_y = jy;
14310 }
14311
14312 void ExitPlayer(struct PlayerInfo *player)
14313 {
14314   DrawPlayer(player);   // needed here only to cleanup last field
14315   RemovePlayer(player);
14316
14317   if (game.players_still_needed > 0)
14318     game.players_still_needed--;
14319 }
14320
14321 static void SetFieldForSnapping(int x, int y, int element, int direction,
14322                                 int player_index_bit)
14323 {
14324   struct ElementInfo *ei = &element_info[element];
14325   int direction_bit = MV_DIR_TO_BIT(direction);
14326   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14327   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14328                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14329
14330   Tile[x][y] = EL_ELEMENT_SNAPPING;
14331   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14332   MovDir[x][y] = direction;
14333   Store[x][y] = element;
14334   Store2[x][y] = player_index_bit;
14335
14336   ResetGfxAnimation(x, y);
14337
14338   GfxElement[x][y] = element;
14339   GfxAction[x][y] = action;
14340   GfxDir[x][y] = direction;
14341   GfxFrame[x][y] = -1;
14342 }
14343
14344 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14345                                    int player_index_bit)
14346 {
14347   TestIfElementTouchesCustomElement(x, y);      // for empty space
14348
14349   if (level.finish_dig_collect)
14350   {
14351     int dig_side = MV_DIR_OPPOSITE(direction);
14352     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14353                         CE_PLAYER_COLLECTS_X);
14354
14355     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14356                                         player_index_bit, dig_side);
14357     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14358                                         player_index_bit, dig_side);
14359   }
14360 }
14361
14362 /*
14363   =============================================================================
14364   checkDiagonalPushing()
14365   -----------------------------------------------------------------------------
14366   check if diagonal input device direction results in pushing of object
14367   (by checking if the alternative direction is walkable, diggable, ...)
14368   =============================================================================
14369 */
14370
14371 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14372                                     int x, int y, int real_dx, int real_dy)
14373 {
14374   int jx, jy, dx, dy, xx, yy;
14375
14376   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14377     return TRUE;
14378
14379   // diagonal direction: check alternative direction
14380   jx = player->jx;
14381   jy = player->jy;
14382   dx = x - jx;
14383   dy = y - jy;
14384   xx = jx + (dx == 0 ? real_dx : 0);
14385   yy = jy + (dy == 0 ? real_dy : 0);
14386
14387   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14388 }
14389
14390 /*
14391   =============================================================================
14392   DigField()
14393   -----------------------------------------------------------------------------
14394   x, y:                 field next to player (non-diagonal) to try to dig to
14395   real_dx, real_dy:     direction as read from input device (can be diagonal)
14396   =============================================================================
14397 */
14398
14399 static int DigField(struct PlayerInfo *player,
14400                     int oldx, int oldy, int x, int y,
14401                     int real_dx, int real_dy, int mode)
14402 {
14403   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14404   boolean player_was_pushing = player->is_pushing;
14405   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14406   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14407   int jx = oldx, jy = oldy;
14408   int dx = x - jx, dy = y - jy;
14409   int nextx = x + dx, nexty = y + dy;
14410   int move_direction = (dx == -1 ? MV_LEFT  :
14411                         dx == +1 ? MV_RIGHT :
14412                         dy == -1 ? MV_UP    :
14413                         dy == +1 ? MV_DOWN  : MV_NONE);
14414   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14415   int dig_side = MV_DIR_OPPOSITE(move_direction);
14416   int old_element = Tile[jx][jy];
14417   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14418   int collect_count;
14419
14420   if (is_player)                // function can also be called by EL_PENGUIN
14421   {
14422     if (player->MovPos == 0)
14423     {
14424       player->is_digging = FALSE;
14425       player->is_collecting = FALSE;
14426     }
14427
14428     if (player->MovPos == 0)    // last pushing move finished
14429       player->is_pushing = FALSE;
14430
14431     if (mode == DF_NO_PUSH)     // player just stopped pushing
14432     {
14433       player->is_switching = FALSE;
14434       player->push_delay = -1;
14435
14436       return MP_NO_ACTION;
14437     }
14438   }
14439   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14440     old_element = Back[jx][jy];
14441
14442   // in case of element dropped at player position, check background
14443   else if (Back[jx][jy] != EL_EMPTY &&
14444            game.engine_version >= VERSION_IDENT(2,2,0,0))
14445     old_element = Back[jx][jy];
14446
14447   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14448     return MP_NO_ACTION;        // field has no opening in this direction
14449
14450   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14451     return MP_NO_ACTION;        // field has no opening in this direction
14452
14453   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14454   {
14455     SplashAcid(x, y);
14456
14457     Tile[jx][jy] = player->artwork_element;
14458     InitMovingField(jx, jy, MV_DOWN);
14459     Store[jx][jy] = EL_ACID;
14460     ContinueMoving(jx, jy);
14461     BuryPlayer(player);
14462
14463     return MP_DONT_RUN_INTO;
14464   }
14465
14466   if (player_can_move && DONT_RUN_INTO(element))
14467   {
14468     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14469
14470     return MP_DONT_RUN_INTO;
14471   }
14472
14473   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14474     return MP_NO_ACTION;
14475
14476   collect_count = element_info[element].collect_count_initial;
14477
14478   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14479     return MP_NO_ACTION;
14480
14481   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14482     player_can_move = player_can_move_or_snap;
14483
14484   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14485       game.engine_version >= VERSION_IDENT(2,2,0,0))
14486   {
14487     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14488                                player->index_bit, dig_side);
14489     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14490                                         player->index_bit, dig_side);
14491
14492     if (element == EL_DC_LANDMINE)
14493       Bang(x, y);
14494
14495     if (Tile[x][y] != element)          // field changed by snapping
14496       return MP_ACTION;
14497
14498     return MP_NO_ACTION;
14499   }
14500
14501   if (player->gravity && is_player && !player->is_auto_moving &&
14502       canFallDown(player) && move_direction != MV_DOWN &&
14503       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14504     return MP_NO_ACTION;        // player cannot walk here due to gravity
14505
14506   if (player_can_move &&
14507       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14508   {
14509     int sound_element = SND_ELEMENT(element);
14510     int sound_action = ACTION_WALKING;
14511
14512     if (IS_RND_GATE(element))
14513     {
14514       if (!player->key[RND_GATE_NR(element)])
14515         return MP_NO_ACTION;
14516     }
14517     else if (IS_RND_GATE_GRAY(element))
14518     {
14519       if (!player->key[RND_GATE_GRAY_NR(element)])
14520         return MP_NO_ACTION;
14521     }
14522     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14523     {
14524       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14525         return MP_NO_ACTION;
14526     }
14527     else if (element == EL_EXIT_OPEN ||
14528              element == EL_EM_EXIT_OPEN ||
14529              element == EL_EM_EXIT_OPENING ||
14530              element == EL_STEEL_EXIT_OPEN ||
14531              element == EL_EM_STEEL_EXIT_OPEN ||
14532              element == EL_EM_STEEL_EXIT_OPENING ||
14533              element == EL_SP_EXIT_OPEN ||
14534              element == EL_SP_EXIT_OPENING)
14535     {
14536       sound_action = ACTION_PASSING;    // player is passing exit
14537     }
14538     else if (element == EL_EMPTY)
14539     {
14540       sound_action = ACTION_MOVING;             // nothing to walk on
14541     }
14542
14543     // play sound from background or player, whatever is available
14544     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14545       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14546     else
14547       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14548   }
14549   else if (player_can_move &&
14550            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14551   {
14552     if (!ACCESS_FROM(element, opposite_direction))
14553       return MP_NO_ACTION;      // field not accessible from this direction
14554
14555     if (CAN_MOVE(element))      // only fixed elements can be passed!
14556       return MP_NO_ACTION;
14557
14558     if (IS_EM_GATE(element))
14559     {
14560       if (!player->key[EM_GATE_NR(element)])
14561         return MP_NO_ACTION;
14562     }
14563     else if (IS_EM_GATE_GRAY(element))
14564     {
14565       if (!player->key[EM_GATE_GRAY_NR(element)])
14566         return MP_NO_ACTION;
14567     }
14568     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14569     {
14570       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14571         return MP_NO_ACTION;
14572     }
14573     else if (IS_EMC_GATE(element))
14574     {
14575       if (!player->key[EMC_GATE_NR(element)])
14576         return MP_NO_ACTION;
14577     }
14578     else if (IS_EMC_GATE_GRAY(element))
14579     {
14580       if (!player->key[EMC_GATE_GRAY_NR(element)])
14581         return MP_NO_ACTION;
14582     }
14583     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14584     {
14585       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14586         return MP_NO_ACTION;
14587     }
14588     else if (element == EL_DC_GATE_WHITE ||
14589              element == EL_DC_GATE_WHITE_GRAY ||
14590              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14591     {
14592       if (player->num_white_keys == 0)
14593         return MP_NO_ACTION;
14594
14595       player->num_white_keys--;
14596     }
14597     else if (IS_SP_PORT(element))
14598     {
14599       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14600           element == EL_SP_GRAVITY_PORT_RIGHT ||
14601           element == EL_SP_GRAVITY_PORT_UP ||
14602           element == EL_SP_GRAVITY_PORT_DOWN)
14603         player->gravity = !player->gravity;
14604       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14605                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14606                element == EL_SP_GRAVITY_ON_PORT_UP ||
14607                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14608         player->gravity = TRUE;
14609       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14610                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14611                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14612                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14613         player->gravity = FALSE;
14614     }
14615
14616     // automatically move to the next field with double speed
14617     player->programmed_action = move_direction;
14618
14619     if (player->move_delay_reset_counter == 0)
14620     {
14621       player->move_delay_reset_counter = 2;     // two double speed steps
14622
14623       DOUBLE_PLAYER_SPEED(player);
14624     }
14625
14626     PlayLevelSoundAction(x, y, ACTION_PASSING);
14627   }
14628   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14629   {
14630     RemoveField(x, y);
14631
14632     if (mode != DF_SNAP)
14633     {
14634       GfxElement[x][y] = GFX_ELEMENT(element);
14635       player->is_digging = TRUE;
14636     }
14637
14638     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14639
14640     // use old behaviour for old levels (digging)
14641     if (!level.finish_dig_collect)
14642     {
14643       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14644                                           player->index_bit, dig_side);
14645
14646       // if digging triggered player relocation, finish digging tile
14647       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14648         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14649     }
14650
14651     if (mode == DF_SNAP)
14652     {
14653       if (level.block_snap_field)
14654         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14655       else
14656         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14657
14658       // use old behaviour for old levels (snapping)
14659       if (!level.finish_dig_collect)
14660         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14661                                             player->index_bit, dig_side);
14662     }
14663   }
14664   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14665   {
14666     RemoveField(x, y);
14667
14668     if (is_player && mode != DF_SNAP)
14669     {
14670       GfxElement[x][y] = element;
14671       player->is_collecting = TRUE;
14672     }
14673
14674     if (element == EL_SPEED_PILL)
14675     {
14676       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14677     }
14678     else if (element == EL_EXTRA_TIME && level.time > 0)
14679     {
14680       TimeLeft += level.extra_time;
14681
14682       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14683
14684       DisplayGameControlValues();
14685     }
14686     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14687     {
14688       int shield_time = (element == EL_SHIELD_DEADLY ?
14689                          level.shield_deadly_time :
14690                          level.shield_normal_time);
14691
14692       player->shield_normal_time_left += shield_time;
14693       if (element == EL_SHIELD_DEADLY)
14694         player->shield_deadly_time_left += shield_time;
14695     }
14696     else if (element == EL_DYNAMITE ||
14697              element == EL_EM_DYNAMITE ||
14698              element == EL_SP_DISK_RED)
14699     {
14700       if (player->inventory_size < MAX_INVENTORY_SIZE)
14701         player->inventory_element[player->inventory_size++] = element;
14702
14703       DrawGameDoorValues();
14704     }
14705     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14706     {
14707       player->dynabomb_count++;
14708       player->dynabombs_left++;
14709     }
14710     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14711     {
14712       player->dynabomb_size++;
14713     }
14714     else if (element == EL_DYNABOMB_INCREASE_POWER)
14715     {
14716       player->dynabomb_xl = TRUE;
14717     }
14718     else if (IS_KEY(element))
14719     {
14720       player->key[KEY_NR(element)] = TRUE;
14721
14722       DrawGameDoorValues();
14723     }
14724     else if (element == EL_DC_KEY_WHITE)
14725     {
14726       player->num_white_keys++;
14727
14728       // display white keys?
14729       // DrawGameDoorValues();
14730     }
14731     else if (IS_ENVELOPE(element))
14732     {
14733       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14734
14735       if (!wait_for_snapping)
14736         player->show_envelope = element;
14737     }
14738     else if (element == EL_EMC_LENSES)
14739     {
14740       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14741
14742       RedrawAllInvisibleElementsForLenses();
14743     }
14744     else if (element == EL_EMC_MAGNIFIER)
14745     {
14746       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14747
14748       RedrawAllInvisibleElementsForMagnifier();
14749     }
14750     else if (IS_DROPPABLE(element) ||
14751              IS_THROWABLE(element))     // can be collected and dropped
14752     {
14753       int i;
14754
14755       if (collect_count == 0)
14756         player->inventory_infinite_element = element;
14757       else
14758         for (i = 0; i < collect_count; i++)
14759           if (player->inventory_size < MAX_INVENTORY_SIZE)
14760             player->inventory_element[player->inventory_size++] = element;
14761
14762       DrawGameDoorValues();
14763     }
14764     else if (collect_count > 0)
14765     {
14766       game.gems_still_needed -= collect_count;
14767       if (game.gems_still_needed < 0)
14768         game.gems_still_needed = 0;
14769
14770       game.snapshot.collected_item = TRUE;
14771
14772       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14773
14774       DisplayGameControlValues();
14775     }
14776
14777     RaiseScoreElement(element);
14778     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14779
14780     // use old behaviour for old levels (collecting)
14781     if (!level.finish_dig_collect && is_player)
14782     {
14783       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14784                                           player->index_bit, dig_side);
14785
14786       // if collecting triggered player relocation, finish collecting tile
14787       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14788         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14789     }
14790
14791     if (mode == DF_SNAP)
14792     {
14793       if (level.block_snap_field)
14794         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14795       else
14796         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14797
14798       // use old behaviour for old levels (snapping)
14799       if (!level.finish_dig_collect)
14800         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14801                                             player->index_bit, dig_side);
14802     }
14803   }
14804   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14805   {
14806     if (mode == DF_SNAP && element != EL_BD_ROCK)
14807       return MP_NO_ACTION;
14808
14809     if (CAN_FALL(element) && dy)
14810       return MP_NO_ACTION;
14811
14812     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14813         !(element == EL_SPRING && level.use_spring_bug))
14814       return MP_NO_ACTION;
14815
14816     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14817         ((move_direction & MV_VERTICAL &&
14818           ((element_info[element].move_pattern & MV_LEFT &&
14819             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14820            (element_info[element].move_pattern & MV_RIGHT &&
14821             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14822          (move_direction & MV_HORIZONTAL &&
14823           ((element_info[element].move_pattern & MV_UP &&
14824             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14825            (element_info[element].move_pattern & MV_DOWN &&
14826             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14827       return MP_NO_ACTION;
14828
14829     // do not push elements already moving away faster than player
14830     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14831         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14832       return MP_NO_ACTION;
14833
14834     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14835     {
14836       if (player->push_delay_value == -1 || !player_was_pushing)
14837         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14838     }
14839     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14840     {
14841       if (player->push_delay_value == -1)
14842         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14843     }
14844     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14845     {
14846       if (!player->is_pushing)
14847         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14848     }
14849
14850     player->is_pushing = TRUE;
14851     player->is_active = TRUE;
14852
14853     if (!(IN_LEV_FIELD(nextx, nexty) &&
14854           (IS_FREE(nextx, nexty) ||
14855            (IS_SB_ELEMENT(element) &&
14856             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14857            (IS_CUSTOM_ELEMENT(element) &&
14858             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14859       return MP_NO_ACTION;
14860
14861     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14862       return MP_NO_ACTION;
14863
14864     if (player->push_delay == -1)       // new pushing; restart delay
14865       player->push_delay = 0;
14866
14867     if (player->push_delay < player->push_delay_value &&
14868         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14869         element != EL_SPRING && element != EL_BALLOON)
14870     {
14871       // make sure that there is no move delay before next try to push
14872       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14873         player->move_delay = 0;
14874
14875       return MP_NO_ACTION;
14876     }
14877
14878     if (IS_CUSTOM_ELEMENT(element) &&
14879         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14880     {
14881       if (!DigFieldByCE(nextx, nexty, element))
14882         return MP_NO_ACTION;
14883     }
14884
14885     if (IS_SB_ELEMENT(element))
14886     {
14887       boolean sokoban_task_solved = FALSE;
14888
14889       if (element == EL_SOKOBAN_FIELD_FULL)
14890       {
14891         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14892
14893         IncrementSokobanFieldsNeeded();
14894         IncrementSokobanObjectsNeeded();
14895       }
14896
14897       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14898       {
14899         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14900
14901         DecrementSokobanFieldsNeeded();
14902         DecrementSokobanObjectsNeeded();
14903
14904         // sokoban object was pushed from empty field to sokoban field
14905         if (Back[x][y] == EL_EMPTY)
14906           sokoban_task_solved = TRUE;
14907       }
14908
14909       Tile[x][y] = EL_SOKOBAN_OBJECT;
14910
14911       if (Back[x][y] == Back[nextx][nexty])
14912         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14913       else if (Back[x][y] != 0)
14914         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14915                                     ACTION_EMPTYING);
14916       else
14917         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14918                                     ACTION_FILLING);
14919
14920       if (sokoban_task_solved &&
14921           game.sokoban_fields_still_needed == 0 &&
14922           game.sokoban_objects_still_needed == 0 &&
14923           level.auto_exit_sokoban)
14924       {
14925         game.players_still_needed = 0;
14926
14927         LevelSolved();
14928
14929         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14930       }
14931     }
14932     else
14933       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14934
14935     InitMovingField(x, y, move_direction);
14936     GfxAction[x][y] = ACTION_PUSHING;
14937
14938     if (mode == DF_SNAP)
14939       ContinueMoving(x, y);
14940     else
14941       MovPos[x][y] = (dx != 0 ? dx : dy);
14942
14943     Pushed[x][y] = TRUE;
14944     Pushed[nextx][nexty] = TRUE;
14945
14946     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14947       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14948     else
14949       player->push_delay_value = -1;    // get new value later
14950
14951     // check for element change _after_ element has been pushed
14952     if (game.use_change_when_pushing_bug)
14953     {
14954       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14955                                  player->index_bit, dig_side);
14956       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14957                                           player->index_bit, dig_side);
14958     }
14959   }
14960   else if (IS_SWITCHABLE(element))
14961   {
14962     if (PLAYER_SWITCHING(player, x, y))
14963     {
14964       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14965                                           player->index_bit, dig_side);
14966
14967       return MP_ACTION;
14968     }
14969
14970     player->is_switching = TRUE;
14971     player->switch_x = x;
14972     player->switch_y = y;
14973
14974     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14975
14976     if (element == EL_ROBOT_WHEEL)
14977     {
14978       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14979
14980       game.robot_wheel_x = x;
14981       game.robot_wheel_y = y;
14982       game.robot_wheel_active = TRUE;
14983
14984       TEST_DrawLevelField(x, y);
14985     }
14986     else if (element == EL_SP_TERMINAL)
14987     {
14988       int xx, yy;
14989
14990       SCAN_PLAYFIELD(xx, yy)
14991       {
14992         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14993         {
14994           Bang(xx, yy);
14995         }
14996         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14997         {
14998           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14999
15000           ResetGfxAnimation(xx, yy);
15001           TEST_DrawLevelField(xx, yy);
15002         }
15003       }
15004     }
15005     else if (IS_BELT_SWITCH(element))
15006     {
15007       ToggleBeltSwitch(x, y);
15008     }
15009     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15010              element == EL_SWITCHGATE_SWITCH_DOWN ||
15011              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15012              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15013     {
15014       ToggleSwitchgateSwitch();
15015     }
15016     else if (element == EL_LIGHT_SWITCH ||
15017              element == EL_LIGHT_SWITCH_ACTIVE)
15018     {
15019       ToggleLightSwitch(x, y);
15020     }
15021     else if (element == EL_TIMEGATE_SWITCH ||
15022              element == EL_DC_TIMEGATE_SWITCH)
15023     {
15024       ActivateTimegateSwitch(x, y);
15025     }
15026     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15027              element == EL_BALLOON_SWITCH_RIGHT ||
15028              element == EL_BALLOON_SWITCH_UP    ||
15029              element == EL_BALLOON_SWITCH_DOWN  ||
15030              element == EL_BALLOON_SWITCH_NONE  ||
15031              element == EL_BALLOON_SWITCH_ANY)
15032     {
15033       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15034                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15035                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15036                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15037                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15038                              move_direction);
15039     }
15040     else if (element == EL_LAMP)
15041     {
15042       Tile[x][y] = EL_LAMP_ACTIVE;
15043       game.lights_still_needed--;
15044
15045       ResetGfxAnimation(x, y);
15046       TEST_DrawLevelField(x, y);
15047     }
15048     else if (element == EL_TIME_ORB_FULL)
15049     {
15050       Tile[x][y] = EL_TIME_ORB_EMPTY;
15051
15052       if (level.time > 0 || level.use_time_orb_bug)
15053       {
15054         TimeLeft += level.time_orb_time;
15055         game.no_level_time_limit = FALSE;
15056
15057         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15058
15059         DisplayGameControlValues();
15060       }
15061
15062       ResetGfxAnimation(x, y);
15063       TEST_DrawLevelField(x, y);
15064     }
15065     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15066              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15067     {
15068       int xx, yy;
15069
15070       game.ball_active = !game.ball_active;
15071
15072       SCAN_PLAYFIELD(xx, yy)
15073       {
15074         int e = Tile[xx][yy];
15075
15076         if (game.ball_active)
15077         {
15078           if (e == EL_EMC_MAGIC_BALL)
15079             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15080           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15081             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15082         }
15083         else
15084         {
15085           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15086             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15087           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15088             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15089         }
15090       }
15091     }
15092
15093     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15094                                         player->index_bit, dig_side);
15095
15096     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15097                                         player->index_bit, dig_side);
15098
15099     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15100                                         player->index_bit, dig_side);
15101
15102     return MP_ACTION;
15103   }
15104   else
15105   {
15106     if (!PLAYER_SWITCHING(player, x, y))
15107     {
15108       player->is_switching = TRUE;
15109       player->switch_x = x;
15110       player->switch_y = y;
15111
15112       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15113                                  player->index_bit, dig_side);
15114       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15115                                           player->index_bit, dig_side);
15116
15117       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15118                                  player->index_bit, dig_side);
15119       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15120                                           player->index_bit, dig_side);
15121     }
15122
15123     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15124                                player->index_bit, dig_side);
15125     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15126                                         player->index_bit, dig_side);
15127
15128     return MP_NO_ACTION;
15129   }
15130
15131   player->push_delay = -1;
15132
15133   if (is_player)                // function can also be called by EL_PENGUIN
15134   {
15135     if (Tile[x][y] != element)          // really digged/collected something
15136     {
15137       player->is_collecting = !player->is_digging;
15138       player->is_active = TRUE;
15139
15140       player->last_removed_element = element;
15141     }
15142   }
15143
15144   return MP_MOVING;
15145 }
15146
15147 static boolean DigFieldByCE(int x, int y, int digging_element)
15148 {
15149   int element = Tile[x][y];
15150
15151   if (!IS_FREE(x, y))
15152   {
15153     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15154                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15155                   ACTION_BREAKING);
15156
15157     // no element can dig solid indestructible elements
15158     if (IS_INDESTRUCTIBLE(element) &&
15159         !IS_DIGGABLE(element) &&
15160         !IS_COLLECTIBLE(element))
15161       return FALSE;
15162
15163     if (AmoebaNr[x][y] &&
15164         (element == EL_AMOEBA_FULL ||
15165          element == EL_BD_AMOEBA ||
15166          element == EL_AMOEBA_GROWING))
15167     {
15168       AmoebaCnt[AmoebaNr[x][y]]--;
15169       AmoebaCnt2[AmoebaNr[x][y]]--;
15170     }
15171
15172     if (IS_MOVING(x, y))
15173       RemoveMovingField(x, y);
15174     else
15175     {
15176       RemoveField(x, y);
15177       TEST_DrawLevelField(x, y);
15178     }
15179
15180     // if digged element was about to explode, prevent the explosion
15181     ExplodeField[x][y] = EX_TYPE_NONE;
15182
15183     PlayLevelSoundAction(x, y, action);
15184   }
15185
15186   Store[x][y] = EL_EMPTY;
15187
15188   // this makes it possible to leave the removed element again
15189   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15190     Store[x][y] = element;
15191
15192   return TRUE;
15193 }
15194
15195 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15196 {
15197   int jx = player->jx, jy = player->jy;
15198   int x = jx + dx, y = jy + dy;
15199   int snap_direction = (dx == -1 ? MV_LEFT  :
15200                         dx == +1 ? MV_RIGHT :
15201                         dy == -1 ? MV_UP    :
15202                         dy == +1 ? MV_DOWN  : MV_NONE);
15203   boolean can_continue_snapping = (level.continuous_snapping &&
15204                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15205
15206   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15207     return FALSE;
15208
15209   if (!player->active || !IN_LEV_FIELD(x, y))
15210     return FALSE;
15211
15212   if (dx && dy)
15213     return FALSE;
15214
15215   if (!dx && !dy)
15216   {
15217     if (player->MovPos == 0)
15218       player->is_pushing = FALSE;
15219
15220     player->is_snapping = FALSE;
15221
15222     if (player->MovPos == 0)
15223     {
15224       player->is_moving = FALSE;
15225       player->is_digging = FALSE;
15226       player->is_collecting = FALSE;
15227     }
15228
15229     return FALSE;
15230   }
15231
15232   // prevent snapping with already pressed snap key when not allowed
15233   if (player->is_snapping && !can_continue_snapping)
15234     return FALSE;
15235
15236   player->MovDir = snap_direction;
15237
15238   if (player->MovPos == 0)
15239   {
15240     player->is_moving = FALSE;
15241     player->is_digging = FALSE;
15242     player->is_collecting = FALSE;
15243   }
15244
15245   player->is_dropping = FALSE;
15246   player->is_dropping_pressed = FALSE;
15247   player->drop_pressed_delay = 0;
15248
15249   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15250     return FALSE;
15251
15252   player->is_snapping = TRUE;
15253   player->is_active = TRUE;
15254
15255   if (player->MovPos == 0)
15256   {
15257     player->is_moving = FALSE;
15258     player->is_digging = FALSE;
15259     player->is_collecting = FALSE;
15260   }
15261
15262   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15263     TEST_DrawLevelField(player->last_jx, player->last_jy);
15264
15265   TEST_DrawLevelField(x, y);
15266
15267   return TRUE;
15268 }
15269
15270 static boolean DropElement(struct PlayerInfo *player)
15271 {
15272   int old_element, new_element;
15273   int dropx = player->jx, dropy = player->jy;
15274   int drop_direction = player->MovDir;
15275   int drop_side = drop_direction;
15276   int drop_element = get_next_dropped_element(player);
15277
15278   /* do not drop an element on top of another element; when holding drop key
15279      pressed without moving, dropped element must move away before the next
15280      element can be dropped (this is especially important if the next element
15281      is dynamite, which can be placed on background for historical reasons) */
15282   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15283     return MP_ACTION;
15284
15285   if (IS_THROWABLE(drop_element))
15286   {
15287     dropx += GET_DX_FROM_DIR(drop_direction);
15288     dropy += GET_DY_FROM_DIR(drop_direction);
15289
15290     if (!IN_LEV_FIELD(dropx, dropy))
15291       return FALSE;
15292   }
15293
15294   old_element = Tile[dropx][dropy];     // old element at dropping position
15295   new_element = drop_element;           // default: no change when dropping
15296
15297   // check if player is active, not moving and ready to drop
15298   if (!player->active || player->MovPos || player->drop_delay > 0)
15299     return FALSE;
15300
15301   // check if player has anything that can be dropped
15302   if (new_element == EL_UNDEFINED)
15303     return FALSE;
15304
15305   // only set if player has anything that can be dropped
15306   player->is_dropping_pressed = TRUE;
15307
15308   // check if drop key was pressed long enough for EM style dynamite
15309   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15310     return FALSE;
15311
15312   // check if anything can be dropped at the current position
15313   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15314     return FALSE;
15315
15316   // collected custom elements can only be dropped on empty fields
15317   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15318     return FALSE;
15319
15320   if (old_element != EL_EMPTY)
15321     Back[dropx][dropy] = old_element;   // store old element on this field
15322
15323   ResetGfxAnimation(dropx, dropy);
15324   ResetRandomAnimationValue(dropx, dropy);
15325
15326   if (player->inventory_size > 0 ||
15327       player->inventory_infinite_element != EL_UNDEFINED)
15328   {
15329     if (player->inventory_size > 0)
15330     {
15331       player->inventory_size--;
15332
15333       DrawGameDoorValues();
15334
15335       if (new_element == EL_DYNAMITE)
15336         new_element = EL_DYNAMITE_ACTIVE;
15337       else if (new_element == EL_EM_DYNAMITE)
15338         new_element = EL_EM_DYNAMITE_ACTIVE;
15339       else if (new_element == EL_SP_DISK_RED)
15340         new_element = EL_SP_DISK_RED_ACTIVE;
15341     }
15342
15343     Tile[dropx][dropy] = new_element;
15344
15345     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15346       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15347                           el2img(Tile[dropx][dropy]), 0);
15348
15349     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15350
15351     // needed if previous element just changed to "empty" in the last frame
15352     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15353
15354     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15355                                player->index_bit, drop_side);
15356     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15357                                         CE_PLAYER_DROPS_X,
15358                                         player->index_bit, drop_side);
15359
15360     TestIfElementTouchesCustomElement(dropx, dropy);
15361   }
15362   else          // player is dropping a dyna bomb
15363   {
15364     player->dynabombs_left--;
15365
15366     Tile[dropx][dropy] = new_element;
15367
15368     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15369       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15370                           el2img(Tile[dropx][dropy]), 0);
15371
15372     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15373   }
15374
15375   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15376     InitField_WithBug1(dropx, dropy, FALSE);
15377
15378   new_element = Tile[dropx][dropy];     // element might have changed
15379
15380   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15381       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15382   {
15383     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15384       MovDir[dropx][dropy] = drop_direction;
15385
15386     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15387
15388     // do not cause impact style collision by dropping elements that can fall
15389     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15390   }
15391
15392   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15393   player->is_dropping = TRUE;
15394
15395   player->drop_pressed_delay = 0;
15396   player->is_dropping_pressed = FALSE;
15397
15398   player->drop_x = dropx;
15399   player->drop_y = dropy;
15400
15401   return TRUE;
15402 }
15403
15404 // ----------------------------------------------------------------------------
15405 // game sound playing functions
15406 // ----------------------------------------------------------------------------
15407
15408 static int *loop_sound_frame = NULL;
15409 static int *loop_sound_volume = NULL;
15410
15411 void InitPlayLevelSound(void)
15412 {
15413   int num_sounds = getSoundListSize();
15414
15415   checked_free(loop_sound_frame);
15416   checked_free(loop_sound_volume);
15417
15418   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15419   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15420 }
15421
15422 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15423 {
15424   int sx = SCREENX(x), sy = SCREENY(y);
15425   int volume, stereo_position;
15426   int max_distance = 8;
15427   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15428
15429   if ((!setup.sound_simple && !is_loop_sound) ||
15430       (!setup.sound_loops && is_loop_sound))
15431     return;
15432
15433   if (!IN_LEV_FIELD(x, y) ||
15434       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15435       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15436     return;
15437
15438   volume = SOUND_MAX_VOLUME;
15439
15440   if (!IN_SCR_FIELD(sx, sy))
15441   {
15442     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15443     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15444
15445     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15446   }
15447
15448   stereo_position = (SOUND_MAX_LEFT +
15449                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15450                      (SCR_FIELDX + 2 * max_distance));
15451
15452   if (is_loop_sound)
15453   {
15454     /* This assures that quieter loop sounds do not overwrite louder ones,
15455        while restarting sound volume comparison with each new game frame. */
15456
15457     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15458       return;
15459
15460     loop_sound_volume[nr] = volume;
15461     loop_sound_frame[nr] = FrameCounter;
15462   }
15463
15464   PlaySoundExt(nr, volume, stereo_position, type);
15465 }
15466
15467 static void PlayLevelSound(int x, int y, int nr)
15468 {
15469   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15470 }
15471
15472 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15473 {
15474   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15475                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15476                  y < LEVELY(BY1) ? LEVELY(BY1) :
15477                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15478                  sound_action);
15479 }
15480
15481 static void PlayLevelSoundAction(int x, int y, int action)
15482 {
15483   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15484 }
15485
15486 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15487 {
15488   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15489
15490   if (sound_effect != SND_UNDEFINED)
15491     PlayLevelSound(x, y, sound_effect);
15492 }
15493
15494 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15495                                               int action)
15496 {
15497   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15498
15499   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15500     PlayLevelSound(x, y, sound_effect);
15501 }
15502
15503 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15504 {
15505   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15506
15507   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15508     PlayLevelSound(x, y, sound_effect);
15509 }
15510
15511 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15512 {
15513   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15514
15515   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15516     StopSound(sound_effect);
15517 }
15518
15519 static int getLevelMusicNr(void)
15520 {
15521   int level_pos = level_nr - leveldir_current->first_level;
15522
15523   if (levelset.music[level_nr] != MUS_UNDEFINED)
15524     return levelset.music[level_nr];            // from config file
15525   else
15526     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15527 }
15528
15529 static void FadeLevelSounds(void)
15530 {
15531   FadeSounds();
15532 }
15533
15534 static void FadeLevelMusic(void)
15535 {
15536   int music_nr = getLevelMusicNr();
15537   char *curr_music = getCurrentlyPlayingMusicFilename();
15538   char *next_music = getMusicInfoEntryFilename(music_nr);
15539
15540   if (!strEqual(curr_music, next_music))
15541     FadeMusic();
15542 }
15543
15544 void FadeLevelSoundsAndMusic(void)
15545 {
15546   FadeLevelSounds();
15547   FadeLevelMusic();
15548 }
15549
15550 static void PlayLevelMusic(void)
15551 {
15552   int music_nr = getLevelMusicNr();
15553   char *curr_music = getCurrentlyPlayingMusicFilename();
15554   char *next_music = getMusicInfoEntryFilename(music_nr);
15555
15556   if (!strEqual(curr_music, next_music))
15557     PlayMusicLoop(music_nr);
15558 }
15559
15560 static int getSoundAction_BD(int sample)
15561 {
15562   switch (sample)
15563   {
15564     case GD_S_STONE_PUSHING:
15565     case GD_S_MEGA_STONE_PUSHING:
15566     case GD_S_FLYING_STONE_PUSHING:
15567     case GD_S_WAITING_STONE_PUSHING:
15568     case GD_S_CHASING_STONE_PUSHING:
15569     case GD_S_NUT_PUSHING:
15570     case GD_S_NITRO_PACK_PUSHING:
15571     case GD_S_BLADDER_PUSHING:
15572     case GD_S_BOX_PUSHING:
15573       return ACTION_PUSHING;
15574
15575     case GD_S_STONE_FALLING:
15576     case GD_S_MEGA_STONE_FALLING:
15577     case GD_S_FLYING_STONE_FALLING:
15578     case GD_S_NUT_FALLING:
15579     case GD_S_DIRT_BALL_FALLING:
15580     case GD_S_DIRT_LOOSE_FALLING:
15581     case GD_S_NITRO_PACK_FALLING:
15582     case GD_S_FALLING_WALL_FALLING:
15583       return ACTION_FALLING;
15584
15585     case GD_S_STONE_IMPACT:
15586     case GD_S_MEGA_STONE_IMPACT:
15587     case GD_S_FLYING_STONE_IMPACT:
15588     case GD_S_NUT_IMPACT:
15589     case GD_S_DIRT_BALL_IMPACT:
15590     case GD_S_DIRT_LOOSE_IMPACT:
15591     case GD_S_NITRO_PACK_IMPACT:
15592     case GD_S_FALLING_WALL_IMPACT:
15593       return ACTION_IMPACT;
15594
15595     case GD_S_NUT_CRACKING:
15596       return ACTION_BREAKING;
15597
15598     case GD_S_EXPANDING_WALL:
15599     case GD_S_WALL_REAPPEARING:
15600     case GD_S_SLIME:
15601     case GD_S_LAVA:
15602     case GD_S_ACID_SPREADING:
15603       return ACTION_GROWING;
15604
15605     case GD_S_DIAMOND_COLLECTING:
15606     case GD_S_FLYING_DIAMOND_COLLECTING:
15607     case GD_S_SKELETON_COLLECTING:
15608     case GD_S_PNEUMATIC_COLLECTING:
15609     case GD_S_BOMB_COLLECTING:
15610     case GD_S_CLOCK_COLLECTING:
15611     case GD_S_SWEET_COLLECTING:
15612     case GD_S_KEY_COLLECTING:
15613     case GD_S_DIAMOND_KEY_COLLECTING:
15614       return ACTION_COLLECTING;
15615
15616     case GD_S_BOMB_PLACING:
15617     case GD_S_REPLICATOR:
15618       return ACTION_DROPPING;
15619
15620     case GD_S_BLADDER_MOVING:
15621       return ACTION_MOVING;
15622
15623     case GD_S_BLADDER_SPENDER:
15624     case GD_S_BLADDER_CONVERTING:
15625     case GD_S_GRAVITY_CHANGING:
15626       return ACTION_CHANGING;
15627
15628     case GD_S_BITER_EATING:
15629       return ACTION_EATING;
15630
15631     case GD_S_DOOR_OPENING:
15632     case GD_S_CRACKING:
15633       return ACTION_OPENING;
15634
15635     case GD_S_DIRT_WALKING:
15636       return ACTION_DIGGING;
15637
15638     case GD_S_EMPTY_WALKING:
15639       return ACTION_WALKING;
15640
15641     case GD_S_SWITCH_BITER:
15642     case GD_S_SWITCH_CREATURES:
15643     case GD_S_SWITCH_GRAVITY:
15644     case GD_S_SWITCH_EXPANDING:
15645     case GD_S_SWITCH_CONVEYOR:
15646     case GD_S_SWITCH_REPLICATOR:
15647     case GD_S_STIRRING:
15648       return ACTION_ACTIVATING;
15649
15650     case GD_S_TELEPORTER:
15651       return ACTION_PASSING;
15652
15653     case GD_S_EXPLODING:
15654     case GD_S_BOMB_EXPLODING:
15655     case GD_S_GHOST_EXPLODING:
15656     case GD_S_VOODOO_EXPLODING:
15657     case GD_S_NITRO_PACK_EXPLODING:
15658       return ACTION_EXPLODING;
15659
15660     case GD_S_COVERING:
15661     case GD_S_AMOEBA:
15662     case GD_S_MAGIC_WALL:
15663     case GD_S_PNEUMATIC_HAMMER:
15664     case GD_S_WATER:
15665       return ACTION_ACTIVE;
15666
15667     case GD_S_DIAMOND_FALLING_RANDOM:
15668     case GD_S_DIAMOND_FALLING_1:
15669     case GD_S_DIAMOND_FALLING_2:
15670     case GD_S_DIAMOND_FALLING_3:
15671     case GD_S_DIAMOND_FALLING_4:
15672     case GD_S_DIAMOND_FALLING_5:
15673     case GD_S_DIAMOND_FALLING_6:
15674     case GD_S_DIAMOND_FALLING_7:
15675     case GD_S_DIAMOND_FALLING_8:
15676     case GD_S_DIAMOND_IMPACT_RANDOM:
15677     case GD_S_DIAMOND_IMPACT_1:
15678     case GD_S_DIAMOND_IMPACT_2:
15679     case GD_S_DIAMOND_IMPACT_3:
15680     case GD_S_DIAMOND_IMPACT_4:
15681     case GD_S_DIAMOND_IMPACT_5:
15682     case GD_S_DIAMOND_IMPACT_6:
15683     case GD_S_DIAMOND_IMPACT_7:
15684     case GD_S_DIAMOND_IMPACT_8:
15685     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15686     case GD_S_FLYING_DIAMOND_FALLING_1:
15687     case GD_S_FLYING_DIAMOND_FALLING_2:
15688     case GD_S_FLYING_DIAMOND_FALLING_3:
15689     case GD_S_FLYING_DIAMOND_FALLING_4:
15690     case GD_S_FLYING_DIAMOND_FALLING_5:
15691     case GD_S_FLYING_DIAMOND_FALLING_6:
15692     case GD_S_FLYING_DIAMOND_FALLING_7:
15693     case GD_S_FLYING_DIAMOND_FALLING_8:
15694     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15695     case GD_S_FLYING_DIAMOND_IMPACT_1:
15696     case GD_S_FLYING_DIAMOND_IMPACT_2:
15697     case GD_S_FLYING_DIAMOND_IMPACT_3:
15698     case GD_S_FLYING_DIAMOND_IMPACT_4:
15699     case GD_S_FLYING_DIAMOND_IMPACT_5:
15700     case GD_S_FLYING_DIAMOND_IMPACT_6:
15701     case GD_S_FLYING_DIAMOND_IMPACT_7:
15702     case GD_S_FLYING_DIAMOND_IMPACT_8:
15703     case GD_S_TIMEOUT_0:
15704     case GD_S_TIMEOUT_1:
15705     case GD_S_TIMEOUT_2:
15706     case GD_S_TIMEOUT_3:
15707     case GD_S_TIMEOUT_4:
15708     case GD_S_TIMEOUT_5:
15709     case GD_S_TIMEOUT_6:
15710     case GD_S_TIMEOUT_7:
15711     case GD_S_TIMEOUT_8:
15712     case GD_S_TIMEOUT_9:
15713     case GD_S_TIMEOUT_10:
15714     case GD_S_BONUS_LIFE:
15715       // trigger special post-processing (and force sound to be non-looping)
15716       return ACTION_OTHER;
15717
15718     case GD_S_AMOEBA_MAGIC:
15719     case GD_S_FINISHED:
15720       // trigger special post-processing (and force sound to be looping)
15721       return ACTION_DEFAULT;
15722
15723     default:
15724       return ACTION_DEFAULT;
15725   }
15726 }
15727
15728 static int getSoundEffect_BD(int element_bd, int sample)
15729 {
15730   int sound_action = getSoundAction_BD(sample);
15731   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15732   int nr;
15733
15734   // standard sounds
15735   if (sound_action != ACTION_OTHER &&
15736       sound_action != ACTION_DEFAULT)
15737     return sound_effect;
15738
15739   // special post-processing for some sounds
15740   switch (sample)
15741   {
15742     case GD_S_DIAMOND_FALLING_RANDOM:
15743     case GD_S_DIAMOND_FALLING_1:
15744     case GD_S_DIAMOND_FALLING_2:
15745     case GD_S_DIAMOND_FALLING_3:
15746     case GD_S_DIAMOND_FALLING_4:
15747     case GD_S_DIAMOND_FALLING_5:
15748     case GD_S_DIAMOND_FALLING_6:
15749     case GD_S_DIAMOND_FALLING_7:
15750     case GD_S_DIAMOND_FALLING_8:
15751       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15752             sample - GD_S_DIAMOND_FALLING_1);
15753       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15754
15755       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15756         sound_effect = SND_BD_DIAMOND_FALLING;
15757       break;
15758
15759     case GD_S_DIAMOND_IMPACT_RANDOM:
15760     case GD_S_DIAMOND_IMPACT_1:
15761     case GD_S_DIAMOND_IMPACT_2:
15762     case GD_S_DIAMOND_IMPACT_3:
15763     case GD_S_DIAMOND_IMPACT_4:
15764     case GD_S_DIAMOND_IMPACT_5:
15765     case GD_S_DIAMOND_IMPACT_6:
15766     case GD_S_DIAMOND_IMPACT_7:
15767     case GD_S_DIAMOND_IMPACT_8:
15768       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15769             sample - GD_S_DIAMOND_IMPACT_1);
15770       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15771
15772       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15773         sound_effect = SND_BD_DIAMOND_IMPACT;
15774       break;
15775
15776     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15777     case GD_S_FLYING_DIAMOND_FALLING_1:
15778     case GD_S_FLYING_DIAMOND_FALLING_2:
15779     case GD_S_FLYING_DIAMOND_FALLING_3:
15780     case GD_S_FLYING_DIAMOND_FALLING_4:
15781     case GD_S_FLYING_DIAMOND_FALLING_5:
15782     case GD_S_FLYING_DIAMOND_FALLING_6:
15783     case GD_S_FLYING_DIAMOND_FALLING_7:
15784     case GD_S_FLYING_DIAMOND_FALLING_8:
15785       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15786             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15787       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15788
15789       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15790         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15791       break;
15792
15793     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15794     case GD_S_FLYING_DIAMOND_IMPACT_1:
15795     case GD_S_FLYING_DIAMOND_IMPACT_2:
15796     case GD_S_FLYING_DIAMOND_IMPACT_3:
15797     case GD_S_FLYING_DIAMOND_IMPACT_4:
15798     case GD_S_FLYING_DIAMOND_IMPACT_5:
15799     case GD_S_FLYING_DIAMOND_IMPACT_6:
15800     case GD_S_FLYING_DIAMOND_IMPACT_7:
15801     case GD_S_FLYING_DIAMOND_IMPACT_8:
15802       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15803             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15804       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15805
15806       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15807         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15808       break;
15809
15810     case GD_S_TIMEOUT_0:
15811     case GD_S_TIMEOUT_1:
15812     case GD_S_TIMEOUT_2:
15813     case GD_S_TIMEOUT_3:
15814     case GD_S_TIMEOUT_4:
15815     case GD_S_TIMEOUT_5:
15816     case GD_S_TIMEOUT_6:
15817     case GD_S_TIMEOUT_7:
15818     case GD_S_TIMEOUT_8:
15819     case GD_S_TIMEOUT_9:
15820     case GD_S_TIMEOUT_10:
15821       nr = sample - GD_S_TIMEOUT_0;
15822       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15823
15824       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15825         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15826       break;
15827
15828     case GD_S_BONUS_LIFE:
15829       sound_effect = SND_GAME_HEALTH_BONUS;
15830       break;
15831
15832     case GD_S_AMOEBA_MAGIC:
15833       sound_effect = SND_BD_AMOEBA_OTHER;
15834       break;
15835
15836     case GD_S_FINISHED:
15837       sound_effect = SND_GAME_LEVELTIME_BONUS;
15838       break;
15839
15840     default:
15841       sound_effect = SND_UNDEFINED;
15842       break;
15843   }
15844
15845   return sound_effect;
15846 }
15847
15848 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15849 {
15850   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15851   int sound_effect = getSoundEffect_BD(element, sample);
15852   int sound_action = getSoundAction_BD(sample);
15853   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15854   int offset = 0;
15855   int x = xx - offset;
15856   int y = yy - offset;
15857
15858   // some sound actions are always looping in native BD game engine
15859   if (sound_action == ACTION_DEFAULT)
15860     is_loop_sound = TRUE;
15861
15862   // some sound actions are always non-looping in native BD game engine
15863   if (sound_action == ACTION_FALLING ||
15864       sound_action == ACTION_MOVING ||
15865       sound_action == ACTION_OTHER)
15866     is_loop_sound = FALSE;
15867
15868   if (sound_effect != SND_UNDEFINED)
15869     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15870 }
15871
15872 void StopSound_BD(int element_bd, int sample)
15873 {
15874   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15875   int sound_effect = getSoundEffect_BD(element, sample);
15876
15877   if (sound_effect != SND_UNDEFINED)
15878     StopSound(sound_effect);
15879 }
15880
15881 boolean isSoundPlaying_BD(int element_bd, int sample)
15882 {
15883   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15884   int sound_effect = getSoundEffect_BD(element, sample);
15885
15886   if (sound_effect != SND_UNDEFINED)
15887     return isSoundPlaying(sound_effect);
15888
15889   return FALSE;
15890 }
15891
15892 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15893 {
15894   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15895   int offset = 0;
15896   int x = xx - offset;
15897   int y = yy - offset;
15898
15899   switch (sample)
15900   {
15901     case SOUND_blank:
15902       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15903       break;
15904
15905     case SOUND_roll:
15906       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15907       break;
15908
15909     case SOUND_stone:
15910       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15911       break;
15912
15913     case SOUND_nut:
15914       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15915       break;
15916
15917     case SOUND_crack:
15918       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15919       break;
15920
15921     case SOUND_bug:
15922       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15923       break;
15924
15925     case SOUND_tank:
15926       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15927       break;
15928
15929     case SOUND_android_clone:
15930       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15931       break;
15932
15933     case SOUND_android_move:
15934       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15935       break;
15936
15937     case SOUND_spring:
15938       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15939       break;
15940
15941     case SOUND_slurp:
15942       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15943       break;
15944
15945     case SOUND_eater:
15946       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15947       break;
15948
15949     case SOUND_eater_eat:
15950       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15951       break;
15952
15953     case SOUND_alien:
15954       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15955       break;
15956
15957     case SOUND_collect:
15958       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15959       break;
15960
15961     case SOUND_diamond:
15962       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15963       break;
15964
15965     case SOUND_squash:
15966       // !!! CHECK THIS !!!
15967 #if 1
15968       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15969 #else
15970       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15971 #endif
15972       break;
15973
15974     case SOUND_wonderfall:
15975       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15976       break;
15977
15978     case SOUND_drip:
15979       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15980       break;
15981
15982     case SOUND_push:
15983       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15984       break;
15985
15986     case SOUND_dirt:
15987       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15988       break;
15989
15990     case SOUND_acid:
15991       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15992       break;
15993
15994     case SOUND_ball:
15995       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15996       break;
15997
15998     case SOUND_slide:
15999       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16000       break;
16001
16002     case SOUND_wonder:
16003       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16004       break;
16005
16006     case SOUND_door:
16007       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16008       break;
16009
16010     case SOUND_exit_open:
16011       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16012       break;
16013
16014     case SOUND_exit_leave:
16015       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16016       break;
16017
16018     case SOUND_dynamite:
16019       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16020       break;
16021
16022     case SOUND_tick:
16023       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16024       break;
16025
16026     case SOUND_press:
16027       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16028       break;
16029
16030     case SOUND_wheel:
16031       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16032       break;
16033
16034     case SOUND_boom:
16035       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16036       break;
16037
16038     case SOUND_die:
16039       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16040       break;
16041
16042     case SOUND_time:
16043       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16044       break;
16045
16046     default:
16047       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16048       break;
16049   }
16050 }
16051
16052 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16053 {
16054   int element = map_element_SP_to_RND(element_sp);
16055   int action = map_action_SP_to_RND(action_sp);
16056   int offset = (setup.sp_show_border_elements ? 0 : 1);
16057   int x = xx - offset;
16058   int y = yy - offset;
16059
16060   PlayLevelSoundElementAction(x, y, element, action);
16061 }
16062
16063 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16064 {
16065   int element = map_element_MM_to_RND(element_mm);
16066   int action = map_action_MM_to_RND(action_mm);
16067   int offset = 0;
16068   int x = xx - offset;
16069   int y = yy - offset;
16070
16071   if (!IS_MM_ELEMENT(element))
16072     element = EL_MM_DEFAULT;
16073
16074   PlayLevelSoundElementAction(x, y, element, action);
16075 }
16076
16077 void PlaySound_MM(int sound_mm)
16078 {
16079   int sound = map_sound_MM_to_RND(sound_mm);
16080
16081   if (sound == SND_UNDEFINED)
16082     return;
16083
16084   PlaySound(sound);
16085 }
16086
16087 void PlaySoundLoop_MM(int sound_mm)
16088 {
16089   int sound = map_sound_MM_to_RND(sound_mm);
16090
16091   if (sound == SND_UNDEFINED)
16092     return;
16093
16094   PlaySoundLoop(sound);
16095 }
16096
16097 void StopSound_MM(int sound_mm)
16098 {
16099   int sound = map_sound_MM_to_RND(sound_mm);
16100
16101   if (sound == SND_UNDEFINED)
16102     return;
16103
16104   StopSound(sound);
16105 }
16106
16107 void RaiseScore(int value)
16108 {
16109   game.score += value;
16110
16111   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16112
16113   DisplayGameControlValues();
16114 }
16115
16116 void RaiseScoreElement(int element)
16117 {
16118   switch (element)
16119   {
16120     case EL_EMERALD:
16121     case EL_BD_DIAMOND:
16122     case EL_EMERALD_YELLOW:
16123     case EL_EMERALD_RED:
16124     case EL_EMERALD_PURPLE:
16125     case EL_SP_INFOTRON:
16126       RaiseScore(level.score[SC_EMERALD]);
16127       break;
16128     case EL_DIAMOND:
16129       RaiseScore(level.score[SC_DIAMOND]);
16130       break;
16131     case EL_CRYSTAL:
16132       RaiseScore(level.score[SC_CRYSTAL]);
16133       break;
16134     case EL_PEARL:
16135       RaiseScore(level.score[SC_PEARL]);
16136       break;
16137     case EL_BUG:
16138     case EL_BD_BUTTERFLY:
16139     case EL_SP_ELECTRON:
16140       RaiseScore(level.score[SC_BUG]);
16141       break;
16142     case EL_SPACESHIP:
16143     case EL_BD_FIREFLY:
16144     case EL_SP_SNIKSNAK:
16145       RaiseScore(level.score[SC_SPACESHIP]);
16146       break;
16147     case EL_YAMYAM:
16148     case EL_DARK_YAMYAM:
16149       RaiseScore(level.score[SC_YAMYAM]);
16150       break;
16151     case EL_ROBOT:
16152       RaiseScore(level.score[SC_ROBOT]);
16153       break;
16154     case EL_PACMAN:
16155       RaiseScore(level.score[SC_PACMAN]);
16156       break;
16157     case EL_NUT:
16158       RaiseScore(level.score[SC_NUT]);
16159       break;
16160     case EL_DYNAMITE:
16161     case EL_EM_DYNAMITE:
16162     case EL_SP_DISK_RED:
16163     case EL_DYNABOMB_INCREASE_NUMBER:
16164     case EL_DYNABOMB_INCREASE_SIZE:
16165     case EL_DYNABOMB_INCREASE_POWER:
16166       RaiseScore(level.score[SC_DYNAMITE]);
16167       break;
16168     case EL_SHIELD_NORMAL:
16169     case EL_SHIELD_DEADLY:
16170       RaiseScore(level.score[SC_SHIELD]);
16171       break;
16172     case EL_EXTRA_TIME:
16173       RaiseScore(level.extra_time_score);
16174       break;
16175     case EL_KEY_1:
16176     case EL_KEY_2:
16177     case EL_KEY_3:
16178     case EL_KEY_4:
16179     case EL_EM_KEY_1:
16180     case EL_EM_KEY_2:
16181     case EL_EM_KEY_3:
16182     case EL_EM_KEY_4:
16183     case EL_EMC_KEY_5:
16184     case EL_EMC_KEY_6:
16185     case EL_EMC_KEY_7:
16186     case EL_EMC_KEY_8:
16187     case EL_DC_KEY_WHITE:
16188       RaiseScore(level.score[SC_KEY]);
16189       break;
16190     default:
16191       RaiseScore(element_info[element].collect_score);
16192       break;
16193   }
16194 }
16195
16196 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16197 {
16198   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16199   {
16200     if (!quick_quit)
16201     {
16202       // prevent short reactivation of overlay buttons while closing door
16203       SetOverlayActive(FALSE);
16204       UnmapGameButtons();
16205
16206       // door may still be open due to skipped or envelope style request
16207       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16208     }
16209
16210     if (network.enabled)
16211     {
16212       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16213     }
16214     else
16215     {
16216       // when using BD game engine, cover screen before fading out
16217       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16218         game_bd.cover_screen = TRUE;
16219
16220       if (quick_quit)
16221         FadeSkipNextFadeIn();
16222
16223       SetGameStatus(GAME_MODE_MAIN);
16224
16225       DrawMainMenu();
16226     }
16227   }
16228   else          // continue playing the game
16229   {
16230     if (tape.playing && tape.deactivate_display)
16231       TapeDeactivateDisplayOff(TRUE);
16232
16233     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16234
16235     if (tape.playing && tape.deactivate_display)
16236       TapeDeactivateDisplayOn();
16237   }
16238 }
16239
16240 void RequestQuitGame(boolean escape_key_pressed)
16241 {
16242   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16243   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16244                         level_editor_test_game);
16245   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16246                           quick_quit || score_info_tape_play);
16247
16248   RequestQuitGameExt(skip_request, quick_quit,
16249                      "Do you really want to quit the game?");
16250 }
16251
16252 static char *getRestartGameMessage(void)
16253 {
16254   boolean play_again = hasStartedNetworkGame();
16255   static char message[MAX_OUTPUT_LINESIZE];
16256   char *game_over_text = "Game over!";
16257   char *play_again_text = " Play it again?";
16258
16259   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16260       game_mm.game_over_message != NULL)
16261     game_over_text = game_mm.game_over_message;
16262
16263   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16264            (play_again ? play_again_text : ""));
16265
16266   return message;
16267 }
16268
16269 static void RequestRestartGame(void)
16270 {
16271   char *message = getRestartGameMessage();
16272   boolean has_started_game = hasStartedNetworkGame();
16273   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16274   int door_state = DOOR_CLOSE_1;
16275
16276   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
16277   {
16278     CloseDoor(door_state);
16279
16280     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16281   }
16282   else
16283   {
16284     // if game was invoked from level editor, also close tape recorder door
16285     if (level_editor_test_game)
16286       door_state = DOOR_CLOSE_ALL;
16287
16288     CloseDoor(door_state);
16289
16290     SetGameStatus(GAME_MODE_MAIN);
16291
16292     DrawMainMenu();
16293   }
16294 }
16295
16296 boolean CheckRestartGame(void)
16297 {
16298   static int game_over_delay = 0;
16299   int game_over_delay_value = 50;
16300   boolean game_over = checkGameFailed();
16301
16302   if (!game_over)
16303   {
16304     game_over_delay = game_over_delay_value;
16305
16306     return FALSE;
16307   }
16308
16309   if (game_over_delay > 0)
16310   {
16311     if (game_over_delay == game_over_delay_value / 2)
16312       PlaySound(SND_GAME_LOSING);
16313
16314     game_over_delay--;
16315
16316     return FALSE;
16317   }
16318
16319   // do not ask to play again if request dialog is already active
16320   if (game.request_active)
16321     return FALSE;
16322
16323   // do not ask to play again if request dialog already handled
16324   if (game.RestartGameRequested)
16325     return FALSE;
16326
16327   // do not ask to play again if game was never actually played
16328   if (!game.GamePlayed)
16329     return FALSE;
16330
16331   // do not ask to play again if this was disabled in setup menu
16332   if (!setup.ask_on_game_over)
16333     return FALSE;
16334
16335   game.RestartGameRequested = TRUE;
16336
16337   RequestRestartGame();
16338
16339   return TRUE;
16340 }
16341
16342 boolean checkGameRunning(void)
16343 {
16344   if (game_status != GAME_MODE_PLAYING)
16345     return FALSE;
16346
16347   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16348     return FALSE;
16349
16350   return TRUE;
16351 }
16352
16353 boolean checkGamePlaying(void)
16354 {
16355   if (game_status != GAME_MODE_PLAYING)
16356     return FALSE;
16357
16358   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16359     return FALSE;
16360
16361   return TRUE;
16362 }
16363
16364 boolean checkGameSolved(void)
16365 {
16366   // set for all game engines if level was solved
16367   return game.LevelSolved_GameEnd;
16368 }
16369
16370 boolean checkGameFailed(void)
16371 {
16372   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16373     return (game_bd.game_over && !game_bd.level_solved);
16374   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16375     return (game_em.game_over && !game_em.level_solved);
16376   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16377     return (game_sp.game_over && !game_sp.level_solved);
16378   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16379     return (game_mm.game_over && !game_mm.level_solved);
16380   else                          // GAME_ENGINE_TYPE_RND
16381     return (game.GameOver && !game.LevelSolved);
16382 }
16383
16384 boolean checkGameEnded(void)
16385 {
16386   return (checkGameSolved() || checkGameFailed());
16387 }
16388
16389
16390 // ----------------------------------------------------------------------------
16391 // random generator functions
16392 // ----------------------------------------------------------------------------
16393
16394 unsigned int InitEngineRandom_RND(int seed)
16395 {
16396   game.num_random_calls = 0;
16397
16398   return InitEngineRandom(seed);
16399 }
16400
16401 unsigned int RND(int max)
16402 {
16403   if (max > 0)
16404   {
16405     game.num_random_calls++;
16406
16407     return GetEngineRandom(max);
16408   }
16409
16410   return 0;
16411 }
16412
16413
16414 // ----------------------------------------------------------------------------
16415 // game engine snapshot handling functions
16416 // ----------------------------------------------------------------------------
16417
16418 struct EngineSnapshotInfo
16419 {
16420   // runtime values for custom element collect score
16421   int collect_score[NUM_CUSTOM_ELEMENTS];
16422
16423   // runtime values for group element choice position
16424   int choice_pos[NUM_GROUP_ELEMENTS];
16425
16426   // runtime values for belt position animations
16427   int belt_graphic[4][NUM_BELT_PARTS];
16428   int belt_anim_mode[4][NUM_BELT_PARTS];
16429 };
16430
16431 static struct EngineSnapshotInfo engine_snapshot_rnd;
16432 static char *snapshot_level_identifier = NULL;
16433 static int snapshot_level_nr = -1;
16434
16435 static void SaveEngineSnapshotValues_RND(void)
16436 {
16437   static int belt_base_active_element[4] =
16438   {
16439     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16440     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16441     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16442     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16443   };
16444   int i, j;
16445
16446   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16447   {
16448     int element = EL_CUSTOM_START + i;
16449
16450     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16451   }
16452
16453   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16454   {
16455     int element = EL_GROUP_START + i;
16456
16457     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16458   }
16459
16460   for (i = 0; i < 4; i++)
16461   {
16462     for (j = 0; j < NUM_BELT_PARTS; j++)
16463     {
16464       int element = belt_base_active_element[i] + j;
16465       int graphic = el2img(element);
16466       int anim_mode = graphic_info[graphic].anim_mode;
16467
16468       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16469       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16470     }
16471   }
16472 }
16473
16474 static void LoadEngineSnapshotValues_RND(void)
16475 {
16476   unsigned int num_random_calls = game.num_random_calls;
16477   int i, j;
16478
16479   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16480   {
16481     int element = EL_CUSTOM_START + i;
16482
16483     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16484   }
16485
16486   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16487   {
16488     int element = EL_GROUP_START + i;
16489
16490     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16491   }
16492
16493   for (i = 0; i < 4; i++)
16494   {
16495     for (j = 0; j < NUM_BELT_PARTS; j++)
16496     {
16497       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16498       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16499
16500       graphic_info[graphic].anim_mode = anim_mode;
16501     }
16502   }
16503
16504   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16505   {
16506     InitRND(tape.random_seed);
16507     for (i = 0; i < num_random_calls; i++)
16508       RND(1);
16509   }
16510
16511   if (game.num_random_calls != num_random_calls)
16512   {
16513     Error("number of random calls out of sync");
16514     Error("number of random calls should be %d", num_random_calls);
16515     Error("number of random calls is %d", game.num_random_calls);
16516
16517     Fail("this should not happen -- please debug");
16518   }
16519 }
16520
16521 void FreeEngineSnapshotSingle(void)
16522 {
16523   FreeSnapshotSingle();
16524
16525   setString(&snapshot_level_identifier, NULL);
16526   snapshot_level_nr = -1;
16527 }
16528
16529 void FreeEngineSnapshotList(void)
16530 {
16531   FreeSnapshotList();
16532 }
16533
16534 static ListNode *SaveEngineSnapshotBuffers(void)
16535 {
16536   ListNode *buffers = NULL;
16537
16538   // copy some special values to a structure better suited for the snapshot
16539
16540   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16541     SaveEngineSnapshotValues_RND();
16542   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16543     SaveEngineSnapshotValues_EM();
16544   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16545     SaveEngineSnapshotValues_SP(&buffers);
16546   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16547     SaveEngineSnapshotValues_MM();
16548
16549   // save values stored in special snapshot structure
16550
16551   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16552     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16553   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16554     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16555   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16556     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16557   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16558     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16559
16560   // save further RND engine values
16561
16562   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16565
16566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16572
16573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16576
16577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16578
16579   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16581
16582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16583   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16600
16601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16603
16604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16607
16608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16610
16611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16617
16618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16620
16621 #if 0
16622   ListNode *node = engine_snapshot_list_rnd;
16623   int num_bytes = 0;
16624
16625   while (node != NULL)
16626   {
16627     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16628
16629     node = node->next;
16630   }
16631
16632   Debug("game:playing:SaveEngineSnapshotBuffers",
16633         "size of engine snapshot: %d bytes", num_bytes);
16634 #endif
16635
16636   return buffers;
16637 }
16638
16639 void SaveEngineSnapshotSingle(void)
16640 {
16641   ListNode *buffers = SaveEngineSnapshotBuffers();
16642
16643   // finally save all snapshot buffers to single snapshot
16644   SaveSnapshotSingle(buffers);
16645
16646   // save level identification information
16647   setString(&snapshot_level_identifier, leveldir_current->identifier);
16648   snapshot_level_nr = level_nr;
16649 }
16650
16651 boolean CheckSaveEngineSnapshotToList(void)
16652 {
16653   boolean save_snapshot =
16654     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16655      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16656       game.snapshot.changed_action) ||
16657      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16658       game.snapshot.collected_item));
16659
16660   game.snapshot.changed_action = FALSE;
16661   game.snapshot.collected_item = FALSE;
16662   game.snapshot.save_snapshot = save_snapshot;
16663
16664   return save_snapshot;
16665 }
16666
16667 void SaveEngineSnapshotToList(void)
16668 {
16669   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16670       tape.quick_resume)
16671     return;
16672
16673   ListNode *buffers = SaveEngineSnapshotBuffers();
16674
16675   // finally save all snapshot buffers to snapshot list
16676   SaveSnapshotToList(buffers);
16677 }
16678
16679 void SaveEngineSnapshotToListInitial(void)
16680 {
16681   FreeEngineSnapshotList();
16682
16683   SaveEngineSnapshotToList();
16684 }
16685
16686 static void LoadEngineSnapshotValues(void)
16687 {
16688   // restore special values from snapshot structure
16689
16690   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16691     LoadEngineSnapshotValues_RND();
16692   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16693     LoadEngineSnapshotValues_EM();
16694   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16695     LoadEngineSnapshotValues_SP();
16696   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16697     LoadEngineSnapshotValues_MM();
16698 }
16699
16700 void LoadEngineSnapshotSingle(void)
16701 {
16702   LoadSnapshotSingle();
16703
16704   LoadEngineSnapshotValues();
16705 }
16706
16707 static void LoadEngineSnapshot_Undo(int steps)
16708 {
16709   LoadSnapshotFromList_Older(steps);
16710
16711   LoadEngineSnapshotValues();
16712 }
16713
16714 static void LoadEngineSnapshot_Redo(int steps)
16715 {
16716   LoadSnapshotFromList_Newer(steps);
16717
16718   LoadEngineSnapshotValues();
16719 }
16720
16721 boolean CheckEngineSnapshotSingle(void)
16722 {
16723   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16724           snapshot_level_nr == level_nr);
16725 }
16726
16727 boolean CheckEngineSnapshotList(void)
16728 {
16729   return CheckSnapshotList();
16730 }
16731
16732
16733 // ---------- new game button stuff -------------------------------------------
16734
16735 static struct
16736 {
16737   int graphic;
16738   struct XY *pos;
16739   int gadget_id;
16740   boolean *setup_value;
16741   boolean allowed_on_tape;
16742   boolean is_touch_button;
16743   char *infotext;
16744 } gamebutton_info[NUM_GAME_BUTTONS] =
16745 {
16746   {
16747     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16748     GAME_CTRL_ID_STOP,                          NULL,
16749     TRUE, FALSE,                                "stop game"
16750   },
16751   {
16752     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16753     GAME_CTRL_ID_PAUSE,                         NULL,
16754     TRUE, FALSE,                                "pause game"
16755   },
16756   {
16757     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16758     GAME_CTRL_ID_PLAY,                          NULL,
16759     TRUE, FALSE,                                "play game"
16760   },
16761   {
16762     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16763     GAME_CTRL_ID_UNDO,                          NULL,
16764     TRUE, FALSE,                                "undo step"
16765   },
16766   {
16767     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16768     GAME_CTRL_ID_REDO,                          NULL,
16769     TRUE, FALSE,                                "redo step"
16770   },
16771   {
16772     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16773     GAME_CTRL_ID_SAVE,                          NULL,
16774     TRUE, FALSE,                                "save game"
16775   },
16776   {
16777     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16778     GAME_CTRL_ID_PAUSE2,                        NULL,
16779     TRUE, FALSE,                                "pause game"
16780   },
16781   {
16782     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16783     GAME_CTRL_ID_LOAD,                          NULL,
16784     TRUE, FALSE,                                "load game"
16785   },
16786   {
16787     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16788     GAME_CTRL_ID_RESTART,                       NULL,
16789     TRUE, FALSE,                                "restart game"
16790   },
16791   {
16792     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16793     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16794     FALSE, FALSE,                               "stop game"
16795   },
16796   {
16797     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16798     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16799     FALSE, FALSE,                               "pause game"
16800   },
16801   {
16802     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16803     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16804     FALSE, FALSE,                               "play game"
16805   },
16806   {
16807     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16808     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16809     FALSE, FALSE,                               "restart game"
16810   },
16811   {
16812     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16813     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16814     FALSE, TRUE,                                "stop game"
16815   },
16816   {
16817     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16818     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16819     FALSE, TRUE,                                "pause game"
16820   },
16821   {
16822     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16823     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16824     FALSE, TRUE,                                "restart game"
16825   },
16826   {
16827     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16828     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16829     TRUE, FALSE,                                "background music on/off"
16830   },
16831   {
16832     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16833     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16834     TRUE, FALSE,                                "sound loops on/off"
16835   },
16836   {
16837     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16838     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16839     TRUE, FALSE,                                "normal sounds on/off"
16840   },
16841   {
16842     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16843     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16844     FALSE, FALSE,                               "background music on/off"
16845   },
16846   {
16847     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16848     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16849     FALSE, FALSE,                               "sound loops on/off"
16850   },
16851   {
16852     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16853     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16854     FALSE, FALSE,                               "normal sounds on/off"
16855   }
16856 };
16857
16858 void CreateGameButtons(void)
16859 {
16860   int i;
16861
16862   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16863   {
16864     int graphic = gamebutton_info[i].graphic;
16865     struct GraphicInfo *gfx = &graphic_info[graphic];
16866     struct XY *pos = gamebutton_info[i].pos;
16867     struct GadgetInfo *gi;
16868     int button_type;
16869     boolean checked;
16870     unsigned int event_mask;
16871     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16872     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16873     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16874     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16875     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16876     int gd_x   = gfx->src_x;
16877     int gd_y   = gfx->src_y;
16878     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16879     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16880     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16881     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16882     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16883     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16884     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16885     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16886     int id = i;
16887
16888     // do not use touch buttons if overlay touch buttons are disabled
16889     if (is_touch_button && !setup.touch.overlay_buttons)
16890       continue;
16891
16892     if (gfx->bitmap == NULL)
16893     {
16894       game_gadget[id] = NULL;
16895
16896       continue;
16897     }
16898
16899     if (id == GAME_CTRL_ID_STOP ||
16900         id == GAME_CTRL_ID_PANEL_STOP ||
16901         id == GAME_CTRL_ID_TOUCH_STOP ||
16902         id == GAME_CTRL_ID_PLAY ||
16903         id == GAME_CTRL_ID_PANEL_PLAY ||
16904         id == GAME_CTRL_ID_SAVE ||
16905         id == GAME_CTRL_ID_LOAD ||
16906         id == GAME_CTRL_ID_RESTART ||
16907         id == GAME_CTRL_ID_PANEL_RESTART ||
16908         id == GAME_CTRL_ID_TOUCH_RESTART)
16909     {
16910       button_type = GD_TYPE_NORMAL_BUTTON;
16911       checked = FALSE;
16912       event_mask = GD_EVENT_RELEASED;
16913     }
16914     else if (id == GAME_CTRL_ID_UNDO ||
16915              id == GAME_CTRL_ID_REDO)
16916     {
16917       button_type = GD_TYPE_NORMAL_BUTTON;
16918       checked = FALSE;
16919       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16920     }
16921     else
16922     {
16923       button_type = GD_TYPE_CHECK_BUTTON;
16924       checked = (gamebutton_info[i].setup_value != NULL ?
16925                  *gamebutton_info[i].setup_value : FALSE);
16926       event_mask = GD_EVENT_PRESSED;
16927     }
16928
16929     gi = CreateGadget(GDI_CUSTOM_ID, id,
16930                       GDI_IMAGE_ID, graphic,
16931                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16932                       GDI_X, base_x + x,
16933                       GDI_Y, base_y + y,
16934                       GDI_WIDTH, gfx->width,
16935                       GDI_HEIGHT, gfx->height,
16936                       GDI_TYPE, button_type,
16937                       GDI_STATE, GD_BUTTON_UNPRESSED,
16938                       GDI_CHECKED, checked,
16939                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16940                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16941                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16942                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16943                       GDI_DIRECT_DRAW, FALSE,
16944                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16945                       GDI_EVENT_MASK, event_mask,
16946                       GDI_CALLBACK_ACTION, HandleGameButtons,
16947                       GDI_END);
16948
16949     if (gi == NULL)
16950       Fail("cannot create gadget");
16951
16952     game_gadget[id] = gi;
16953   }
16954 }
16955
16956 void FreeGameButtons(void)
16957 {
16958   int i;
16959
16960   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16961     FreeGadget(game_gadget[i]);
16962 }
16963
16964 static void UnmapGameButtonsAtSamePosition(int id)
16965 {
16966   int i;
16967
16968   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16969     if (i != id &&
16970         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16971         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16972       UnmapGadget(game_gadget[i]);
16973 }
16974
16975 static void UnmapGameButtonsAtSamePosition_All(void)
16976 {
16977   if (setup.show_load_save_buttons)
16978   {
16979     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16980     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16981     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16982   }
16983   else if (setup.show_undo_redo_buttons)
16984   {
16985     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16986     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16987     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16988   }
16989   else
16990   {
16991     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16992     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16993     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16994
16995     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16996     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16997     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16998   }
16999 }
17000
17001 void MapLoadSaveButtons(void)
17002 {
17003   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17004   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17005
17006   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17007   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17008 }
17009
17010 void MapUndoRedoButtons(void)
17011 {
17012   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17013   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17014
17015   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17016   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17017 }
17018
17019 void ModifyPauseButtons(void)
17020 {
17021   static int ids[] =
17022   {
17023     GAME_CTRL_ID_PAUSE,
17024     GAME_CTRL_ID_PAUSE2,
17025     GAME_CTRL_ID_PANEL_PAUSE,
17026     GAME_CTRL_ID_TOUCH_PAUSE,
17027     -1
17028   };
17029   int i;
17030
17031   // do not redraw pause button on closed door (may happen when restarting game)
17032   if (!(GetDoorState() & DOOR_OPEN_1))
17033     return;
17034
17035   for (i = 0; ids[i] > -1; i++)
17036     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17037 }
17038
17039 static void MapGameButtonsExt(boolean on_tape)
17040 {
17041   int i;
17042
17043   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17044   {
17045     if ((i == GAME_CTRL_ID_UNDO ||
17046          i == GAME_CTRL_ID_REDO) &&
17047         game_status != GAME_MODE_PLAYING)
17048       continue;
17049
17050     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17051       MapGadget(game_gadget[i]);
17052   }
17053
17054   UnmapGameButtonsAtSamePosition_All();
17055
17056   RedrawGameButtons();
17057 }
17058
17059 static void UnmapGameButtonsExt(boolean on_tape)
17060 {
17061   int i;
17062
17063   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17064     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17065       UnmapGadget(game_gadget[i]);
17066 }
17067
17068 static void RedrawGameButtonsExt(boolean on_tape)
17069 {
17070   int i;
17071
17072   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17073     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17074       RedrawGadget(game_gadget[i]);
17075 }
17076
17077 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17078 {
17079   if (gi == NULL)
17080     return;
17081
17082   gi->checked = state;
17083 }
17084
17085 static void RedrawSoundButtonGadget(int id)
17086 {
17087   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17088              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17089              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17090              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17091              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17092              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17093              id);
17094
17095   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17096   RedrawGadget(game_gadget[id2]);
17097 }
17098
17099 void MapGameButtons(void)
17100 {
17101   MapGameButtonsExt(FALSE);
17102 }
17103
17104 void UnmapGameButtons(void)
17105 {
17106   UnmapGameButtonsExt(FALSE);
17107 }
17108
17109 void RedrawGameButtons(void)
17110 {
17111   RedrawGameButtonsExt(FALSE);
17112 }
17113
17114 void MapGameButtonsOnTape(void)
17115 {
17116   MapGameButtonsExt(TRUE);
17117 }
17118
17119 void UnmapGameButtonsOnTape(void)
17120 {
17121   UnmapGameButtonsExt(TRUE);
17122 }
17123
17124 void RedrawGameButtonsOnTape(void)
17125 {
17126   RedrawGameButtonsExt(TRUE);
17127 }
17128
17129 static void GameUndoRedoExt(void)
17130 {
17131   ClearPlayerAction();
17132
17133   tape.pausing = TRUE;
17134
17135   RedrawPlayfield();
17136   UpdateAndDisplayGameControlValues();
17137
17138   DrawCompleteVideoDisplay();
17139   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17140   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17141   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17142
17143   ModifyPauseButtons();
17144
17145   BackToFront();
17146 }
17147
17148 static void GameUndo(int steps)
17149 {
17150   if (!CheckEngineSnapshotList())
17151     return;
17152
17153   int tape_property_bits = tape.property_bits;
17154
17155   LoadEngineSnapshot_Undo(steps);
17156
17157   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17158
17159   GameUndoRedoExt();
17160 }
17161
17162 static void GameRedo(int steps)
17163 {
17164   if (!CheckEngineSnapshotList())
17165     return;
17166
17167   int tape_property_bits = tape.property_bits;
17168
17169   LoadEngineSnapshot_Redo(steps);
17170
17171   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17172
17173   GameUndoRedoExt();
17174 }
17175
17176 static void HandleGameButtonsExt(int id, int button)
17177 {
17178   static boolean game_undo_executed = FALSE;
17179   int steps = BUTTON_STEPSIZE(button);
17180   boolean handle_game_buttons =
17181     (game_status == GAME_MODE_PLAYING ||
17182      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17183
17184   if (!handle_game_buttons)
17185     return;
17186
17187   switch (id)
17188   {
17189     case GAME_CTRL_ID_STOP:
17190     case GAME_CTRL_ID_PANEL_STOP:
17191     case GAME_CTRL_ID_TOUCH_STOP:
17192       TapeStopGame();
17193
17194       break;
17195
17196     case GAME_CTRL_ID_PAUSE:
17197     case GAME_CTRL_ID_PAUSE2:
17198     case GAME_CTRL_ID_PANEL_PAUSE:
17199     case GAME_CTRL_ID_TOUCH_PAUSE:
17200       if (network.enabled && game_status == GAME_MODE_PLAYING)
17201       {
17202         if (tape.pausing)
17203           SendToServer_ContinuePlaying();
17204         else
17205           SendToServer_PausePlaying();
17206       }
17207       else
17208         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17209
17210       game_undo_executed = FALSE;
17211
17212       break;
17213
17214     case GAME_CTRL_ID_PLAY:
17215     case GAME_CTRL_ID_PANEL_PLAY:
17216       if (game_status == GAME_MODE_MAIN)
17217       {
17218         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17219       }
17220       else if (tape.pausing)
17221       {
17222         if (network.enabled)
17223           SendToServer_ContinuePlaying();
17224         else
17225           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17226       }
17227       break;
17228
17229     case GAME_CTRL_ID_UNDO:
17230       // Important: When using "save snapshot when collecting an item" mode,
17231       // load last (current) snapshot for first "undo" after pressing "pause"
17232       // (else the last-but-one snapshot would be loaded, because the snapshot
17233       // pointer already points to the last snapshot when pressing "pause",
17234       // which is fine for "every step/move" mode, but not for "every collect")
17235       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17236           !game_undo_executed)
17237         steps--;
17238
17239       game_undo_executed = TRUE;
17240
17241       GameUndo(steps);
17242       break;
17243
17244     case GAME_CTRL_ID_REDO:
17245       GameRedo(steps);
17246       break;
17247
17248     case GAME_CTRL_ID_SAVE:
17249       TapeQuickSave();
17250       break;
17251
17252     case GAME_CTRL_ID_LOAD:
17253       TapeQuickLoad();
17254       break;
17255
17256     case GAME_CTRL_ID_RESTART:
17257     case GAME_CTRL_ID_PANEL_RESTART:
17258     case GAME_CTRL_ID_TOUCH_RESTART:
17259       TapeRestartGame();
17260
17261       break;
17262
17263     case SOUND_CTRL_ID_MUSIC:
17264     case SOUND_CTRL_ID_PANEL_MUSIC:
17265       if (setup.sound_music)
17266       { 
17267         setup.sound_music = FALSE;
17268
17269         FadeMusic();
17270       }
17271       else if (audio.music_available)
17272       { 
17273         setup.sound = setup.sound_music = TRUE;
17274
17275         SetAudioMode(setup.sound);
17276
17277         if (game_status == GAME_MODE_PLAYING)
17278           PlayLevelMusic();
17279       }
17280
17281       RedrawSoundButtonGadget(id);
17282
17283       break;
17284
17285     case SOUND_CTRL_ID_LOOPS:
17286     case SOUND_CTRL_ID_PANEL_LOOPS:
17287       if (setup.sound_loops)
17288         setup.sound_loops = FALSE;
17289       else if (audio.loops_available)
17290       {
17291         setup.sound = setup.sound_loops = TRUE;
17292
17293         SetAudioMode(setup.sound);
17294       }
17295
17296       RedrawSoundButtonGadget(id);
17297
17298       break;
17299
17300     case SOUND_CTRL_ID_SIMPLE:
17301     case SOUND_CTRL_ID_PANEL_SIMPLE:
17302       if (setup.sound_simple)
17303         setup.sound_simple = FALSE;
17304       else if (audio.sound_available)
17305       {
17306         setup.sound = setup.sound_simple = TRUE;
17307
17308         SetAudioMode(setup.sound);
17309       }
17310
17311       RedrawSoundButtonGadget(id);
17312
17313       break;
17314
17315     default:
17316       break;
17317   }
17318 }
17319
17320 static void HandleGameButtons(struct GadgetInfo *gi)
17321 {
17322   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17323 }
17324
17325 void HandleSoundButtonKeys(Key key)
17326 {
17327   if (key == setup.shortcut.sound_simple)
17328     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17329   else if (key == setup.shortcut.sound_loops)
17330     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17331   else if (key == setup.shortcut.sound_music)
17332     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17333 }