moved code to separate function
[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_PLAYER            ? EL_PLAYER_1 :
1845              element == EL_BD_INBOX             ? EL_PLAYER_1 :
1846              element == EL_BD_SAND              ? EL_SAND :
1847              element == EL_BD_STEELWALL         ? EL_STEELWALL :
1848              element == EL_BD_EXIT_CLOSED       ? EL_EXIT_CLOSED :
1849              element == EL_BD_EXIT_OPEN         ? EL_EXIT_OPEN :
1850              element);
1851
1852   Tile[x][y] = element;
1853 }
1854
1855 static void InitFieldForEngine(int x, int y)
1856 {
1857   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1858     InitFieldForEngine_RND(x, y);
1859 }
1860
1861 static void InitField(int x, int y, boolean init_game)
1862 {
1863   int element = Tile[x][y];
1864
1865   switch (element)
1866   {
1867     case EL_SP_MURPHY:
1868     case EL_PLAYER_1:
1869     case EL_PLAYER_2:
1870     case EL_PLAYER_3:
1871     case EL_PLAYER_4:
1872       InitPlayerField(x, y, element, init_game);
1873       break;
1874
1875     case EL_SOKOBAN_FIELD_PLAYER:
1876       element = Tile[x][y] = EL_PLAYER_1;
1877       InitField(x, y, init_game);
1878
1879       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1880       InitField(x, y, init_game);
1881       break;
1882
1883     case EL_SOKOBAN_FIELD_EMPTY:
1884       IncrementSokobanFieldsNeeded();
1885       break;
1886
1887     case EL_SOKOBAN_OBJECT:
1888       IncrementSokobanObjectsNeeded();
1889       break;
1890
1891     case EL_STONEBLOCK:
1892       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1893         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1894       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1895         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1896       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1897         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1898       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1899         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1900       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1901         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1902       break;
1903
1904     case EL_BUG:
1905     case EL_BUG_RIGHT:
1906     case EL_BUG_UP:
1907     case EL_BUG_LEFT:
1908     case EL_BUG_DOWN:
1909     case EL_SPACESHIP:
1910     case EL_SPACESHIP_RIGHT:
1911     case EL_SPACESHIP_UP:
1912     case EL_SPACESHIP_LEFT:
1913     case EL_SPACESHIP_DOWN:
1914     case EL_BD_BUTTERFLY:
1915     case EL_BD_BUTTERFLY_RIGHT:
1916     case EL_BD_BUTTERFLY_UP:
1917     case EL_BD_BUTTERFLY_LEFT:
1918     case EL_BD_BUTTERFLY_DOWN:
1919     case EL_BD_FIREFLY:
1920     case EL_BD_FIREFLY_RIGHT:
1921     case EL_BD_FIREFLY_UP:
1922     case EL_BD_FIREFLY_LEFT:
1923     case EL_BD_FIREFLY_DOWN:
1924     case EL_PACMAN_RIGHT:
1925     case EL_PACMAN_UP:
1926     case EL_PACMAN_LEFT:
1927     case EL_PACMAN_DOWN:
1928     case EL_YAMYAM:
1929     case EL_YAMYAM_LEFT:
1930     case EL_YAMYAM_RIGHT:
1931     case EL_YAMYAM_UP:
1932     case EL_YAMYAM_DOWN:
1933     case EL_DARK_YAMYAM:
1934     case EL_ROBOT:
1935     case EL_PACMAN:
1936     case EL_SP_SNIKSNAK:
1937     case EL_SP_ELECTRON:
1938     case EL_MOLE:
1939     case EL_MOLE_LEFT:
1940     case EL_MOLE_RIGHT:
1941     case EL_MOLE_UP:
1942     case EL_MOLE_DOWN:
1943     case EL_SPRING_LEFT:
1944     case EL_SPRING_RIGHT:
1945       InitMovDir(x, y);
1946       break;
1947
1948     case EL_AMOEBA_FULL:
1949     case EL_BD_AMOEBA:
1950       InitAmoebaNr(x, y);
1951       break;
1952
1953     case EL_AMOEBA_DROP:
1954       if (y == lev_fieldy - 1)
1955       {
1956         Tile[x][y] = EL_AMOEBA_GROWING;
1957         Store[x][y] = EL_AMOEBA_WET;
1958       }
1959       break;
1960
1961     case EL_DYNAMITE_ACTIVE:
1962     case EL_SP_DISK_RED_ACTIVE:
1963     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1964     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1965     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1966     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1967       MovDelay[x][y] = 96;
1968       break;
1969
1970     case EL_EM_DYNAMITE_ACTIVE:
1971       MovDelay[x][y] = 32;
1972       break;
1973
1974     case EL_LAMP:
1975       game.lights_still_needed++;
1976       break;
1977
1978     case EL_PENGUIN:
1979       game.friends_still_needed++;
1980       break;
1981
1982     case EL_PIG:
1983     case EL_DRAGON:
1984       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1985       break;
1986
1987     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1988     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1989     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1990     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1991     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1992     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1993     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1994     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1995     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1996     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1997     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1998     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1999       if (init_game)
2000       {
2001         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2002         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2003         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2004
2005         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2006         {
2007           game.belt_dir[belt_nr] = belt_dir;
2008           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2009         }
2010         else    // more than one switch -- set it like the first switch
2011         {
2012           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2013         }
2014       }
2015       break;
2016
2017     case EL_LIGHT_SWITCH_ACTIVE:
2018       if (init_game)
2019         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2020       break;
2021
2022     case EL_INVISIBLE_STEELWALL:
2023     case EL_INVISIBLE_WALL:
2024     case EL_INVISIBLE_SAND:
2025       if (game.light_time_left > 0 ||
2026           game.lenses_time_left > 0)
2027         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2028       break;
2029
2030     case EL_EMC_MAGIC_BALL:
2031       if (game.ball_active)
2032         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2033       break;
2034
2035     case EL_EMC_MAGIC_BALL_SWITCH:
2036       if (game.ball_active)
2037         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2038       break;
2039
2040     case EL_TRIGGER_PLAYER:
2041     case EL_TRIGGER_ELEMENT:
2042     case EL_TRIGGER_CE_VALUE:
2043     case EL_TRIGGER_CE_SCORE:
2044     case EL_SELF:
2045     case EL_ANY_ELEMENT:
2046     case EL_CURRENT_CE_VALUE:
2047     case EL_CURRENT_CE_SCORE:
2048     case EL_PREV_CE_1:
2049     case EL_PREV_CE_2:
2050     case EL_PREV_CE_3:
2051     case EL_PREV_CE_4:
2052     case EL_PREV_CE_5:
2053     case EL_PREV_CE_6:
2054     case EL_PREV_CE_7:
2055     case EL_PREV_CE_8:
2056     case EL_NEXT_CE_1:
2057     case EL_NEXT_CE_2:
2058     case EL_NEXT_CE_3:
2059     case EL_NEXT_CE_4:
2060     case EL_NEXT_CE_5:
2061     case EL_NEXT_CE_6:
2062     case EL_NEXT_CE_7:
2063     case EL_NEXT_CE_8:
2064       // reference elements should not be used on the playfield
2065       Tile[x][y] = EL_EMPTY;
2066       break;
2067
2068     default:
2069       if (IS_CUSTOM_ELEMENT(element))
2070       {
2071         if (CAN_MOVE(element))
2072           InitMovDir(x, y);
2073
2074         if (!element_info[element].use_last_ce_value || init_game)
2075           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2076       }
2077       else if (IS_GROUP_ELEMENT(element))
2078       {
2079         Tile[x][y] = GetElementFromGroupElement(element);
2080
2081         InitField(x, y, init_game);
2082       }
2083       else if (IS_EMPTY_ELEMENT(element))
2084       {
2085         GfxElementEmpty[x][y] = element;
2086         Tile[x][y] = EL_EMPTY;
2087
2088         if (element_info[element].use_gfx_element)
2089           game.use_masked_elements = TRUE;
2090       }
2091
2092       break;
2093   }
2094
2095   if (!init_game)
2096     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2097 }
2098
2099 static void InitField_WithBug1(int x, int y, boolean init_game)
2100 {
2101   InitField(x, y, init_game);
2102
2103   // not needed to call InitMovDir() -- already done by InitField()!
2104   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2105       CAN_MOVE(Tile[x][y]))
2106     InitMovDir(x, y);
2107 }
2108
2109 static void InitField_WithBug2(int x, int y, boolean init_game)
2110 {
2111   int old_element = Tile[x][y];
2112
2113   InitField(x, y, init_game);
2114
2115   // not needed to call InitMovDir() -- already done by InitField()!
2116   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2117       CAN_MOVE(old_element) &&
2118       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2119     InitMovDir(x, y);
2120
2121   /* this case is in fact a combination of not less than three bugs:
2122      first, it calls InitMovDir() for elements that can move, although this is
2123      already done by InitField(); then, it checks the element that was at this
2124      field _before_ the call to InitField() (which can change it); lastly, it
2125      was not called for "mole with direction" elements, which were treated as
2126      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2127   */
2128 }
2129
2130 static int get_key_element_from_nr(int key_nr)
2131 {
2132   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2133                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                           EL_EM_KEY_1 : EL_KEY_1);
2135
2136   return key_base_element + key_nr;
2137 }
2138
2139 static int get_next_dropped_element(struct PlayerInfo *player)
2140 {
2141   return (player->inventory_size > 0 ?
2142           player->inventory_element[player->inventory_size - 1] :
2143           player->inventory_infinite_element != EL_UNDEFINED ?
2144           player->inventory_infinite_element :
2145           player->dynabombs_left > 0 ?
2146           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2147           EL_UNDEFINED);
2148 }
2149
2150 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2151 {
2152   // pos >= 0: get element from bottom of the stack;
2153   // pos <  0: get element from top of the stack
2154
2155   if (pos < 0)
2156   {
2157     int min_inventory_size = -pos;
2158     int inventory_pos = player->inventory_size - min_inventory_size;
2159     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2160
2161     return (player->inventory_size >= min_inventory_size ?
2162             player->inventory_element[inventory_pos] :
2163             player->inventory_infinite_element != EL_UNDEFINED ?
2164             player->inventory_infinite_element :
2165             player->dynabombs_left >= min_dynabombs_left ?
2166             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2167             EL_UNDEFINED);
2168   }
2169   else
2170   {
2171     int min_dynabombs_left = pos + 1;
2172     int min_inventory_size = pos + 1 - player->dynabombs_left;
2173     int inventory_pos = pos - player->dynabombs_left;
2174
2175     return (player->inventory_infinite_element != EL_UNDEFINED ?
2176             player->inventory_infinite_element :
2177             player->dynabombs_left >= min_dynabombs_left ?
2178             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2179             player->inventory_size >= min_inventory_size ?
2180             player->inventory_element[inventory_pos] :
2181             EL_UNDEFINED);
2182   }
2183 }
2184
2185 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2186 {
2187   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2188   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2189   int compare_result;
2190
2191   if (gpo1->sort_priority != gpo2->sort_priority)
2192     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2193   else
2194     compare_result = gpo1->nr - gpo2->nr;
2195
2196   return compare_result;
2197 }
2198
2199 int getPlayerInventorySize(int player_nr)
2200 {
2201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202     return game_em.ply[player_nr]->dynamite;
2203   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2204     return game_sp.red_disk_count;
2205   else
2206     return stored_player[player_nr].inventory_size;
2207 }
2208
2209 static void InitGameControlValues(void)
2210 {
2211   int i;
2212
2213   for (i = 0; game_panel_controls[i].nr != -1; i++)
2214   {
2215     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2216     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2217     struct TextPosInfo *pos = gpc->pos;
2218     int nr = gpc->nr;
2219     int type = gpc->type;
2220
2221     if (nr != i)
2222     {
2223       Error("'game_panel_controls' structure corrupted at %d", i);
2224
2225       Fail("this should not happen -- please debug");
2226     }
2227
2228     // force update of game controls after initialization
2229     gpc->value = gpc->last_value = -1;
2230     gpc->frame = gpc->last_frame = -1;
2231     gpc->gfx_frame = -1;
2232
2233     // determine panel value width for later calculation of alignment
2234     if (type == TYPE_INTEGER || type == TYPE_STRING)
2235     {
2236       pos->width = pos->size * getFontWidth(pos->font);
2237       pos->height = getFontHeight(pos->font);
2238     }
2239     else if (type == TYPE_ELEMENT)
2240     {
2241       pos->width = pos->size;
2242       pos->height = pos->size;
2243     }
2244
2245     // fill structure for game panel draw order
2246     gpo->nr = gpc->nr;
2247     gpo->sort_priority = pos->sort_priority;
2248   }
2249
2250   // sort game panel controls according to sort_priority and control number
2251   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2252         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2253 }
2254
2255 static void UpdatePlayfieldElementCount(void)
2256 {
2257   boolean use_element_count = FALSE;
2258   int i, j, x, y;
2259
2260   // first check if it is needed at all to calculate playfield element count
2261   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2262     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2263       use_element_count = TRUE;
2264
2265   if (!use_element_count)
2266     return;
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269     element_info[i].element_count = 0;
2270
2271   SCAN_PLAYFIELD(x, y)
2272   {
2273     element_info[Tile[x][y]].element_count++;
2274   }
2275
2276   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2277     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2278       if (IS_IN_GROUP(j, i))
2279         element_info[EL_GROUP_START + i].element_count +=
2280           element_info[j].element_count;
2281 }
2282
2283 static void UpdateGameControlValues(void)
2284 {
2285   int i, k;
2286   int time = (game.LevelSolved ?
2287               game.LevelSolved_CountingTime :
2288               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2289               game_bd.time_played :
2290               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2291               game_em.lev->time :
2292               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2293               game_sp.time_played :
2294               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2295               game_mm.energy_left :
2296               game.no_level_time_limit ? TimePlayed : TimeLeft);
2297   int score = (game.LevelSolved ?
2298                game.LevelSolved_CountingScore :
2299                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2300                game_bd.score :
2301                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2302                game_em.lev->score :
2303                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2304                game_sp.score :
2305                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2306                game_mm.score :
2307                game.score);
2308   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2309               game_bd.gems_still_needed :
2310               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2311               game_em.lev->gems_needed :
2312               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2313               game_sp.infotrons_still_needed :
2314               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2315               game_mm.kettles_still_needed :
2316               game.gems_still_needed);
2317   int gems_needed = level.gems_needed;
2318   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2319                         game_bd.game->cave->diamonds_collected :
2320                         gems_needed - gems);
2321   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2322                     game_bd.game->cave->diamond_value :
2323                     level.score[SC_EMERALD]);
2324   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2325                      game_bd.gems_still_needed > 0 :
2326                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2327                      game_em.lev->gems_needed > 0 :
2328                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2329                      game_sp.infotrons_still_needed > 0 :
2330                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2331                      game_mm.kettles_still_needed > 0 ||
2332                      game_mm.lights_still_needed > 0 :
2333                      game.gems_still_needed > 0 ||
2334                      game.sokoban_fields_still_needed > 0 ||
2335                      game.sokoban_objects_still_needed > 0 ||
2336                      game.lights_still_needed > 0);
2337   int health = (game.LevelSolved ?
2338                 game.LevelSolved_CountingHealth :
2339                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2340                 MM_HEALTH(game_mm.laser_overload_value) :
2341                 game.health);
2342   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2343
2344   UpdatePlayfieldElementCount();
2345
2346   // update game panel control values
2347
2348   // used instead of "level_nr" (for network games)
2349   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2350   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2351   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2352   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2353   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2354
2355   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2356   for (i = 0; i < MAX_NUM_KEYS; i++)
2357     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2358   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2359   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2360
2361   if (game.centered_player_nr == -1)
2362   {
2363     for (i = 0; i < MAX_PLAYERS; i++)
2364     {
2365       // only one player in Supaplex game engine
2366       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2367         break;
2368
2369       for (k = 0; k < MAX_NUM_KEYS; k++)
2370       {
2371         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2372         {
2373           if (game_em.ply[i]->keys & (1 << k))
2374             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2375               get_key_element_from_nr(k);
2376         }
2377         else if (stored_player[i].key[k])
2378           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2379             get_key_element_from_nr(k);
2380       }
2381
2382       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2383         getPlayerInventorySize(i);
2384
2385       if (stored_player[i].num_white_keys > 0)
2386         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2387           EL_DC_KEY_WHITE;
2388
2389       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2390         stored_player[i].num_white_keys;
2391     }
2392   }
2393   else
2394   {
2395     int player_nr = game.centered_player_nr;
2396
2397     for (k = 0; k < MAX_NUM_KEYS; k++)
2398     {
2399       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2400       {
2401         if (game_em.ply[player_nr]->keys & (1 << k))
2402           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2403             get_key_element_from_nr(k);
2404       }
2405       else if (stored_player[player_nr].key[k])
2406         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2407           get_key_element_from_nr(k);
2408     }
2409
2410     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2411       getPlayerInventorySize(player_nr);
2412
2413     if (stored_player[player_nr].num_white_keys > 0)
2414       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2415
2416     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2417       stored_player[player_nr].num_white_keys;
2418   }
2419
2420   // re-arrange keys on game panel, if needed or if defined by style settings
2421   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2422   {
2423     int nr = GAME_PANEL_KEY_1 + i;
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2425     struct TextPosInfo *pos = gpc->pos;
2426
2427     // skip check if key is not in the player's inventory
2428     if (gpc->value == EL_EMPTY)
2429       continue;
2430
2431     // check if keys should be arranged on panel from left to right
2432     if (pos->style == STYLE_LEFTMOST_POSITION)
2433     {
2434       // check previous key positions (left from current key)
2435       for (k = 0; k < i; k++)
2436       {
2437         int nr_new = GAME_PANEL_KEY_1 + k;
2438
2439         if (game_panel_controls[nr_new].value == EL_EMPTY)
2440         {
2441           game_panel_controls[nr_new].value = gpc->value;
2442           gpc->value = EL_EMPTY;
2443
2444           break;
2445         }
2446       }
2447     }
2448
2449     // check if "undefined" keys can be placed at some other position
2450     if (pos->x == -1 && pos->y == -1)
2451     {
2452       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2453
2454       // 1st try: display key at the same position as normal or EM keys
2455       if (game_panel_controls[nr_new].value == EL_EMPTY)
2456       {
2457         game_panel_controls[nr_new].value = gpc->value;
2458       }
2459       else
2460       {
2461         // 2nd try: display key at the next free position in the key panel
2462         for (k = 0; k < STD_NUM_KEYS; k++)
2463         {
2464           nr_new = GAME_PANEL_KEY_1 + k;
2465
2466           if (game_panel_controls[nr_new].value == EL_EMPTY)
2467           {
2468             game_panel_controls[nr_new].value = gpc->value;
2469
2470             break;
2471           }
2472         }
2473       }
2474     }
2475   }
2476
2477   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2478   {
2479     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2480       get_inventory_element_from_pos(local_player, i);
2481     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2482       get_inventory_element_from_pos(local_player, -i - 1);
2483   }
2484
2485   game_panel_controls[GAME_PANEL_SCORE].value = score;
2486   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2487
2488   game_panel_controls[GAME_PANEL_TIME].value = time;
2489
2490   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2491   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2492   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2493
2494   if (level.time == 0)
2495     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2496   else
2497     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2498
2499   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2500   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2501
2502   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2503
2504   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2505     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2506      EL_EMPTY);
2507   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2508     local_player->shield_normal_time_left;
2509   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2510     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2511      EL_EMPTY);
2512   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2513     local_player->shield_deadly_time_left;
2514
2515   game_panel_controls[GAME_PANEL_EXIT].value =
2516     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2517
2518   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2519     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2520   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2521     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2522      EL_EMC_MAGIC_BALL_SWITCH);
2523
2524   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2525     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2526   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2527     game.light_time_left;
2528
2529   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2530     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2531   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2532     game.timegate_time_left;
2533
2534   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2535     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2536
2537   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2538     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2539   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2540     game.lenses_time_left;
2541
2542   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2543     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2544   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2545     game.magnify_time_left;
2546
2547   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2548     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2549      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2550      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2551      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2552      EL_BALLOON_SWITCH_NONE);
2553
2554   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2555     local_player->dynabomb_count;
2556   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2557     local_player->dynabomb_size;
2558   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2559     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2560
2561   game_panel_controls[GAME_PANEL_PENGUINS].value =
2562     game.friends_still_needed;
2563
2564   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2565     game.sokoban_objects_still_needed;
2566   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2567     game.sokoban_fields_still_needed;
2568
2569   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2570     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2571
2572   for (i = 0; i < NUM_BELTS; i++)
2573   {
2574     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2575       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2576        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2577     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2578       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2579   }
2580
2581   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2582     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2583   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2584     game.magic_wall_time_left;
2585
2586   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2587     local_player->gravity;
2588
2589   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2590     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2591
2592   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2593     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2594       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2595        game.panel.element[i].id : EL_UNDEFINED);
2596
2597   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2598     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2599       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2600        element_info[game.panel.element_count[i].id].element_count : 0);
2601
2602   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2603     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2604       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2605        element_info[game.panel.ce_score[i].id].collect_score : 0);
2606
2607   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2608     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2609       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2610        element_info[game.panel.ce_score_element[i].id].collect_score :
2611        EL_UNDEFINED);
2612
2613   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2614   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2615   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2616
2617   // update game panel control frames
2618
2619   for (i = 0; game_panel_controls[i].nr != -1; i++)
2620   {
2621     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2622
2623     if (gpc->type == TYPE_ELEMENT)
2624     {
2625       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2626       {
2627         int last_anim_random_frame = gfx.anim_random_frame;
2628         int element = gpc->value;
2629         int graphic = el2panelimg(element);
2630         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2631                                sync_random_frame :
2632                                graphic_info[graphic].anim_global_anim_sync ?
2633                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2634
2635         if (gpc->value != gpc->last_value)
2636         {
2637           gpc->gfx_frame = 0;
2638           gpc->gfx_random = init_gfx_random;
2639         }
2640         else
2641         {
2642           gpc->gfx_frame++;
2643
2644           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2645               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2646             gpc->gfx_random = init_gfx_random;
2647         }
2648
2649         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2650           gfx.anim_random_frame = gpc->gfx_random;
2651
2652         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2653           gpc->gfx_frame = element_info[element].collect_score;
2654
2655         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2656
2657         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2658           gfx.anim_random_frame = last_anim_random_frame;
2659       }
2660     }
2661     else if (gpc->type == TYPE_GRAPHIC)
2662     {
2663       if (gpc->graphic != IMG_UNDEFINED)
2664       {
2665         int last_anim_random_frame = gfx.anim_random_frame;
2666         int graphic = gpc->graphic;
2667         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2668                                sync_random_frame :
2669                                graphic_info[graphic].anim_global_anim_sync ?
2670                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2671
2672         if (gpc->value != gpc->last_value)
2673         {
2674           gpc->gfx_frame = 0;
2675           gpc->gfx_random = init_gfx_random;
2676         }
2677         else
2678         {
2679           gpc->gfx_frame++;
2680
2681           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2682               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2683             gpc->gfx_random = init_gfx_random;
2684         }
2685
2686         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2687           gfx.anim_random_frame = gpc->gfx_random;
2688
2689         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2690
2691         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2692           gfx.anim_random_frame = last_anim_random_frame;
2693       }
2694     }
2695   }
2696 }
2697
2698 static void DisplayGameControlValues(void)
2699 {
2700   boolean redraw_panel = FALSE;
2701   int i;
2702
2703   for (i = 0; game_panel_controls[i].nr != -1; i++)
2704   {
2705     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2706
2707     if (PANEL_DEACTIVATED(gpc->pos))
2708       continue;
2709
2710     if (gpc->value == gpc->last_value &&
2711         gpc->frame == gpc->last_frame)
2712       continue;
2713
2714     redraw_panel = TRUE;
2715   }
2716
2717   if (!redraw_panel)
2718     return;
2719
2720   // copy default game door content to main double buffer
2721
2722   // !!! CHECK AGAIN !!!
2723   SetPanelBackground();
2724   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2725   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2726
2727   // redraw game control buttons
2728   RedrawGameButtons();
2729
2730   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2731
2732   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2733   {
2734     int nr = game_panel_order[i].nr;
2735     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2736     struct TextPosInfo *pos = gpc->pos;
2737     int type = gpc->type;
2738     int value = gpc->value;
2739     int frame = gpc->frame;
2740     int size = pos->size;
2741     int font = pos->font;
2742     boolean draw_masked = pos->draw_masked;
2743     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2744
2745     if (PANEL_DEACTIVATED(pos))
2746       continue;
2747
2748     if (pos->class == get_hash_from_string("extra_panel_items") &&
2749         !setup.prefer_extra_panel_items)
2750       continue;
2751
2752     gpc->last_value = value;
2753     gpc->last_frame = frame;
2754
2755     if (type == TYPE_INTEGER)
2756     {
2757       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2758           nr == GAME_PANEL_INVENTORY_COUNT ||
2759           nr == GAME_PANEL_SCORE ||
2760           nr == GAME_PANEL_HIGHSCORE ||
2761           nr == GAME_PANEL_TIME)
2762       {
2763         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2764
2765         if (use_dynamic_size)           // use dynamic number of digits
2766         {
2767           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2768                               nr == GAME_PANEL_INVENTORY_COUNT ||
2769                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2770           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2771                           nr == GAME_PANEL_INVENTORY_COUNT ||
2772                           nr == GAME_PANEL_TIME ? 1 : 2);
2773           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2774                        nr == GAME_PANEL_INVENTORY_COUNT ||
2775                        nr == GAME_PANEL_TIME ? 3 : 5);
2776           int size2 = size1 + size_add;
2777           int font1 = pos->font;
2778           int font2 = pos->font_alt;
2779
2780           size = (value < value_change ? size1 : size2);
2781           font = (value < value_change ? font1 : font2);
2782         }
2783       }
2784
2785       // correct text size if "digits" is zero or less
2786       if (size <= 0)
2787         size = strlen(int2str(value, size));
2788
2789       // dynamically correct text alignment
2790       pos->width = size * getFontWidth(font);
2791
2792       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2793                   int2str(value, size), font, mask_mode);
2794     }
2795     else if (type == TYPE_ELEMENT)
2796     {
2797       int element, graphic;
2798       Bitmap *src_bitmap;
2799       int src_x, src_y;
2800       int width, height;
2801       int dst_x = PANEL_XPOS(pos);
2802       int dst_y = PANEL_YPOS(pos);
2803
2804       if (value != EL_UNDEFINED && value != EL_EMPTY)
2805       {
2806         element = value;
2807         graphic = el2panelimg(value);
2808
2809 #if 0
2810         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2811               element, EL_NAME(element), size);
2812 #endif
2813
2814         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2815           size = TILESIZE;
2816
2817         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2818                               &src_x, &src_y);
2819
2820         width  = graphic_info[graphic].width  * size / TILESIZE;
2821         height = graphic_info[graphic].height * size / TILESIZE;
2822
2823         if (draw_masked)
2824           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2825                            dst_x, dst_y);
2826         else
2827           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2828                      dst_x, dst_y);
2829       }
2830     }
2831     else if (type == TYPE_GRAPHIC)
2832     {
2833       int graphic        = gpc->graphic;
2834       int graphic_active = gpc->graphic_active;
2835       Bitmap *src_bitmap;
2836       int src_x, src_y;
2837       int width, height;
2838       int dst_x = PANEL_XPOS(pos);
2839       int dst_y = PANEL_YPOS(pos);
2840       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2841                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2842
2843       if (graphic != IMG_UNDEFINED && !skip)
2844       {
2845         if (pos->style == STYLE_REVERSE)
2846           value = 100 - value;
2847
2848         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2849
2850         if (pos->direction & MV_HORIZONTAL)
2851         {
2852           width  = graphic_info[graphic_active].width * value / 100;
2853           height = graphic_info[graphic_active].height;
2854
2855           if (pos->direction == MV_LEFT)
2856           {
2857             src_x += graphic_info[graphic_active].width - width;
2858             dst_x += graphic_info[graphic_active].width - width;
2859           }
2860         }
2861         else
2862         {
2863           width  = graphic_info[graphic_active].width;
2864           height = graphic_info[graphic_active].height * value / 100;
2865
2866           if (pos->direction == MV_UP)
2867           {
2868             src_y += graphic_info[graphic_active].height - height;
2869             dst_y += graphic_info[graphic_active].height - height;
2870           }
2871         }
2872
2873         if (draw_masked)
2874           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2875                            dst_x, dst_y);
2876         else
2877           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2878                      dst_x, dst_y);
2879
2880         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2881
2882         if (pos->direction & MV_HORIZONTAL)
2883         {
2884           if (pos->direction == MV_RIGHT)
2885           {
2886             src_x += width;
2887             dst_x += width;
2888           }
2889           else
2890           {
2891             dst_x = PANEL_XPOS(pos);
2892           }
2893
2894           width = graphic_info[graphic].width - width;
2895         }
2896         else
2897         {
2898           if (pos->direction == MV_DOWN)
2899           {
2900             src_y += height;
2901             dst_y += height;
2902           }
2903           else
2904           {
2905             dst_y = PANEL_YPOS(pos);
2906           }
2907
2908           height = graphic_info[graphic].height - height;
2909         }
2910
2911         if (draw_masked)
2912           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2913                            dst_x, dst_y);
2914         else
2915           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2916                      dst_x, dst_y);
2917       }
2918     }
2919     else if (type == TYPE_STRING)
2920     {
2921       boolean active = (value != 0);
2922       char *state_normal = "off";
2923       char *state_active = "on";
2924       char *state = (active ? state_active : state_normal);
2925       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2926                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2927                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2928                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2929
2930       if (nr == GAME_PANEL_GRAVITY_STATE)
2931       {
2932         int font1 = pos->font;          // (used for normal state)
2933         int font2 = pos->font_alt;      // (used for active state)
2934
2935         font = (active ? font2 : font1);
2936       }
2937
2938       if (s != NULL)
2939       {
2940         char *s_cut;
2941
2942         if (size <= 0)
2943         {
2944           // don't truncate output if "chars" is zero or less
2945           size = strlen(s);
2946
2947           // dynamically correct text alignment
2948           pos->width = size * getFontWidth(font);
2949         }
2950
2951         s_cut = getStringCopyN(s, size);
2952
2953         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2954                     s_cut, font, mask_mode);
2955
2956         free(s_cut);
2957       }
2958     }
2959
2960     redraw_mask |= REDRAW_DOOR_1;
2961   }
2962
2963   SetGameStatus(GAME_MODE_PLAYING);
2964 }
2965
2966 void UpdateAndDisplayGameControlValues(void)
2967 {
2968   if (tape.deactivate_display)
2969     return;
2970
2971   UpdateGameControlValues();
2972   DisplayGameControlValues();
2973 }
2974
2975 void UpdateGameDoorValues(void)
2976 {
2977   UpdateGameControlValues();
2978 }
2979
2980 void DrawGameDoorValues(void)
2981 {
2982   DisplayGameControlValues();
2983 }
2984
2985
2986 // ============================================================================
2987 // InitGameEngine()
2988 // ----------------------------------------------------------------------------
2989 // initialize game engine due to level / tape version number
2990 // ============================================================================
2991
2992 static void InitGameEngine(void)
2993 {
2994   int i, j, k, l, x, y;
2995
2996   // set game engine from tape file when re-playing, else from level file
2997   game.engine_version = (tape.playing ? tape.engine_version :
2998                          level.game_version);
2999
3000   // set single or multi-player game mode (needed for re-playing tapes)
3001   game.team_mode = setup.team_mode;
3002
3003   if (tape.playing)
3004   {
3005     int num_players = 0;
3006
3007     for (i = 0; i < MAX_PLAYERS; i++)
3008       if (tape.player_participates[i])
3009         num_players++;
3010
3011     // multi-player tapes contain input data for more than one player
3012     game.team_mode = (num_players > 1);
3013   }
3014
3015 #if 0
3016   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3017         level.game_version);
3018   Debug("game:init:level", "          tape.file_version   == %06d",
3019         tape.file_version);
3020   Debug("game:init:level", "          tape.game_version   == %06d",
3021         tape.game_version);
3022   Debug("game:init:level", "          tape.engine_version == %06d",
3023         tape.engine_version);
3024   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3025         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3026 #endif
3027
3028   // --------------------------------------------------------------------------
3029   // set flags for bugs and changes according to active game engine version
3030   // --------------------------------------------------------------------------
3031
3032   /*
3033     Summary of bugfix:
3034     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3035
3036     Bug was introduced in version:
3037     2.0.1
3038
3039     Bug was fixed in version:
3040     4.2.0.0
3041
3042     Description:
3043     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3044     but the property "can fall" was missing, which caused some levels to be
3045     unsolvable. This was fixed in version 4.2.0.0.
3046
3047     Affected levels/tapes:
3048     An example for a tape that was fixed by this bugfix is tape 029 from the
3049     level set "rnd_sam_bateman".
3050     The wrong behaviour will still be used for all levels or tapes that were
3051     created/recorded with it. An example for this is tape 023 from the level
3052     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3053   */
3054
3055   boolean use_amoeba_dropping_cannot_fall_bug =
3056     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3057       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3058      (tape.playing &&
3059       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3060       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3061
3062   /*
3063     Summary of bugfix/change:
3064     Fixed move speed of elements entering or leaving magic wall.
3065
3066     Fixed/changed in version:
3067     2.0.1
3068
3069     Description:
3070     Before 2.0.1, move speed of elements entering or leaving magic wall was
3071     twice as fast as it is now.
3072     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3073
3074     Affected levels/tapes:
3075     The first condition is generally needed for all levels/tapes before version
3076     2.0.1, which might use the old behaviour before it was changed; known tapes
3077     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3078     The second condition is an exception from the above case and is needed for
3079     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3080     above, but before it was known that this change would break tapes like the
3081     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3082     although the engine version while recording maybe was before 2.0.1. There
3083     are a lot of tapes that are affected by this exception, like tape 006 from
3084     the level set "rnd_conor_mancone".
3085   */
3086
3087   boolean use_old_move_stepsize_for_magic_wall =
3088     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3089      !(tape.playing &&
3090        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3091        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3092
3093   /*
3094     Summary of bugfix/change:
3095     Fixed handling for custom elements that change when pushed by the player.
3096
3097     Fixed/changed in version:
3098     3.1.0
3099
3100     Description:
3101     Before 3.1.0, custom elements that "change when pushing" changed directly
3102     after the player started pushing them (until then handled in "DigField()").
3103     Since 3.1.0, these custom elements are not changed until the "pushing"
3104     move of the element is finished (now handled in "ContinueMoving()").
3105
3106     Affected levels/tapes:
3107     The first condition is generally needed for all levels/tapes before version
3108     3.1.0, which might use the old behaviour before it was changed; known tapes
3109     that are affected are some tapes from the level set "Walpurgis Gardens" by
3110     Jamie Cullen.
3111     The second condition is an exception from the above case and is needed for
3112     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3113     above (including some development versions of 3.1.0), but before it was
3114     known that this change would break tapes like the above and was fixed in
3115     3.1.1, so that the changed behaviour was active although the engine version
3116     while recording maybe was before 3.1.0. There is at least one tape that is
3117     affected by this exception, which is the tape for the one-level set "Bug
3118     Machine" by Juergen Bonhagen.
3119   */
3120
3121   game.use_change_when_pushing_bug =
3122     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3123      !(tape.playing &&
3124        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3125        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3126
3127   /*
3128     Summary of bugfix/change:
3129     Fixed handling for blocking the field the player leaves when moving.
3130
3131     Fixed/changed in version:
3132     3.1.1
3133
3134     Description:
3135     Before 3.1.1, when "block last field when moving" was enabled, the field
3136     the player is leaving when moving was blocked for the time of the move,
3137     and was directly unblocked afterwards. This resulted in the last field
3138     being blocked for exactly one less than the number of frames of one player
3139     move. Additionally, even when blocking was disabled, the last field was
3140     blocked for exactly one frame.
3141     Since 3.1.1, due to changes in player movement handling, the last field
3142     is not blocked at all when blocking is disabled. When blocking is enabled,
3143     the last field is blocked for exactly the number of frames of one player
3144     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3145     last field is blocked for exactly one more than the number of frames of
3146     one player move.
3147
3148     Affected levels/tapes:
3149     (!!! yet to be determined -- probably many !!!)
3150   */
3151
3152   game.use_block_last_field_bug =
3153     (game.engine_version < VERSION_IDENT(3,1,1,0));
3154
3155   /* various special flags and settings for native Emerald Mine game engine */
3156
3157   game_em.use_single_button =
3158     (game.engine_version > VERSION_IDENT(4,0,0,2));
3159
3160   game_em.use_push_delay =
3161     (game.engine_version > VERSION_IDENT(4,3,7,1));
3162
3163   game_em.use_snap_key_bug =
3164     (game.engine_version < VERSION_IDENT(4,0,1,0));
3165
3166   game_em.use_random_bug =
3167     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3168
3169   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3170
3171   game_em.use_old_explosions            = use_old_em_engine;
3172   game_em.use_old_android               = use_old_em_engine;
3173   game_em.use_old_push_elements         = use_old_em_engine;
3174   game_em.use_old_push_into_acid        = use_old_em_engine;
3175
3176   game_em.use_wrap_around               = !use_old_em_engine;
3177
3178   // --------------------------------------------------------------------------
3179
3180   // set maximal allowed number of custom element changes per game frame
3181   game.max_num_changes_per_frame = 1;
3182
3183   // default scan direction: scan playfield from top/left to bottom/right
3184   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3185
3186   // dynamically adjust element properties according to game engine version
3187   InitElementPropertiesEngine(game.engine_version);
3188
3189   // ---------- initialize special element properties -------------------------
3190
3191   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3192   if (use_amoeba_dropping_cannot_fall_bug)
3193     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3194
3195   // ---------- initialize player's initial move delay ------------------------
3196
3197   // dynamically adjust player properties according to level information
3198   for (i = 0; i < MAX_PLAYERS; i++)
3199     game.initial_move_delay_value[i] =
3200       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3201
3202   // dynamically adjust player properties according to game engine version
3203   for (i = 0; i < MAX_PLAYERS; i++)
3204     game.initial_move_delay[i] =
3205       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3206        game.initial_move_delay_value[i] : 0);
3207
3208   // ---------- initialize player's initial push delay ------------------------
3209
3210   // dynamically adjust player properties according to game engine version
3211   game.initial_push_delay_value =
3212     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3213
3214   // ---------- initialize changing elements ----------------------------------
3215
3216   // initialize changing elements information
3217   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[i];
3220
3221     // this pointer might have been changed in the level editor
3222     ei->change = &ei->change_page[0];
3223
3224     if (!IS_CUSTOM_ELEMENT(i))
3225     {
3226       ei->change->target_element = EL_EMPTY_SPACE;
3227       ei->change->delay_fixed = 0;
3228       ei->change->delay_random = 0;
3229       ei->change->delay_frames = 1;
3230     }
3231
3232     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3233     {
3234       ei->has_change_event[j] = FALSE;
3235
3236       ei->event_page_nr[j] = 0;
3237       ei->event_page[j] = &ei->change_page[0];
3238     }
3239   }
3240
3241   // add changing elements from pre-defined list
3242   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3243   {
3244     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3245     struct ElementInfo *ei = &element_info[ch_delay->element];
3246
3247     ei->change->target_element       = ch_delay->target_element;
3248     ei->change->delay_fixed          = ch_delay->change_delay;
3249
3250     ei->change->pre_change_function  = ch_delay->pre_change_function;
3251     ei->change->change_function      = ch_delay->change_function;
3252     ei->change->post_change_function = ch_delay->post_change_function;
3253
3254     ei->change->can_change = TRUE;
3255     ei->change->can_change_or_has_action = TRUE;
3256
3257     ei->has_change_event[CE_DELAY] = TRUE;
3258
3259     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3260     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3261   }
3262
3263   // ---------- initialize if element can trigger global animations -----------
3264
3265   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3266   {
3267     struct ElementInfo *ei = &element_info[i];
3268
3269     ei->has_anim_event = FALSE;
3270   }
3271
3272   InitGlobalAnimEventsForCustomElements();
3273
3274   // ---------- initialize internal run-time variables ------------------------
3275
3276   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       ei->change_page[j].can_change_or_has_action =
3283         (ei->change_page[j].can_change |
3284          ei->change_page[j].has_action);
3285     }
3286   }
3287
3288   // add change events from custom element configuration
3289   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3290   {
3291     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3292
3293     for (j = 0; j < ei->num_change_pages; j++)
3294     {
3295       if (!ei->change_page[j].can_change_or_has_action)
3296         continue;
3297
3298       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3299       {
3300         // only add event page for the first page found with this event
3301         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3302         {
3303           ei->has_change_event[k] = TRUE;
3304
3305           ei->event_page_nr[k] = j;
3306           ei->event_page[k] = &ei->change_page[j];
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize reference elements in change conditions ------------
3313
3314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3315   {
3316     int element = EL_CUSTOM_START + i;
3317     struct ElementInfo *ei = &element_info[element];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       int trigger_element = ei->change_page[j].initial_trigger_element;
3322
3323       if (trigger_element >= EL_PREV_CE_8 &&
3324           trigger_element <= EL_NEXT_CE_8)
3325         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3326
3327       ei->change_page[j].trigger_element = trigger_element;
3328     }
3329   }
3330
3331   // ---------- initialize run-time trigger player and element ----------------
3332
3333   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334   {
3335     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336
3337     for (j = 0; j < ei->num_change_pages; j++)
3338     {
3339       struct ElementChangeInfo *change = &ei->change_page[j];
3340
3341       change->actual_trigger_element = EL_EMPTY;
3342       change->actual_trigger_player = EL_EMPTY;
3343       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3344       change->actual_trigger_side = CH_SIDE_NONE;
3345       change->actual_trigger_ce_value = 0;
3346       change->actual_trigger_ce_score = 0;
3347       change->actual_trigger_x = -1;
3348       change->actual_trigger_y = -1;
3349     }
3350   }
3351
3352   // ---------- initialize trigger events -------------------------------------
3353
3354   // initialize trigger events information
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3357       trigger_events[i][j] = FALSE;
3358
3359   // add trigger events from element change event properties
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361   {
3362     struct ElementInfo *ei = &element_info[i];
3363
3364     for (j = 0; j < ei->num_change_pages; j++)
3365     {
3366       struct ElementChangeInfo *change = &ei->change_page[j];
3367
3368       if (!change->can_change_or_has_action)
3369         continue;
3370
3371       if (change->has_event[CE_BY_OTHER_ACTION])
3372       {
3373         int trigger_element = change->trigger_element;
3374
3375         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3376         {
3377           if (change->has_event[k])
3378           {
3379             if (IS_GROUP_ELEMENT(trigger_element))
3380             {
3381               struct ElementGroupInfo *group =
3382                 element_info[trigger_element].group;
3383
3384               for (l = 0; l < group->num_elements_resolved; l++)
3385                 trigger_events[group->element_resolved[l]][k] = TRUE;
3386             }
3387             else if (trigger_element == EL_ANY_ELEMENT)
3388               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3389                 trigger_events[l][k] = TRUE;
3390             else
3391               trigger_events[trigger_element][k] = TRUE;
3392           }
3393         }
3394       }
3395     }
3396   }
3397
3398   // ---------- initialize push delay -----------------------------------------
3399
3400   // initialize push delay values to default
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402   {
3403     if (!IS_CUSTOM_ELEMENT(i))
3404     {
3405       // set default push delay values (corrected since version 3.0.7-1)
3406       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3407       {
3408         element_info[i].push_delay_fixed = 2;
3409         element_info[i].push_delay_random = 8;
3410       }
3411       else
3412       {
3413         element_info[i].push_delay_fixed = 8;
3414         element_info[i].push_delay_random = 8;
3415       }
3416     }
3417   }
3418
3419   // set push delay value for certain elements from pre-defined list
3420   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3421   {
3422     int e = push_delay_list[i].element;
3423
3424     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3425     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3426   }
3427
3428   // set push delay value for Supaplex elements for newer engine versions
3429   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3430   {
3431     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432     {
3433       if (IS_SP_ELEMENT(i))
3434       {
3435         // set SP push delay to just enough to push under a falling zonk
3436         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3437
3438         element_info[i].push_delay_fixed  = delay;
3439         element_info[i].push_delay_random = 0;
3440       }
3441     }
3442   }
3443
3444   // ---------- initialize move stepsize --------------------------------------
3445
3446   // initialize move stepsize values to default
3447   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3448     if (!IS_CUSTOM_ELEMENT(i))
3449       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3450
3451   // set move stepsize value for certain elements from pre-defined list
3452   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3453   {
3454     int e = move_stepsize_list[i].element;
3455
3456     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3457
3458     // set move stepsize value for certain elements for older engine versions
3459     if (use_old_move_stepsize_for_magic_wall)
3460     {
3461       if (e == EL_MAGIC_WALL_FILLING ||
3462           e == EL_MAGIC_WALL_EMPTYING ||
3463           e == EL_BD_MAGIC_WALL_FILLING ||
3464           e == EL_BD_MAGIC_WALL_EMPTYING)
3465         element_info[e].move_stepsize *= 2;
3466     }
3467   }
3468
3469   // ---------- initialize collect score --------------------------------------
3470
3471   // initialize collect score values for custom elements from initial value
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473     if (IS_CUSTOM_ELEMENT(i))
3474       element_info[i].collect_score = element_info[i].collect_score_initial;
3475
3476   // ---------- initialize collect count --------------------------------------
3477
3478   // initialize collect count values for non-custom elements
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (!IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_count_initial = 0;
3482
3483   // add collect count values for all elements from pre-defined list
3484   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485     element_info[collect_count_list[i].element].collect_count_initial =
3486       collect_count_list[i].count;
3487
3488   // ---------- initialize access direction -----------------------------------
3489
3490   // initialize access direction values to default (access from every side)
3491   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492     if (!IS_CUSTOM_ELEMENT(i))
3493       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3494
3495   // set access direction value for certain elements from pre-defined list
3496   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497     element_info[access_direction_list[i].element].access_direction =
3498       access_direction_list[i].direction;
3499
3500   // ---------- initialize explosion content ----------------------------------
3501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502   {
3503     if (IS_CUSTOM_ELEMENT(i))
3504       continue;
3505
3506     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3507     {
3508       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3509
3510       element_info[i].content.e[x][y] =
3511         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513          i == EL_PLAYER_3 ? EL_EMERALD :
3514          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515          i == EL_MOLE ? EL_EMERALD_RED :
3516          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521          i == EL_WALL_EMERALD ? EL_EMERALD :
3522          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527          i == EL_WALL_PEARL ? EL_PEARL :
3528          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3529          EL_EMPTY);
3530     }
3531   }
3532
3533   // ---------- initialize recursion detection --------------------------------
3534   recursion_loop_depth = 0;
3535   recursion_loop_detected = FALSE;
3536   recursion_loop_element = EL_UNDEFINED;
3537
3538   // ---------- initialize graphics engine ------------------------------------
3539   game.scroll_delay_value =
3540     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3542      !setup.forced_scroll_delay           ? 0 :
3543      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3544   if (game.forced_scroll_delay_value == -1)
3545     game.scroll_delay_value =
3546       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3547
3548   // ---------- initialize game engine snapshots ------------------------------
3549   for (i = 0; i < MAX_PLAYERS; i++)
3550     game.snapshot.last_action[i] = 0;
3551   game.snapshot.changed_action = FALSE;
3552   game.snapshot.collected_item = FALSE;
3553   game.snapshot.mode =
3554     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3555      SNAPSHOT_MODE_EVERY_STEP :
3556      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3557      SNAPSHOT_MODE_EVERY_MOVE :
3558      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3559      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3560   game.snapshot.save_snapshot = FALSE;
3561
3562   // ---------- initialize level time for Supaplex engine ---------------------
3563   // Supaplex levels with time limit currently unsupported -- should be added
3564   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3565     level.time = 0;
3566
3567   // ---------- initialize flags for handling game actions --------------------
3568
3569   // set flags for game actions to default values
3570   game.use_key_actions = TRUE;
3571   game.use_mouse_actions = FALSE;
3572
3573   // when using Mirror Magic game engine, handle mouse events only
3574   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3575   {
3576     game.use_key_actions = FALSE;
3577     game.use_mouse_actions = TRUE;
3578   }
3579
3580   // check for custom elements with mouse click events
3581   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3582   {
3583     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3584     {
3585       int element = EL_CUSTOM_START + i;
3586
3587       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3588           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3589           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3590           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3591         game.use_mouse_actions = TRUE;
3592     }
3593   }
3594 }
3595
3596 static int get_num_special_action(int element, int action_first,
3597                                   int action_last)
3598 {
3599   int num_special_action = 0;
3600   int i, j;
3601
3602   for (i = action_first; i <= action_last; i++)
3603   {
3604     boolean found = FALSE;
3605
3606     for (j = 0; j < NUM_DIRECTIONS; j++)
3607       if (el_act_dir2img(element, i, j) !=
3608           el_act_dir2img(element, ACTION_DEFAULT, j))
3609         found = TRUE;
3610
3611     if (found)
3612       num_special_action++;
3613     else
3614       break;
3615   }
3616
3617   return num_special_action;
3618 }
3619
3620
3621 // ============================================================================
3622 // InitGame()
3623 // ----------------------------------------------------------------------------
3624 // initialize and start new game
3625 // ============================================================================
3626
3627 #if DEBUG_INIT_PLAYER
3628 static void DebugPrintPlayerStatus(char *message)
3629 {
3630   int i;
3631
3632   if (!options.debug)
3633     return;
3634
3635   Debug("game:init:player", "%s:", message);
3636
3637   for (i = 0; i < MAX_PLAYERS; i++)
3638   {
3639     struct PlayerInfo *player = &stored_player[i];
3640
3641     Debug("game:init:player",
3642           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3643           i + 1,
3644           player->present,
3645           player->connected,
3646           player->connected_locally,
3647           player->connected_network,
3648           player->active,
3649           (local_player == player ? " (local player)" : ""));
3650   }
3651 }
3652 #endif
3653
3654 void InitGame(void)
3655 {
3656   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3657   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3658   int fade_mask = REDRAW_FIELD;
3659   boolean restarting = (game_status == GAME_MODE_PLAYING);
3660   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3661   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3662   int initial_move_dir = MV_DOWN;
3663   int i, j, x, y;
3664
3665   // required here to update video display before fading (FIX THIS)
3666   DrawMaskedBorder(REDRAW_DOOR_2);
3667
3668   if (!game.restart_level)
3669     CloseDoor(DOOR_CLOSE_1);
3670
3671   if (restarting)
3672   {
3673     // force fading out global animations displayed during game play
3674     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3675   }
3676   else
3677   {
3678     SetGameStatus(GAME_MODE_PLAYING);
3679   }
3680
3681   if (level_editor_test_game)
3682     FadeSkipNextFadeOut();
3683   else
3684     FadeSetEnterScreen();
3685
3686   if (CheckFadeAll())
3687     fade_mask = REDRAW_ALL;
3688
3689   FadeLevelSoundsAndMusic();
3690
3691   ExpireSoundLoops(TRUE);
3692
3693   FadeOut(fade_mask);
3694
3695   if (restarting)
3696   {
3697     // force restarting global animations displayed during game play
3698     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3699
3700     // this is required for "transforming" fade modes like cross-fading
3701     // (else global animations will be stopped, but not restarted here)
3702     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3703
3704     SetGameStatus(GAME_MODE_PLAYING);
3705   }
3706
3707   if (level_editor_test_game)
3708     FadeSkipNextFadeIn();
3709
3710   // needed if different viewport properties defined for playing
3711   ChangeViewportPropertiesIfNeeded();
3712
3713   ClearField();
3714
3715   DrawCompleteVideoDisplay();
3716
3717   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3718
3719   InitGameEngine();
3720   InitGameControlValues();
3721
3722   if (tape.recording)
3723   {
3724     // initialize tape actions from game when recording tape
3725     tape.use_key_actions   = game.use_key_actions;
3726     tape.use_mouse_actions = game.use_mouse_actions;
3727
3728     // initialize visible playfield size when recording tape (for team mode)
3729     tape.scr_fieldx = SCR_FIELDX;
3730     tape.scr_fieldy = SCR_FIELDY;
3731   }
3732
3733   // don't play tapes over network
3734   network_playing = (network.enabled && !tape.playing);
3735
3736   for (i = 0; i < MAX_PLAYERS; i++)
3737   {
3738     struct PlayerInfo *player = &stored_player[i];
3739
3740     player->index_nr = i;
3741     player->index_bit = (1 << i);
3742     player->element_nr = EL_PLAYER_1 + i;
3743
3744     player->present = FALSE;
3745     player->active = FALSE;
3746     player->mapped = FALSE;
3747
3748     player->killed = FALSE;
3749     player->reanimated = FALSE;
3750     player->buried = FALSE;
3751
3752     player->action = 0;
3753     player->effective_action = 0;
3754     player->programmed_action = 0;
3755     player->snap_action = 0;
3756
3757     player->mouse_action.lx = 0;
3758     player->mouse_action.ly = 0;
3759     player->mouse_action.button = 0;
3760     player->mouse_action.button_hint = 0;
3761
3762     player->effective_mouse_action.lx = 0;
3763     player->effective_mouse_action.ly = 0;
3764     player->effective_mouse_action.button = 0;
3765     player->effective_mouse_action.button_hint = 0;
3766
3767     for (j = 0; j < MAX_NUM_KEYS; j++)
3768       player->key[j] = FALSE;
3769
3770     player->num_white_keys = 0;
3771
3772     player->dynabomb_count = 0;
3773     player->dynabomb_size = 1;
3774     player->dynabombs_left = 0;
3775     player->dynabomb_xl = FALSE;
3776
3777     player->MovDir = initial_move_dir;
3778     player->MovPos = 0;
3779     player->GfxPos = 0;
3780     player->GfxDir = initial_move_dir;
3781     player->GfxAction = ACTION_DEFAULT;
3782     player->Frame = 0;
3783     player->StepFrame = 0;
3784
3785     player->initial_element = player->element_nr;
3786     player->artwork_element =
3787       (level.use_artwork_element[i] ? level.artwork_element[i] :
3788        player->element_nr);
3789     player->use_murphy = FALSE;
3790
3791     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3792     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3793
3794     player->gravity = level.initial_player_gravity[i];
3795
3796     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3797
3798     player->actual_frame_counter.count = 0;
3799     player->actual_frame_counter.value = 1;
3800
3801     player->step_counter = 0;
3802
3803     player->last_move_dir = initial_move_dir;
3804
3805     player->is_active = FALSE;
3806
3807     player->is_waiting = FALSE;
3808     player->is_moving = FALSE;
3809     player->is_auto_moving = FALSE;
3810     player->is_digging = FALSE;
3811     player->is_snapping = FALSE;
3812     player->is_collecting = FALSE;
3813     player->is_pushing = FALSE;
3814     player->is_switching = FALSE;
3815     player->is_dropping = FALSE;
3816     player->is_dropping_pressed = FALSE;
3817
3818     player->is_bored = FALSE;
3819     player->is_sleeping = FALSE;
3820
3821     player->was_waiting = TRUE;
3822     player->was_moving = FALSE;
3823     player->was_snapping = FALSE;
3824     player->was_dropping = FALSE;
3825
3826     player->force_dropping = FALSE;
3827
3828     player->frame_counter_bored = -1;
3829     player->frame_counter_sleeping = -1;
3830
3831     player->anim_delay_counter = 0;
3832     player->post_delay_counter = 0;
3833
3834     player->dir_waiting = initial_move_dir;
3835     player->action_waiting = ACTION_DEFAULT;
3836     player->last_action_waiting = ACTION_DEFAULT;
3837     player->special_action_bored = ACTION_DEFAULT;
3838     player->special_action_sleeping = ACTION_DEFAULT;
3839
3840     player->switch_x = -1;
3841     player->switch_y = -1;
3842
3843     player->drop_x = -1;
3844     player->drop_y = -1;
3845
3846     player->show_envelope = 0;
3847
3848     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3849
3850     player->push_delay       = -1;      // initialized when pushing starts
3851     player->push_delay_value = game.initial_push_delay_value;
3852
3853     player->drop_delay = 0;
3854     player->drop_pressed_delay = 0;
3855
3856     player->last_jx = -1;
3857     player->last_jy = -1;
3858     player->jx = -1;
3859     player->jy = -1;
3860
3861     player->shield_normal_time_left = 0;
3862     player->shield_deadly_time_left = 0;
3863
3864     player->last_removed_element = EL_UNDEFINED;
3865
3866     player->inventory_infinite_element = EL_UNDEFINED;
3867     player->inventory_size = 0;
3868
3869     if (level.use_initial_inventory[i])
3870     {
3871       for (j = 0; j < level.initial_inventory_size[i]; j++)
3872       {
3873         int element = level.initial_inventory_content[i][j];
3874         int collect_count = element_info[element].collect_count_initial;
3875         int k;
3876
3877         if (!IS_CUSTOM_ELEMENT(element))
3878           collect_count = 1;
3879
3880         if (collect_count == 0)
3881           player->inventory_infinite_element = element;
3882         else
3883           for (k = 0; k < collect_count; k++)
3884             if (player->inventory_size < MAX_INVENTORY_SIZE)
3885               player->inventory_element[player->inventory_size++] = element;
3886       }
3887     }
3888
3889     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3890     SnapField(player, 0, 0);
3891
3892     map_player_action[i] = i;
3893   }
3894
3895   network_player_action_received = FALSE;
3896
3897   // initial null action
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900
3901   FrameCounter = 0;
3902   TimeFrames = 0;
3903   TimePlayed = 0;
3904   TimeLeft = level.time;
3905
3906   TapeTimeFrames = 0;
3907   TapeTime = 0;
3908
3909   ScreenMovDir = MV_NONE;
3910   ScreenMovPos = 0;
3911   ScreenGfxPos = 0;
3912
3913   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3914
3915   game.robot_wheel_x = -1;
3916   game.robot_wheel_y = -1;
3917
3918   game.exit_x = -1;
3919   game.exit_y = -1;
3920
3921   game.all_players_gone = FALSE;
3922
3923   game.LevelSolved = FALSE;
3924   game.GameOver = FALSE;
3925
3926   game.GamePlayed = !tape.playing;
3927
3928   game.LevelSolved_GameWon = FALSE;
3929   game.LevelSolved_GameEnd = FALSE;
3930   game.LevelSolved_SaveTape = FALSE;
3931   game.LevelSolved_SaveScore = FALSE;
3932
3933   game.LevelSolved_CountingTime = 0;
3934   game.LevelSolved_CountingScore = 0;
3935   game.LevelSolved_CountingHealth = 0;
3936
3937   game.RestartGameRequested = FALSE;
3938
3939   game.panel.active = TRUE;
3940
3941   game.no_level_time_limit = (level.time == 0);
3942   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3943
3944   game.yamyam_content_nr = 0;
3945   game.robot_wheel_active = FALSE;
3946   game.magic_wall_active = FALSE;
3947   game.magic_wall_time_left = 0;
3948   game.light_time_left = 0;
3949   game.timegate_time_left = 0;
3950   game.switchgate_pos = 0;
3951   game.wind_direction = level.wind_direction_initial;
3952
3953   game.time_final = 0;
3954   game.score_time_final = 0;
3955
3956   game.score = 0;
3957   game.score_final = 0;
3958
3959   game.health = MAX_HEALTH;
3960   game.health_final = MAX_HEALTH;
3961
3962   game.gems_still_needed = level.gems_needed;
3963   game.sokoban_fields_still_needed = 0;
3964   game.sokoban_objects_still_needed = 0;
3965   game.lights_still_needed = 0;
3966   game.players_still_needed = 0;
3967   game.friends_still_needed = 0;
3968
3969   game.lenses_time_left = 0;
3970   game.magnify_time_left = 0;
3971
3972   game.ball_active = level.ball_active_initial;
3973   game.ball_content_nr = 0;
3974
3975   game.explosions_delayed = TRUE;
3976
3977   game.envelope_active = FALSE;
3978
3979   // special case: set custom artwork setting to initial value
3980   game.use_masked_elements = game.use_masked_elements_initial;
3981
3982   for (i = 0; i < NUM_BELTS; i++)
3983   {
3984     game.belt_dir[i] = MV_NONE;
3985     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3986   }
3987
3988   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3989     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3990
3991 #if DEBUG_INIT_PLAYER
3992   DebugPrintPlayerStatus("Player status at level initialization");
3993 #endif
3994
3995   SCAN_PLAYFIELD(x, y)
3996   {
3997     Tile[x][y] = Last[x][y] = level.field[x][y];
3998     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3999     ChangeDelay[x][y] = 0;
4000     ChangePage[x][y] = -1;
4001     CustomValue[x][y] = 0;              // initialized in InitField()
4002     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4003     AmoebaNr[x][y] = 0;
4004     WasJustMoving[x][y] = 0;
4005     WasJustFalling[x][y] = 0;
4006     CheckCollision[x][y] = 0;
4007     CheckImpact[x][y] = 0;
4008     Stop[x][y] = FALSE;
4009     Pushed[x][y] = FALSE;
4010
4011     ChangeCount[x][y] = 0;
4012     ChangeEvent[x][y] = -1;
4013
4014     ExplodePhase[x][y] = 0;
4015     ExplodeDelay[x][y] = 0;
4016     ExplodeField[x][y] = EX_TYPE_NONE;
4017
4018     RunnerVisit[x][y] = 0;
4019     PlayerVisit[x][y] = 0;
4020
4021     GfxFrame[x][y] = 0;
4022     GfxRandom[x][y] = INIT_GFX_RANDOM();
4023     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4024     GfxElement[x][y] = EL_UNDEFINED;
4025     GfxElementEmpty[x][y] = EL_EMPTY;
4026     GfxAction[x][y] = ACTION_DEFAULT;
4027     GfxDir[x][y] = MV_NONE;
4028     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4029   }
4030
4031   SCAN_PLAYFIELD(x, y)
4032   {
4033     InitFieldForEngine(x, y);
4034
4035     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4036       emulate_bd = FALSE;
4037     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4038       emulate_sp = FALSE;
4039
4040     InitField(x, y, TRUE);
4041
4042     ResetGfxAnimation(x, y);
4043   }
4044
4045   InitBeltMovement();
4046
4047   // required if level does not contain any "empty space" element
4048   if (element_info[EL_EMPTY].use_gfx_element)
4049     game.use_masked_elements = TRUE;
4050
4051   for (i = 0; i < MAX_PLAYERS; i++)
4052   {
4053     struct PlayerInfo *player = &stored_player[i];
4054
4055     // set number of special actions for bored and sleeping animation
4056     player->num_special_action_bored =
4057       get_num_special_action(player->artwork_element,
4058                              ACTION_BORING_1, ACTION_BORING_LAST);
4059     player->num_special_action_sleeping =
4060       get_num_special_action(player->artwork_element,
4061                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4062   }
4063
4064   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4065                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4066
4067   // initialize type of slippery elements
4068   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4069   {
4070     if (!IS_CUSTOM_ELEMENT(i))
4071     {
4072       // default: elements slip down either to the left or right randomly
4073       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4074
4075       // SP style elements prefer to slip down on the left side
4076       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4077         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4078
4079       // BD style elements prefer to slip down on the left side
4080       if (game.emulation == EMU_BOULDERDASH)
4081         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4082     }
4083   }
4084
4085   // initialize explosion and ignition delay
4086   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4087   {
4088     if (!IS_CUSTOM_ELEMENT(i))
4089     {
4090       int num_phase = 8;
4091       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4092                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4093                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4094       int last_phase = (num_phase + 1) * delay;
4095       int half_phase = (num_phase / 2) * delay;
4096
4097       element_info[i].explosion_delay = last_phase - 1;
4098       element_info[i].ignition_delay = half_phase;
4099
4100       if (i == EL_BLACK_ORB)
4101         element_info[i].ignition_delay = 1;
4102     }
4103   }
4104
4105   // correct non-moving belts to start moving left
4106   for (i = 0; i < NUM_BELTS; i++)
4107     if (game.belt_dir[i] == MV_NONE)
4108       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4109
4110 #if USE_NEW_PLAYER_ASSIGNMENTS
4111   // use preferred player also in local single-player mode
4112   if (!network.enabled && !game.team_mode)
4113   {
4114     int new_index_nr = setup.network_player_nr;
4115
4116     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4117     {
4118       for (i = 0; i < MAX_PLAYERS; i++)
4119         stored_player[i].connected_locally = FALSE;
4120
4121       stored_player[new_index_nr].connected_locally = TRUE;
4122     }
4123   }
4124
4125   for (i = 0; i < MAX_PLAYERS; i++)
4126   {
4127     stored_player[i].connected = FALSE;
4128
4129     // in network game mode, the local player might not be the first player
4130     if (stored_player[i].connected_locally)
4131       local_player = &stored_player[i];
4132   }
4133
4134   if (!network.enabled)
4135     local_player->connected = TRUE;
4136
4137   if (tape.playing)
4138   {
4139     for (i = 0; i < MAX_PLAYERS; i++)
4140       stored_player[i].connected = tape.player_participates[i];
4141   }
4142   else if (network.enabled)
4143   {
4144     // add team mode players connected over the network (needed for correct
4145     // assignment of player figures from level to locally playing players)
4146
4147     for (i = 0; i < MAX_PLAYERS; i++)
4148       if (stored_player[i].connected_network)
4149         stored_player[i].connected = TRUE;
4150   }
4151   else if (game.team_mode)
4152   {
4153     // try to guess locally connected team mode players (needed for correct
4154     // assignment of player figures from level to locally playing players)
4155
4156     for (i = 0; i < MAX_PLAYERS; i++)
4157       if (setup.input[i].use_joystick ||
4158           setup.input[i].key.left != KSYM_UNDEFINED)
4159         stored_player[i].connected = TRUE;
4160   }
4161
4162 #if DEBUG_INIT_PLAYER
4163   DebugPrintPlayerStatus("Player status after level initialization");
4164 #endif
4165
4166 #if DEBUG_INIT_PLAYER
4167   Debug("game:init:player", "Reassigning players ...");
4168 #endif
4169
4170   // check if any connected player was not found in playfield
4171   for (i = 0; i < MAX_PLAYERS; i++)
4172   {
4173     struct PlayerInfo *player = &stored_player[i];
4174
4175     if (player->connected && !player->present)
4176     {
4177       struct PlayerInfo *field_player = NULL;
4178
4179 #if DEBUG_INIT_PLAYER
4180       Debug("game:init:player",
4181             "- looking for field player for player %d ...", i + 1);
4182 #endif
4183
4184       // assign first free player found that is present in the playfield
4185
4186       // first try: look for unmapped playfield player that is not connected
4187       for (j = 0; j < MAX_PLAYERS; j++)
4188         if (field_player == NULL &&
4189             stored_player[j].present &&
4190             !stored_player[j].mapped &&
4191             !stored_player[j].connected)
4192           field_player = &stored_player[j];
4193
4194       // second try: look for *any* unmapped playfield player
4195       for (j = 0; j < MAX_PLAYERS; j++)
4196         if (field_player == NULL &&
4197             stored_player[j].present &&
4198             !stored_player[j].mapped)
4199           field_player = &stored_player[j];
4200
4201       if (field_player != NULL)
4202       {
4203         int jx = field_player->jx, jy = field_player->jy;
4204
4205 #if DEBUG_INIT_PLAYER
4206         Debug("game:init:player", "- found player %d",
4207               field_player->index_nr + 1);
4208 #endif
4209
4210         player->present = FALSE;
4211         player->active = FALSE;
4212
4213         field_player->present = TRUE;
4214         field_player->active = TRUE;
4215
4216         /*
4217         player->initial_element = field_player->initial_element;
4218         player->artwork_element = field_player->artwork_element;
4219
4220         player->block_last_field       = field_player->block_last_field;
4221         player->block_delay_adjustment = field_player->block_delay_adjustment;
4222         */
4223
4224         StorePlayer[jx][jy] = field_player->element_nr;
4225
4226         field_player->jx = field_player->last_jx = jx;
4227         field_player->jy = field_player->last_jy = jy;
4228
4229         if (local_player == player)
4230           local_player = field_player;
4231
4232         map_player_action[field_player->index_nr] = i;
4233
4234         field_player->mapped = TRUE;
4235
4236 #if DEBUG_INIT_PLAYER
4237         Debug("game:init:player", "- map_player_action[%d] == %d",
4238               field_player->index_nr + 1, i + 1);
4239 #endif
4240       }
4241     }
4242
4243     if (player->connected && player->present)
4244       player->mapped = TRUE;
4245   }
4246
4247 #if DEBUG_INIT_PLAYER
4248   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4249 #endif
4250
4251 #else
4252
4253   // check if any connected player was not found in playfield
4254   for (i = 0; i < MAX_PLAYERS; i++)
4255   {
4256     struct PlayerInfo *player = &stored_player[i];
4257
4258     if (player->connected && !player->present)
4259     {
4260       for (j = 0; j < MAX_PLAYERS; j++)
4261       {
4262         struct PlayerInfo *field_player = &stored_player[j];
4263         int jx = field_player->jx, jy = field_player->jy;
4264
4265         // assign first free player found that is present in the playfield
4266         if (field_player->present && !field_player->connected)
4267         {
4268           player->present = TRUE;
4269           player->active = TRUE;
4270
4271           field_player->present = FALSE;
4272           field_player->active = FALSE;
4273
4274           player->initial_element = field_player->initial_element;
4275           player->artwork_element = field_player->artwork_element;
4276
4277           player->block_last_field       = field_player->block_last_field;
4278           player->block_delay_adjustment = field_player->block_delay_adjustment;
4279
4280           StorePlayer[jx][jy] = player->element_nr;
4281
4282           player->jx = player->last_jx = jx;
4283           player->jy = player->last_jy = jy;
4284
4285           break;
4286         }
4287       }
4288     }
4289   }
4290 #endif
4291
4292 #if 0
4293   Debug("game:init:player", "local_player->present == %d",
4294         local_player->present);
4295 #endif
4296
4297   // set focus to local player for network games, else to all players
4298   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4299   game.centered_player_nr_next = game.centered_player_nr;
4300   game.set_centered_player = FALSE;
4301   game.set_centered_player_wrap = FALSE;
4302
4303   if (network_playing && tape.recording)
4304   {
4305     // store client dependent player focus when recording network games
4306     tape.centered_player_nr_next = game.centered_player_nr_next;
4307     tape.set_centered_player = TRUE;
4308   }
4309
4310   if (tape.playing)
4311   {
4312     // when playing a tape, eliminate all players who do not participate
4313
4314 #if USE_NEW_PLAYER_ASSIGNMENTS
4315
4316     if (!game.team_mode)
4317     {
4318       for (i = 0; i < MAX_PLAYERS; i++)
4319       {
4320         if (stored_player[i].active &&
4321             !tape.player_participates[map_player_action[i]])
4322         {
4323           struct PlayerInfo *player = &stored_player[i];
4324           int jx = player->jx, jy = player->jy;
4325
4326 #if DEBUG_INIT_PLAYER
4327           Debug("game:init:player", "Removing player %d at (%d, %d)",
4328                 i + 1, jx, jy);
4329 #endif
4330
4331           player->active = FALSE;
4332           StorePlayer[jx][jy] = 0;
4333           Tile[jx][jy] = EL_EMPTY;
4334         }
4335       }
4336     }
4337
4338 #else
4339
4340     for (i = 0; i < MAX_PLAYERS; i++)
4341     {
4342       if (stored_player[i].active &&
4343           !tape.player_participates[i])
4344       {
4345         struct PlayerInfo *player = &stored_player[i];
4346         int jx = player->jx, jy = player->jy;
4347
4348         player->active = FALSE;
4349         StorePlayer[jx][jy] = 0;
4350         Tile[jx][jy] = EL_EMPTY;
4351       }
4352     }
4353 #endif
4354   }
4355   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4356   {
4357     // when in single player mode, eliminate all but the local player
4358
4359     for (i = 0; i < MAX_PLAYERS; i++)
4360     {
4361       struct PlayerInfo *player = &stored_player[i];
4362
4363       if (player->active && player != local_player)
4364       {
4365         int jx = player->jx, jy = player->jy;
4366
4367         player->active = FALSE;
4368         player->present = FALSE;
4369
4370         StorePlayer[jx][jy] = 0;
4371         Tile[jx][jy] = EL_EMPTY;
4372       }
4373     }
4374   }
4375
4376   for (i = 0; i < MAX_PLAYERS; i++)
4377     if (stored_player[i].active)
4378       game.players_still_needed++;
4379
4380   if (level.solved_by_one_player)
4381     game.players_still_needed = 1;
4382
4383   // when recording the game, store which players take part in the game
4384   if (tape.recording)
4385   {
4386 #if USE_NEW_PLAYER_ASSIGNMENTS
4387     for (i = 0; i < MAX_PLAYERS; i++)
4388       if (stored_player[i].connected)
4389         tape.player_participates[i] = TRUE;
4390 #else
4391     for (i = 0; i < MAX_PLAYERS; i++)
4392       if (stored_player[i].active)
4393         tape.player_participates[i] = TRUE;
4394 #endif
4395   }
4396
4397 #if DEBUG_INIT_PLAYER
4398   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4399 #endif
4400
4401   if (BorderElement == EL_EMPTY)
4402   {
4403     SBX_Left = 0;
4404     SBX_Right = lev_fieldx - SCR_FIELDX;
4405     SBY_Upper = 0;
4406     SBY_Lower = lev_fieldy - SCR_FIELDY;
4407   }
4408   else
4409   {
4410     SBX_Left = -1;
4411     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4412     SBY_Upper = -1;
4413     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4414   }
4415
4416   if (full_lev_fieldx <= SCR_FIELDX)
4417     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4418   if (full_lev_fieldy <= SCR_FIELDY)
4419     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4420
4421   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4422     SBX_Left--;
4423   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4424     SBY_Upper--;
4425
4426   // if local player not found, look for custom element that might create
4427   // the player (make some assumptions about the right custom element)
4428   if (!local_player->present)
4429   {
4430     int start_x = 0, start_y = 0;
4431     int found_rating = 0;
4432     int found_element = EL_UNDEFINED;
4433     int player_nr = local_player->index_nr;
4434
4435     SCAN_PLAYFIELD(x, y)
4436     {
4437       int element = Tile[x][y];
4438       int content;
4439       int xx, yy;
4440       boolean is_player;
4441
4442       if (level.use_start_element[player_nr] &&
4443           level.start_element[player_nr] == element &&
4444           found_rating < 4)
4445       {
4446         start_x = x;
4447         start_y = y;
4448
4449         found_rating = 4;
4450         found_element = element;
4451       }
4452
4453       if (!IS_CUSTOM_ELEMENT(element))
4454         continue;
4455
4456       if (CAN_CHANGE(element))
4457       {
4458         for (i = 0; i < element_info[element].num_change_pages; i++)
4459         {
4460           // check for player created from custom element as single target
4461           content = element_info[element].change_page[i].target_element;
4462           is_player = IS_PLAYER_ELEMENT(content);
4463
4464           if (is_player && (found_rating < 3 ||
4465                             (found_rating == 3 && element < found_element)))
4466           {
4467             start_x = x;
4468             start_y = y;
4469
4470             found_rating = 3;
4471             found_element = element;
4472           }
4473         }
4474       }
4475
4476       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4477       {
4478         // check for player created from custom element as explosion content
4479         content = element_info[element].content.e[xx][yy];
4480         is_player = IS_PLAYER_ELEMENT(content);
4481
4482         if (is_player && (found_rating < 2 ||
4483                           (found_rating == 2 && element < found_element)))
4484         {
4485           start_x = x + xx - 1;
4486           start_y = y + yy - 1;
4487
4488           found_rating = 2;
4489           found_element = element;
4490         }
4491
4492         if (!CAN_CHANGE(element))
4493           continue;
4494
4495         for (i = 0; i < element_info[element].num_change_pages; i++)
4496         {
4497           // check for player created from custom element as extended target
4498           content =
4499             element_info[element].change_page[i].target_content.e[xx][yy];
4500
4501           is_player = IS_PLAYER_ELEMENT(content);
4502
4503           if (is_player && (found_rating < 1 ||
4504                             (found_rating == 1 && element < found_element)))
4505           {
4506             start_x = x + xx - 1;
4507             start_y = y + yy - 1;
4508
4509             found_rating = 1;
4510             found_element = element;
4511           }
4512         }
4513       }
4514     }
4515
4516     scroll_x = SCROLL_POSITION_X(start_x);
4517     scroll_y = SCROLL_POSITION_Y(start_y);
4518   }
4519   else
4520   {
4521     scroll_x = SCROLL_POSITION_X(local_player->jx);
4522     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4523   }
4524
4525   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4526     scroll_x = game.forced_scroll_x;
4527   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4528     scroll_y = game.forced_scroll_y;
4529
4530   // !!! FIX THIS (START) !!!
4531   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4532   {
4533     InitGameEngine_BD();
4534   }
4535   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4536   {
4537     InitGameEngine_EM();
4538   }
4539   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4540   {
4541     InitGameEngine_SP();
4542   }
4543   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4544   {
4545     InitGameEngine_MM();
4546   }
4547   else
4548   {
4549     DrawLevel(REDRAW_FIELD);
4550     DrawAllPlayers();
4551
4552     // after drawing the level, correct some elements
4553     if (game.timegate_time_left == 0)
4554       CloseAllOpenTimegates();
4555   }
4556
4557   // blit playfield from scroll buffer to normal back buffer for fading in
4558   BlitScreenToBitmap(backbuffer);
4559   // !!! FIX THIS (END) !!!
4560
4561   DrawMaskedBorder(fade_mask);
4562
4563   FadeIn(fade_mask);
4564
4565 #if 1
4566   // full screen redraw is required at this point in the following cases:
4567   // - special editor door undrawn when game was started from level editor
4568   // - drawing area (playfield) was changed and has to be removed completely
4569   redraw_mask = REDRAW_ALL;
4570   BackToFront();
4571 #endif
4572
4573   if (!game.restart_level)
4574   {
4575     // copy default game door content to main double buffer
4576
4577     // !!! CHECK AGAIN !!!
4578     SetPanelBackground();
4579     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4580     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4581   }
4582
4583   SetPanelBackground();
4584   SetDrawBackgroundMask(REDRAW_DOOR_1);
4585
4586   UpdateAndDisplayGameControlValues();
4587
4588   if (!game.restart_level)
4589   {
4590     UnmapGameButtons();
4591     UnmapTapeButtons();
4592
4593     FreeGameButtons();
4594     CreateGameButtons();
4595
4596     MapGameButtons();
4597     MapTapeButtons();
4598
4599     // copy actual game door content to door double buffer for OpenDoor()
4600     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4601
4602     OpenDoor(DOOR_OPEN_ALL);
4603
4604     KeyboardAutoRepeatOffUnlessAutoplay();
4605
4606 #if DEBUG_INIT_PLAYER
4607     DebugPrintPlayerStatus("Player status (final)");
4608 #endif
4609   }
4610
4611   UnmapAllGadgets();
4612
4613   MapGameButtons();
4614   MapTapeButtons();
4615
4616   if (!game.restart_level && !tape.playing)
4617   {
4618     LevelStats_incPlayed(level_nr);
4619
4620     SaveLevelSetup_SeriesInfo();
4621   }
4622
4623   game.restart_level = FALSE;
4624   game.request_active = FALSE;
4625
4626   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4627     InitGameActions_MM();
4628
4629   SaveEngineSnapshotToListInitial();
4630
4631   if (!game.restart_level)
4632   {
4633     PlaySound(SND_GAME_STARTING);
4634
4635     if (setup.sound_music)
4636       PlayLevelMusic();
4637   }
4638
4639   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4640 }
4641
4642 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4643                         int actual_player_x, int actual_player_y)
4644 {
4645   // this is used for non-R'n'D game engines to update certain engine values
4646
4647   // needed to determine if sounds are played within the visible screen area
4648   scroll_x = actual_scroll_x;
4649   scroll_y = actual_scroll_y;
4650
4651   // needed to get player position for "follow finger" playing input method
4652   local_player->jx = actual_player_x;
4653   local_player->jy = actual_player_y;
4654 }
4655
4656 void InitMovDir(int x, int y)
4657 {
4658   int i, element = Tile[x][y];
4659   static int xy[4][2] =
4660   {
4661     {  0, +1 },
4662     { +1,  0 },
4663     {  0, -1 },
4664     { -1,  0 }
4665   };
4666   static int direction[3][4] =
4667   {
4668     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4669     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4670     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4671   };
4672
4673   switch (element)
4674   {
4675     case EL_BUG_RIGHT:
4676     case EL_BUG_UP:
4677     case EL_BUG_LEFT:
4678     case EL_BUG_DOWN:
4679       Tile[x][y] = EL_BUG;
4680       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4681       break;
4682
4683     case EL_SPACESHIP_RIGHT:
4684     case EL_SPACESHIP_UP:
4685     case EL_SPACESHIP_LEFT:
4686     case EL_SPACESHIP_DOWN:
4687       Tile[x][y] = EL_SPACESHIP;
4688       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4689       break;
4690
4691     case EL_BD_BUTTERFLY_RIGHT:
4692     case EL_BD_BUTTERFLY_UP:
4693     case EL_BD_BUTTERFLY_LEFT:
4694     case EL_BD_BUTTERFLY_DOWN:
4695       Tile[x][y] = EL_BD_BUTTERFLY;
4696       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4697       break;
4698
4699     case EL_BD_FIREFLY_RIGHT:
4700     case EL_BD_FIREFLY_UP:
4701     case EL_BD_FIREFLY_LEFT:
4702     case EL_BD_FIREFLY_DOWN:
4703       Tile[x][y] = EL_BD_FIREFLY;
4704       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4705       break;
4706
4707     case EL_PACMAN_RIGHT:
4708     case EL_PACMAN_UP:
4709     case EL_PACMAN_LEFT:
4710     case EL_PACMAN_DOWN:
4711       Tile[x][y] = EL_PACMAN;
4712       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4713       break;
4714
4715     case EL_YAMYAM_LEFT:
4716     case EL_YAMYAM_RIGHT:
4717     case EL_YAMYAM_UP:
4718     case EL_YAMYAM_DOWN:
4719       Tile[x][y] = EL_YAMYAM;
4720       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4721       break;
4722
4723     case EL_SP_SNIKSNAK:
4724       MovDir[x][y] = MV_UP;
4725       break;
4726
4727     case EL_SP_ELECTRON:
4728       MovDir[x][y] = MV_LEFT;
4729       break;
4730
4731     case EL_MOLE_LEFT:
4732     case EL_MOLE_RIGHT:
4733     case EL_MOLE_UP:
4734     case EL_MOLE_DOWN:
4735       Tile[x][y] = EL_MOLE;
4736       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4737       break;
4738
4739     case EL_SPRING_LEFT:
4740     case EL_SPRING_RIGHT:
4741       Tile[x][y] = EL_SPRING;
4742       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4743       break;
4744
4745     default:
4746       if (IS_CUSTOM_ELEMENT(element))
4747       {
4748         struct ElementInfo *ei = &element_info[element];
4749         int move_direction_initial = ei->move_direction_initial;
4750         int move_pattern = ei->move_pattern;
4751
4752         if (move_direction_initial == MV_START_PREVIOUS)
4753         {
4754           if (MovDir[x][y] != MV_NONE)
4755             return;
4756
4757           move_direction_initial = MV_START_AUTOMATIC;
4758         }
4759
4760         if (move_direction_initial == MV_START_RANDOM)
4761           MovDir[x][y] = 1 << RND(4);
4762         else if (move_direction_initial & MV_ANY_DIRECTION)
4763           MovDir[x][y] = move_direction_initial;
4764         else if (move_pattern == MV_ALL_DIRECTIONS ||
4765                  move_pattern == MV_TURNING_LEFT ||
4766                  move_pattern == MV_TURNING_RIGHT ||
4767                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4768                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4769                  move_pattern == MV_TURNING_RANDOM)
4770           MovDir[x][y] = 1 << RND(4);
4771         else if (move_pattern == MV_HORIZONTAL)
4772           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4773         else if (move_pattern == MV_VERTICAL)
4774           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4775         else if (move_pattern & MV_ANY_DIRECTION)
4776           MovDir[x][y] = element_info[element].move_pattern;
4777         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4778                  move_pattern == MV_ALONG_RIGHT_SIDE)
4779         {
4780           // use random direction as default start direction
4781           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4782             MovDir[x][y] = 1 << RND(4);
4783
4784           for (i = 0; i < NUM_DIRECTIONS; i++)
4785           {
4786             int x1 = x + xy[i][0];
4787             int y1 = y + xy[i][1];
4788
4789             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4790             {
4791               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4792                 MovDir[x][y] = direction[0][i];
4793               else
4794                 MovDir[x][y] = direction[1][i];
4795
4796               break;
4797             }
4798           }
4799         }                
4800       }
4801       else
4802       {
4803         MovDir[x][y] = 1 << RND(4);
4804
4805         if (element != EL_BUG &&
4806             element != EL_SPACESHIP &&
4807             element != EL_BD_BUTTERFLY &&
4808             element != EL_BD_FIREFLY)
4809           break;
4810
4811         for (i = 0; i < NUM_DIRECTIONS; i++)
4812         {
4813           int x1 = x + xy[i][0];
4814           int y1 = y + xy[i][1];
4815
4816           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4817           {
4818             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4819             {
4820               MovDir[x][y] = direction[0][i];
4821               break;
4822             }
4823             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4824                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4825             {
4826               MovDir[x][y] = direction[1][i];
4827               break;
4828             }
4829           }
4830         }
4831       }
4832       break;
4833   }
4834
4835   GfxDir[x][y] = MovDir[x][y];
4836 }
4837
4838 void InitAmoebaNr(int x, int y)
4839 {
4840   int i;
4841   int group_nr = AmoebaNeighbourNr(x, y);
4842
4843   if (group_nr == 0)
4844   {
4845     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4846     {
4847       if (AmoebaCnt[i] == 0)
4848       {
4849         group_nr = i;
4850         break;
4851       }
4852     }
4853   }
4854
4855   AmoebaNr[x][y] = group_nr;
4856   AmoebaCnt[group_nr]++;
4857   AmoebaCnt2[group_nr]++;
4858 }
4859
4860 static void LevelSolved_SetFinalGameValues(void)
4861 {
4862   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4863                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4864   game.score_time_final = (level.use_step_counter ? TimePlayed :
4865                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4866
4867   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4868                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4869                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4870                       game.score);
4871
4872   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4873                        MM_HEALTH(game_mm.laser_overload_value) :
4874                        game.health);
4875
4876   game.LevelSolved_CountingTime = game.time_final;
4877   game.LevelSolved_CountingScore = game.score_final;
4878   game.LevelSolved_CountingHealth = game.health_final;
4879 }
4880
4881 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4882 {
4883   game.LevelSolved_CountingTime = time;
4884   game.LevelSolved_CountingScore = score;
4885   game.LevelSolved_CountingHealth = health;
4886
4887   game_panel_controls[GAME_PANEL_TIME].value = time;
4888   game_panel_controls[GAME_PANEL_SCORE].value = score;
4889   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4890
4891   DisplayGameControlValues();
4892 }
4893
4894 static void LevelSolved(void)
4895 {
4896   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4897       game.players_still_needed > 0)
4898     return;
4899
4900   game.LevelSolved = TRUE;
4901   game.GameOver = TRUE;
4902
4903   tape.solved = TRUE;
4904
4905   // needed here to display correct panel values while player walks into exit
4906   LevelSolved_SetFinalGameValues();
4907 }
4908
4909 static void AdvanceToNextLevel(void)
4910 {
4911   if (setup.increment_levels &&
4912       level_nr < leveldir_current->last_level &&
4913       !network_playing)
4914   {
4915     level_nr++;         // advance to next level
4916     TapeErase();        // start with empty tape
4917
4918     if (setup.auto_play_next_level)
4919     {
4920       scores.continue_playing = TRUE;
4921       scores.next_level_nr = level_nr;
4922
4923       LoadLevel(level_nr);
4924
4925       SaveLevelSetup_SeriesInfo();
4926     }
4927   }
4928 }
4929
4930 void GameWon(void)
4931 {
4932   static int time_count_steps;
4933   static int time, time_final;
4934   static float score, score_final; // needed for time score < 10 for 10 seconds
4935   static int health, health_final;
4936   static int game_over_delay_1 = 0;
4937   static int game_over_delay_2 = 0;
4938   static int game_over_delay_3 = 0;
4939   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4940   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4941
4942   if (!game.LevelSolved_GameWon)
4943   {
4944     int i;
4945
4946     // do not start end game actions before the player stops moving (to exit)
4947     if (local_player->active && local_player->MovPos)
4948       return;
4949
4950     // calculate final game values after player finished walking into exit
4951     LevelSolved_SetFinalGameValues();
4952
4953     game.LevelSolved_GameWon = TRUE;
4954     game.LevelSolved_SaveTape = tape.recording;
4955     game.LevelSolved_SaveScore = !tape.playing;
4956
4957     if (!tape.playing)
4958     {
4959       LevelStats_incSolved(level_nr);
4960
4961       SaveLevelSetup_SeriesInfo();
4962     }
4963
4964     if (tape.auto_play)         // tape might already be stopped here
4965       tape.auto_play_level_solved = TRUE;
4966
4967     TapeStop();
4968
4969     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4970     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4971     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4972
4973     time = time_final = game.time_final;
4974     score = score_final = game.score_final;
4975     health = health_final = game.health_final;
4976
4977     // update game panel values before (delayed) counting of score (if any)
4978     LevelSolved_DisplayFinalGameValues(time, score, health);
4979
4980     // if level has time score defined, calculate new final game values
4981     if (time_score > 0)
4982     {
4983       int time_final_max = 999;
4984       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4985       int time_frames = 0;
4986       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4987       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4988
4989       if (TimeLeft > 0)
4990       {
4991         time_final = 0;
4992         time_frames = time_frames_left;
4993       }
4994       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4995       {
4996         time_final = time_final_max;
4997         time_frames = time_frames_final_max - time_frames_played;
4998       }
4999
5000       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
5001
5002       time_count_steps = MAX(1, ABS(time_final - time) / 100);
5003
5004       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
5005       {
5006         // keep previous values (final values already processed here)
5007         time_final = time;
5008         score_final = score;
5009       }
5010       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
5011       {
5012         health_final = 0;
5013         score_final += health * time_score;
5014       }
5015
5016       game.score_final = score_final;
5017       game.health_final = health_final;
5018     }
5019
5020     // if not counting score after game, immediately update game panel values
5021     if (level_editor_test_game || !setup.count_score_after_game ||
5022         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5023     {
5024       time = time_final;
5025       score = score_final;
5026
5027       LevelSolved_DisplayFinalGameValues(time, score, health);
5028     }
5029
5030     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5031     {
5032       // check if last player has left the level
5033       if (game.exit_x >= 0 &&
5034           game.exit_y >= 0)
5035       {
5036         int x = game.exit_x;
5037         int y = game.exit_y;
5038         int element = Tile[x][y];
5039
5040         // close exit door after last player
5041         if ((game.all_players_gone &&
5042              (element == EL_EXIT_OPEN ||
5043               element == EL_SP_EXIT_OPEN ||
5044               element == EL_STEEL_EXIT_OPEN)) ||
5045             element == EL_EM_EXIT_OPEN ||
5046             element == EL_EM_STEEL_EXIT_OPEN)
5047         {
5048
5049           Tile[x][y] =
5050             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5051              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5052              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5053              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5054              EL_EM_STEEL_EXIT_CLOSING);
5055
5056           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5057         }
5058
5059         // player disappears
5060         DrawLevelField(x, y);
5061       }
5062
5063       for (i = 0; i < MAX_PLAYERS; i++)
5064       {
5065         struct PlayerInfo *player = &stored_player[i];
5066
5067         if (player->present)
5068         {
5069           RemovePlayer(player);
5070
5071           // player disappears
5072           DrawLevelField(player->jx, player->jy);
5073         }
5074       }
5075     }
5076
5077     PlaySound(SND_GAME_WINNING);
5078   }
5079
5080   if (setup.count_score_after_game)
5081   {
5082     if (time != time_final)
5083     {
5084       if (game_over_delay_1 > 0)
5085       {
5086         game_over_delay_1--;
5087
5088         return;
5089       }
5090
5091       int time_to_go = ABS(time_final - time);
5092       int time_count_dir = (time < time_final ? +1 : -1);
5093
5094       if (time_to_go < time_count_steps)
5095         time_count_steps = 1;
5096
5097       time  += time_count_steps * time_count_dir;
5098       score += time_count_steps * time_score;
5099
5100       // set final score to correct rounding differences after counting score
5101       if (time == time_final)
5102         score = score_final;
5103
5104       LevelSolved_DisplayFinalGameValues(time, score, health);
5105
5106       if (time == time_final)
5107         StopSound(SND_GAME_LEVELTIME_BONUS);
5108       else if (setup.sound_loops)
5109         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5110       else
5111         PlaySound(SND_GAME_LEVELTIME_BONUS);
5112
5113       return;
5114     }
5115
5116     if (health != health_final)
5117     {
5118       if (game_over_delay_2 > 0)
5119       {
5120         game_over_delay_2--;
5121
5122         return;
5123       }
5124
5125       int health_count_dir = (health < health_final ? +1 : -1);
5126
5127       health += health_count_dir;
5128       score  += time_score;
5129
5130       LevelSolved_DisplayFinalGameValues(time, score, health);
5131
5132       if (health == health_final)
5133         StopSound(SND_GAME_LEVELTIME_BONUS);
5134       else if (setup.sound_loops)
5135         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5136       else
5137         PlaySound(SND_GAME_LEVELTIME_BONUS);
5138
5139       return;
5140     }
5141   }
5142
5143   game.panel.active = FALSE;
5144
5145   if (game_over_delay_3 > 0)
5146   {
5147     game_over_delay_3--;
5148
5149     return;
5150   }
5151
5152   GameEnd();
5153 }
5154
5155 void GameEnd(void)
5156 {
5157   // used instead of "level_nr" (needed for network games)
5158   int last_level_nr = levelset.level_nr;
5159   boolean tape_saved = FALSE;
5160
5161   game.LevelSolved_GameEnd = TRUE;
5162
5163   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5164   {
5165     // make sure that request dialog to save tape does not open door again
5166     if (!global.use_envelope_request)
5167       CloseDoor(DOOR_CLOSE_1);
5168
5169     // ask to save tape
5170     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5171
5172     // set unique basename for score tape (also saved in high score table)
5173     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5174   }
5175
5176   // if no tape is to be saved, close both doors simultaneously
5177   CloseDoor(DOOR_CLOSE_ALL);
5178
5179   if (level_editor_test_game || score_info_tape_play)
5180   {
5181     SetGameStatus(GAME_MODE_MAIN);
5182
5183     DrawMainMenu();
5184
5185     return;
5186   }
5187
5188   if (!game.LevelSolved_SaveScore)
5189   {
5190     SetGameStatus(GAME_MODE_MAIN);
5191
5192     DrawMainMenu();
5193
5194     return;
5195   }
5196
5197   if (level_nr == leveldir_current->handicap_level)
5198   {
5199     leveldir_current->handicap_level++;
5200
5201     SaveLevelSetup_SeriesInfo();
5202   }
5203
5204   // save score and score tape before potentially erasing tape below
5205   NewHighScore(last_level_nr, tape_saved);
5206
5207   // increment and load next level (if possible and not configured otherwise)
5208   AdvanceToNextLevel();
5209
5210   if (scores.last_added >= 0 && setup.show_scores_after_game)
5211   {
5212     SetGameStatus(GAME_MODE_SCORES);
5213
5214     DrawHallOfFame(last_level_nr);
5215   }
5216   else if (scores.continue_playing)
5217   {
5218     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5219   }
5220   else
5221   {
5222     SetGameStatus(GAME_MODE_MAIN);
5223
5224     DrawMainMenu();
5225   }
5226 }
5227
5228 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5229                          boolean one_score_entry_per_name)
5230 {
5231   int i;
5232
5233   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5234     return -1;
5235
5236   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5237   {
5238     struct ScoreEntry *entry = &list->entry[i];
5239     boolean score_is_better = (new_entry->score >  entry->score);
5240     boolean score_is_equal  = (new_entry->score == entry->score);
5241     boolean time_is_better  = (new_entry->time  <  entry->time);
5242     boolean time_is_equal   = (new_entry->time  == entry->time);
5243     boolean better_by_score = (score_is_better ||
5244                                (score_is_equal && time_is_better));
5245     boolean better_by_time  = (time_is_better ||
5246                                (time_is_equal && score_is_better));
5247     boolean is_better = (level.rate_time_over_score ? better_by_time :
5248                          better_by_score);
5249     boolean entry_is_empty = (entry->score == 0 &&
5250                               entry->time == 0);
5251
5252     // prevent adding server score entries if also existing in local score file
5253     // (special case: historic score entries have an empty tape basename entry)
5254     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5255         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5256     {
5257       // add fields from server score entry not stored in local score entry
5258       // (currently, this means setting platform, version and country fields;
5259       // in rare cases, this may also correct an invalid score value, as
5260       // historic scores might have been truncated to 16-bit values locally)
5261       *entry = *new_entry;
5262
5263       return -1;
5264     }
5265
5266     if (is_better || entry_is_empty)
5267     {
5268       // player has made it to the hall of fame
5269
5270       if (i < MAX_SCORE_ENTRIES - 1)
5271       {
5272         int m = MAX_SCORE_ENTRIES - 1;
5273         int l;
5274
5275         if (one_score_entry_per_name)
5276         {
5277           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5278             if (strEqual(list->entry[l].name, new_entry->name))
5279               m = l;
5280
5281           if (m == i)   // player's new highscore overwrites his old one
5282             goto put_into_list;
5283         }
5284
5285         for (l = m; l > i; l--)
5286           list->entry[l] = list->entry[l - 1];
5287       }
5288
5289       put_into_list:
5290
5291       *entry = *new_entry;
5292
5293       return i;
5294     }
5295     else if (one_score_entry_per_name &&
5296              strEqual(entry->name, new_entry->name))
5297     {
5298       // player already in high score list with better score or time
5299
5300       return -1;
5301     }
5302   }
5303
5304   // special case: new score is beyond the last high score list position
5305   return MAX_SCORE_ENTRIES;
5306 }
5307
5308 void NewHighScore(int level_nr, boolean tape_saved)
5309 {
5310   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5311   boolean one_per_name = FALSE;
5312
5313   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5314   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5315
5316   new_entry.score = game.score_final;
5317   new_entry.time = game.score_time_final;
5318
5319   LoadScore(level_nr);
5320
5321   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5322
5323   if (scores.last_added >= MAX_SCORE_ENTRIES)
5324   {
5325     scores.last_added = MAX_SCORE_ENTRIES - 1;
5326     scores.force_last_added = TRUE;
5327
5328     scores.entry[scores.last_added] = new_entry;
5329
5330     // store last added local score entry (before merging server scores)
5331     scores.last_added_local = scores.last_added;
5332
5333     return;
5334   }
5335
5336   if (scores.last_added < 0)
5337     return;
5338
5339   SaveScore(level_nr);
5340
5341   // store last added local score entry (before merging server scores)
5342   scores.last_added_local = scores.last_added;
5343
5344   if (!game.LevelSolved_SaveTape)
5345     return;
5346
5347   SaveScoreTape(level_nr);
5348
5349   if (setup.ask_for_using_api_server)
5350   {
5351     setup.use_api_server =
5352       Request("Upload your score and tape to the high score server?", REQ_ASK);
5353
5354     if (!setup.use_api_server)
5355       Request("Not using high score server! Use setup menu to enable again!",
5356               REQ_CONFIRM);
5357
5358     runtime.use_api_server = setup.use_api_server;
5359
5360     // after asking for using API server once, do not ask again
5361     setup.ask_for_using_api_server = FALSE;
5362
5363     SaveSetup_ServerSetup();
5364   }
5365
5366   SaveServerScore(level_nr, tape_saved);
5367 }
5368
5369 void MergeServerScore(void)
5370 {
5371   struct ScoreEntry last_added_entry;
5372   boolean one_per_name = FALSE;
5373   int i;
5374
5375   if (scores.last_added >= 0)
5376     last_added_entry = scores.entry[scores.last_added];
5377
5378   for (i = 0; i < server_scores.num_entries; i++)
5379   {
5380     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5381
5382     if (pos >= 0 && pos <= scores.last_added)
5383       scores.last_added++;
5384   }
5385
5386   if (scores.last_added >= MAX_SCORE_ENTRIES)
5387   {
5388     scores.last_added = MAX_SCORE_ENTRIES - 1;
5389     scores.force_last_added = TRUE;
5390
5391     scores.entry[scores.last_added] = last_added_entry;
5392   }
5393 }
5394
5395 static int getElementMoveStepsizeExt(int x, int y, int direction)
5396 {
5397   int element = Tile[x][y];
5398   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5399   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5400   int horiz_move = (dx != 0);
5401   int sign = (horiz_move ? dx : dy);
5402   int step = sign * element_info[element].move_stepsize;
5403
5404   // special values for move stepsize for spring and things on conveyor belt
5405   if (horiz_move)
5406   {
5407     if (CAN_FALL(element) &&
5408         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5409       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5410     else if (element == EL_SPRING)
5411       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5412   }
5413
5414   return step;
5415 }
5416
5417 static int getElementMoveStepsize(int x, int y)
5418 {
5419   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5420 }
5421
5422 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5423 {
5424   if (player->GfxAction != action || player->GfxDir != dir)
5425   {
5426     player->GfxAction = action;
5427     player->GfxDir = dir;
5428     player->Frame = 0;
5429     player->StepFrame = 0;
5430   }
5431 }
5432
5433 static void ResetGfxFrame(int x, int y)
5434 {
5435   // profiling showed that "autotest" spends 10~20% of its time in this function
5436   if (DrawingDeactivatedField())
5437     return;
5438
5439   int element = Tile[x][y];
5440   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5441
5442   if (graphic_info[graphic].anim_global_sync)
5443     GfxFrame[x][y] = FrameCounter;
5444   else if (graphic_info[graphic].anim_global_anim_sync)
5445     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5446   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5447     GfxFrame[x][y] = CustomValue[x][y];
5448   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5449     GfxFrame[x][y] = element_info[element].collect_score;
5450   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5451     GfxFrame[x][y] = ChangeDelay[x][y];
5452 }
5453
5454 static void ResetGfxAnimation(int x, int y)
5455 {
5456   GfxAction[x][y] = ACTION_DEFAULT;
5457   GfxDir[x][y] = MovDir[x][y];
5458   GfxFrame[x][y] = 0;
5459
5460   ResetGfxFrame(x, y);
5461 }
5462
5463 static void ResetRandomAnimationValue(int x, int y)
5464 {
5465   GfxRandom[x][y] = INIT_GFX_RANDOM();
5466 }
5467
5468 static void InitMovingField(int x, int y, int direction)
5469 {
5470   int element = Tile[x][y];
5471   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5472   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5473   int newx = x + dx;
5474   int newy = y + dy;
5475   boolean is_moving_before, is_moving_after;
5476
5477   // check if element was/is moving or being moved before/after mode change
5478   is_moving_before = (WasJustMoving[x][y] != 0);
5479   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5480
5481   // reset animation only for moving elements which change direction of moving
5482   // or which just started or stopped moving
5483   // (else CEs with property "can move" / "not moving" are reset each frame)
5484   if (is_moving_before != is_moving_after ||
5485       direction != MovDir[x][y])
5486     ResetGfxAnimation(x, y);
5487
5488   MovDir[x][y] = direction;
5489   GfxDir[x][y] = direction;
5490
5491   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5492                      direction == MV_DOWN && CAN_FALL(element) ?
5493                      ACTION_FALLING : ACTION_MOVING);
5494
5495   // this is needed for CEs with property "can move" / "not moving"
5496
5497   if (is_moving_after)
5498   {
5499     if (Tile[newx][newy] == EL_EMPTY)
5500       Tile[newx][newy] = EL_BLOCKED;
5501
5502     MovDir[newx][newy] = MovDir[x][y];
5503
5504     CustomValue[newx][newy] = CustomValue[x][y];
5505
5506     GfxFrame[newx][newy] = GfxFrame[x][y];
5507     GfxRandom[newx][newy] = GfxRandom[x][y];
5508     GfxAction[newx][newy] = GfxAction[x][y];
5509     GfxDir[newx][newy] = GfxDir[x][y];
5510   }
5511 }
5512
5513 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5514 {
5515   int direction = MovDir[x][y];
5516   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5517   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5518
5519   *goes_to_x = newx;
5520   *goes_to_y = newy;
5521 }
5522
5523 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5524 {
5525   int direction = MovDir[x][y];
5526   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5527   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5528
5529   *comes_from_x = oldx;
5530   *comes_from_y = oldy;
5531 }
5532
5533 static int MovingOrBlocked2Element(int x, int y)
5534 {
5535   int element = Tile[x][y];
5536
5537   if (element == EL_BLOCKED)
5538   {
5539     int oldx, oldy;
5540
5541     Blocked2Moving(x, y, &oldx, &oldy);
5542
5543     return Tile[oldx][oldy];
5544   }
5545
5546   return element;
5547 }
5548
5549 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5550 {
5551   // like MovingOrBlocked2Element(), but if element is moving
5552   // and (x, y) is the field the moving element is just leaving,
5553   // return EL_BLOCKED instead of the element value
5554   int element = Tile[x][y];
5555
5556   if (IS_MOVING(x, y))
5557   {
5558     if (element == EL_BLOCKED)
5559     {
5560       int oldx, oldy;
5561
5562       Blocked2Moving(x, y, &oldx, &oldy);
5563       return Tile[oldx][oldy];
5564     }
5565     else
5566       return EL_BLOCKED;
5567   }
5568   else
5569     return element;
5570 }
5571
5572 static void RemoveField(int x, int y)
5573 {
5574   Tile[x][y] = EL_EMPTY;
5575
5576   MovPos[x][y] = 0;
5577   MovDir[x][y] = 0;
5578   MovDelay[x][y] = 0;
5579
5580   CustomValue[x][y] = 0;
5581
5582   AmoebaNr[x][y] = 0;
5583   ChangeDelay[x][y] = 0;
5584   ChangePage[x][y] = -1;
5585   Pushed[x][y] = FALSE;
5586
5587   GfxElement[x][y] = EL_UNDEFINED;
5588   GfxAction[x][y] = ACTION_DEFAULT;
5589   GfxDir[x][y] = MV_NONE;
5590 }
5591
5592 static void RemoveMovingField(int x, int y)
5593 {
5594   int oldx = x, oldy = y, newx = x, newy = y;
5595   int element = Tile[x][y];
5596   int next_element = EL_UNDEFINED;
5597
5598   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5599     return;
5600
5601   if (IS_MOVING(x, y))
5602   {
5603     Moving2Blocked(x, y, &newx, &newy);
5604
5605     if (Tile[newx][newy] != EL_BLOCKED)
5606     {
5607       // element is moving, but target field is not free (blocked), but
5608       // already occupied by something different (example: acid pool);
5609       // in this case, only remove the moving field, but not the target
5610
5611       RemoveField(oldx, oldy);
5612
5613       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5614
5615       TEST_DrawLevelField(oldx, oldy);
5616
5617       return;
5618     }
5619   }
5620   else if (element == EL_BLOCKED)
5621   {
5622     Blocked2Moving(x, y, &oldx, &oldy);
5623     if (!IS_MOVING(oldx, oldy))
5624       return;
5625   }
5626
5627   if (element == EL_BLOCKED &&
5628       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5629        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5630        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5631        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5632        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5633        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5634     next_element = get_next_element(Tile[oldx][oldy]);
5635
5636   RemoveField(oldx, oldy);
5637   RemoveField(newx, newy);
5638
5639   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5640
5641   if (next_element != EL_UNDEFINED)
5642     Tile[oldx][oldy] = next_element;
5643
5644   TEST_DrawLevelField(oldx, oldy);
5645   TEST_DrawLevelField(newx, newy);
5646 }
5647
5648 void DrawDynamite(int x, int y)
5649 {
5650   int sx = SCREENX(x), sy = SCREENY(y);
5651   int graphic = el2img(Tile[x][y]);
5652   int frame;
5653
5654   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5655     return;
5656
5657   if (IS_WALKABLE_INSIDE(Back[x][y]))
5658     return;
5659
5660   if (Back[x][y])
5661     DrawLevelElement(x, y, Back[x][y]);
5662   else if (Store[x][y])
5663     DrawLevelElement(x, y, Store[x][y]);
5664   else if (game.use_masked_elements)
5665     DrawLevelElement(x, y, EL_EMPTY);
5666
5667   frame = getGraphicAnimationFrameXY(graphic, x, y);
5668
5669   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5670     DrawGraphicThruMask(sx, sy, graphic, frame);
5671   else
5672     DrawGraphic(sx, sy, graphic, frame);
5673 }
5674
5675 static void CheckDynamite(int x, int y)
5676 {
5677   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5678   {
5679     MovDelay[x][y]--;
5680
5681     if (MovDelay[x][y] != 0)
5682     {
5683       DrawDynamite(x, y);
5684       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5685
5686       return;
5687     }
5688   }
5689
5690   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5691
5692   Bang(x, y);
5693 }
5694
5695 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5696 {
5697   boolean num_checked_players = 0;
5698   int i;
5699
5700   for (i = 0; i < MAX_PLAYERS; i++)
5701   {
5702     if (stored_player[i].active)
5703     {
5704       int sx = stored_player[i].jx;
5705       int sy = stored_player[i].jy;
5706
5707       if (num_checked_players == 0)
5708       {
5709         *sx1 = *sx2 = sx;
5710         *sy1 = *sy2 = sy;
5711       }
5712       else
5713       {
5714         *sx1 = MIN(*sx1, sx);
5715         *sy1 = MIN(*sy1, sy);
5716         *sx2 = MAX(*sx2, sx);
5717         *sy2 = MAX(*sy2, sy);
5718       }
5719
5720       num_checked_players++;
5721     }
5722   }
5723 }
5724
5725 static boolean checkIfAllPlayersFitToScreen_RND(void)
5726 {
5727   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5728
5729   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5730
5731   return (sx2 - sx1 < SCR_FIELDX &&
5732           sy2 - sy1 < SCR_FIELDY);
5733 }
5734
5735 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5736 {
5737   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5738
5739   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5740
5741   *sx = (sx1 + sx2) / 2;
5742   *sy = (sy1 + sy2) / 2;
5743 }
5744
5745 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5746                                boolean center_screen, boolean quick_relocation)
5747 {
5748   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5749   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5750   boolean no_delay = (tape.warp_forward);
5751   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5752   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5753   int new_scroll_x, new_scroll_y;
5754
5755   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5756   {
5757     // case 1: quick relocation inside visible screen (without scrolling)
5758
5759     RedrawPlayfield();
5760
5761     return;
5762   }
5763
5764   if (!level.shifted_relocation || center_screen)
5765   {
5766     // relocation _with_ centering of screen
5767
5768     new_scroll_x = SCROLL_POSITION_X(x);
5769     new_scroll_y = SCROLL_POSITION_Y(y);
5770   }
5771   else
5772   {
5773     // relocation _without_ centering of screen
5774
5775     // apply distance between old and new player position to scroll position
5776     int shifted_scroll_x = scroll_x + (x - old_x);
5777     int shifted_scroll_y = scroll_y + (y - old_y);
5778
5779     // make sure that shifted scroll position does not scroll beyond screen
5780     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5781     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5782
5783     // special case for teleporting from one end of the playfield to the other
5784     // (this kludge prevents the destination area to be shifted by half a tile
5785     // against the source destination for even screen width or screen height;
5786     // probably most useful when used with high "game.forced_scroll_delay_value"
5787     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5788     if (quick_relocation)
5789     {
5790       if (EVEN(SCR_FIELDX))
5791       {
5792         // relocate (teleport) between left and right border (half or full)
5793         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5794           new_scroll_x = SBX_Right;
5795         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5796           new_scroll_x = SBX_Right - 1;
5797         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5798           new_scroll_x = SBX_Left;
5799         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5800           new_scroll_x = SBX_Left + 1;
5801       }
5802
5803       if (EVEN(SCR_FIELDY))
5804       {
5805         // relocate (teleport) between top and bottom border (half or full)
5806         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5807           new_scroll_y = SBY_Lower;
5808         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5809           new_scroll_y = SBY_Lower - 1;
5810         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5811           new_scroll_y = SBY_Upper;
5812         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5813           new_scroll_y = SBY_Upper + 1;
5814       }
5815     }
5816   }
5817
5818   if (quick_relocation)
5819   {
5820     // case 2: quick relocation (redraw without visible scrolling)
5821
5822     scroll_x = new_scroll_x;
5823     scroll_y = new_scroll_y;
5824
5825     RedrawPlayfield();
5826
5827     return;
5828   }
5829
5830   // case 3: visible relocation (with scrolling to new position)
5831
5832   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5833
5834   SetVideoFrameDelay(wait_delay_value);
5835
5836   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5837   {
5838     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5839     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5840
5841     if (dx == 0 && dy == 0)             // no scrolling needed at all
5842       break;
5843
5844     scroll_x -= dx;
5845     scroll_y -= dy;
5846
5847     // set values for horizontal/vertical screen scrolling (half tile size)
5848     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5849     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5850     int pos_x = dx * TILEX / 2;
5851     int pos_y = dy * TILEY / 2;
5852     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5853     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5854
5855     ScrollLevel(dx, dy);
5856     DrawAllPlayers();
5857
5858     // scroll in two steps of half tile size to make things smoother
5859     BlitScreenToBitmapExt_RND(window, fx, fy);
5860
5861     // scroll second step to align at full tile size
5862     BlitScreenToBitmap(window);
5863   }
5864
5865   DrawAllPlayers();
5866   BackToFront();
5867
5868   SetVideoFrameDelay(frame_delay_value_old);
5869 }
5870
5871 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5872 {
5873   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5874   int player_nr = GET_PLAYER_NR(el_player);
5875   struct PlayerInfo *player = &stored_player[player_nr];
5876   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5877   boolean no_delay = (tape.warp_forward);
5878   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5879   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5880   int old_jx = player->jx;
5881   int old_jy = player->jy;
5882   int old_element = Tile[old_jx][old_jy];
5883   int element = Tile[jx][jy];
5884   boolean player_relocated = (old_jx != jx || old_jy != jy);
5885
5886   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5887   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5888   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5889   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5890   int leave_side_horiz = move_dir_horiz;
5891   int leave_side_vert  = move_dir_vert;
5892   int enter_side = enter_side_horiz | enter_side_vert;
5893   int leave_side = leave_side_horiz | leave_side_vert;
5894
5895   if (player->buried)           // do not reanimate dead player
5896     return;
5897
5898   if (!player_relocated)        // no need to relocate the player
5899     return;
5900
5901   if (IS_PLAYER(jx, jy))        // player already placed at new position
5902   {
5903     RemoveField(jx, jy);        // temporarily remove newly placed player
5904     DrawLevelField(jx, jy);
5905   }
5906
5907   if (player->present)
5908   {
5909     while (player->MovPos)
5910     {
5911       ScrollPlayer(player, SCROLL_GO_ON);
5912       ScrollScreen(NULL, SCROLL_GO_ON);
5913
5914       AdvanceFrameAndPlayerCounters(player->index_nr);
5915
5916       DrawPlayer(player);
5917
5918       BackToFront_WithFrameDelay(wait_delay_value);
5919     }
5920
5921     DrawPlayer(player);         // needed here only to cleanup last field
5922     DrawLevelField(player->jx, player->jy);     // remove player graphic
5923
5924     player->is_moving = FALSE;
5925   }
5926
5927   if (IS_CUSTOM_ELEMENT(old_element))
5928     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5929                                CE_LEFT_BY_PLAYER,
5930                                player->index_bit, leave_side);
5931
5932   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5933                                       CE_PLAYER_LEAVES_X,
5934                                       player->index_bit, leave_side);
5935
5936   Tile[jx][jy] = el_player;
5937   InitPlayerField(jx, jy, el_player, TRUE);
5938
5939   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5940      possible that the relocation target field did not contain a player element,
5941      but a walkable element, to which the new player was relocated -- in this
5942      case, restore that (already initialized!) element on the player field */
5943   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5944   {
5945     Tile[jx][jy] = element;     // restore previously existing element
5946   }
5947
5948   // only visually relocate centered player
5949   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5950                      FALSE, level.instant_relocation);
5951
5952   TestIfPlayerTouchesBadThing(jx, jy);
5953   TestIfPlayerTouchesCustomElement(jx, jy);
5954
5955   if (IS_CUSTOM_ELEMENT(element))
5956     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5957                                player->index_bit, enter_side);
5958
5959   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5960                                       player->index_bit, enter_side);
5961
5962   if (player->is_switching)
5963   {
5964     /* ensure that relocation while still switching an element does not cause
5965        a new element to be treated as also switched directly after relocation
5966        (this is important for teleporter switches that teleport the player to
5967        a place where another teleporter switch is in the same direction, which
5968        would then incorrectly be treated as immediately switched before the
5969        direction key that caused the switch was released) */
5970
5971     player->switch_x += jx - old_jx;
5972     player->switch_y += jy - old_jy;
5973   }
5974 }
5975
5976 static void Explode(int ex, int ey, int phase, int mode)
5977 {
5978   int x, y;
5979   int last_phase;
5980   int border_element;
5981
5982   if (game.explosions_delayed)
5983   {
5984     ExplodeField[ex][ey] = mode;
5985     return;
5986   }
5987
5988   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5989   {
5990     int center_element = Tile[ex][ey];
5991     int ce_value = CustomValue[ex][ey];
5992     int ce_score = element_info[center_element].collect_score;
5993     int artwork_element, explosion_element;     // set these values later
5994
5995     // remove things displayed in background while burning dynamite
5996     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5997       Back[ex][ey] = 0;
5998
5999     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6000     {
6001       // put moving element to center field (and let it explode there)
6002       center_element = MovingOrBlocked2Element(ex, ey);
6003       RemoveMovingField(ex, ey);
6004       Tile[ex][ey] = center_element;
6005     }
6006
6007     // now "center_element" is finally determined -- set related values now
6008     artwork_element = center_element;           // for custom player artwork
6009     explosion_element = center_element;         // for custom player artwork
6010
6011     if (IS_PLAYER(ex, ey))
6012     {
6013       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6014
6015       artwork_element = stored_player[player_nr].artwork_element;
6016
6017       if (level.use_explosion_element[player_nr])
6018       {
6019         explosion_element = level.explosion_element[player_nr];
6020         artwork_element = explosion_element;
6021       }
6022     }
6023
6024     if (mode == EX_TYPE_NORMAL ||
6025         mode == EX_TYPE_CENTER ||
6026         mode == EX_TYPE_CROSS)
6027       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6028
6029     last_phase = element_info[explosion_element].explosion_delay + 1;
6030
6031     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6032     {
6033       int xx = x - ex + 1;
6034       int yy = y - ey + 1;
6035       int element;
6036
6037       if (!IN_LEV_FIELD(x, y) ||
6038           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6039           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6040         continue;
6041
6042       element = Tile[x][y];
6043
6044       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6045       {
6046         element = MovingOrBlocked2Element(x, y);
6047
6048         if (!IS_EXPLOSION_PROOF(element))
6049           RemoveMovingField(x, y);
6050       }
6051
6052       // indestructible elements can only explode in center (but not flames)
6053       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6054                                            mode == EX_TYPE_BORDER)) ||
6055           element == EL_FLAMES)
6056         continue;
6057
6058       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6059          behaviour, for example when touching a yamyam that explodes to rocks
6060          with active deadly shield, a rock is created under the player !!! */
6061       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6062 #if 0
6063       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6064           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6065            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6066 #else
6067       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6068 #endif
6069       {
6070         if (IS_ACTIVE_BOMB(element))
6071         {
6072           // re-activate things under the bomb like gate or penguin
6073           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6074           Back[x][y] = 0;
6075         }
6076
6077         continue;
6078       }
6079
6080       // save walkable background elements while explosion on same tile
6081       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6082           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6083         Back[x][y] = element;
6084
6085       // ignite explodable elements reached by other explosion
6086       if (element == EL_EXPLOSION)
6087         element = Store2[x][y];
6088
6089       if (AmoebaNr[x][y] &&
6090           (element == EL_AMOEBA_FULL ||
6091            element == EL_BD_AMOEBA ||
6092            element == EL_AMOEBA_GROWING))
6093       {
6094         AmoebaCnt[AmoebaNr[x][y]]--;
6095         AmoebaCnt2[AmoebaNr[x][y]]--;
6096       }
6097
6098       RemoveField(x, y);
6099
6100       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6101       {
6102         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6103
6104         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6105
6106         if (PLAYERINFO(ex, ey)->use_murphy)
6107           Store[x][y] = EL_EMPTY;
6108       }
6109
6110       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6111       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6112       else if (IS_PLAYER_ELEMENT(center_element))
6113         Store[x][y] = EL_EMPTY;
6114       else if (center_element == EL_YAMYAM)
6115         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6116       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6117         Store[x][y] = element_info[center_element].content.e[xx][yy];
6118 #if 1
6119       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6120       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6121       // otherwise) -- FIX THIS !!!
6122       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6123         Store[x][y] = element_info[element].content.e[1][1];
6124 #else
6125       else if (!CAN_EXPLODE(element))
6126         Store[x][y] = element_info[element].content.e[1][1];
6127 #endif
6128       else
6129         Store[x][y] = EL_EMPTY;
6130
6131       if (IS_CUSTOM_ELEMENT(center_element))
6132         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6133                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6134                        Store[x][y] >= EL_PREV_CE_8 &&
6135                        Store[x][y] <= EL_NEXT_CE_8 ?
6136                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6137                        Store[x][y]);
6138
6139       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6140           center_element == EL_AMOEBA_TO_DIAMOND)
6141         Store2[x][y] = element;
6142
6143       Tile[x][y] = EL_EXPLOSION;
6144       GfxElement[x][y] = artwork_element;
6145
6146       ExplodePhase[x][y] = 1;
6147       ExplodeDelay[x][y] = last_phase;
6148
6149       Stop[x][y] = TRUE;
6150     }
6151
6152     if (center_element == EL_YAMYAM)
6153       game.yamyam_content_nr =
6154         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6155
6156     return;
6157   }
6158
6159   if (Stop[ex][ey])
6160     return;
6161
6162   x = ex;
6163   y = ey;
6164
6165   if (phase == 1)
6166     GfxFrame[x][y] = 0;         // restart explosion animation
6167
6168   last_phase = ExplodeDelay[x][y];
6169
6170   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6171
6172   // this can happen if the player leaves an explosion just in time
6173   if (GfxElement[x][y] == EL_UNDEFINED)
6174     GfxElement[x][y] = EL_EMPTY;
6175
6176   border_element = Store2[x][y];
6177   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6178     border_element = StorePlayer[x][y];
6179
6180   if (phase == element_info[border_element].ignition_delay ||
6181       phase == last_phase)
6182   {
6183     boolean border_explosion = FALSE;
6184
6185     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6186         !PLAYER_EXPLOSION_PROTECTED(x, y))
6187     {
6188       KillPlayerUnlessExplosionProtected(x, y);
6189       border_explosion = TRUE;
6190     }
6191     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6192     {
6193       Tile[x][y] = Store2[x][y];
6194       Store2[x][y] = 0;
6195       Bang(x, y);
6196       border_explosion = TRUE;
6197     }
6198     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6199     {
6200       AmoebaToDiamond(x, y);
6201       Store2[x][y] = 0;
6202       border_explosion = TRUE;
6203     }
6204
6205     // if an element just explodes due to another explosion (chain-reaction),
6206     // do not immediately end the new explosion when it was the last frame of
6207     // the explosion (as it would be done in the following "if"-statement!)
6208     if (border_explosion && phase == last_phase)
6209       return;
6210   }
6211
6212   // this can happen if the player was just killed by an explosion
6213   if (GfxElement[x][y] == EL_UNDEFINED)
6214     GfxElement[x][y] = EL_EMPTY;
6215
6216   if (phase == last_phase)
6217   {
6218     int element;
6219
6220     element = Tile[x][y] = Store[x][y];
6221     Store[x][y] = Store2[x][y] = 0;
6222     GfxElement[x][y] = EL_UNDEFINED;
6223
6224     // player can escape from explosions and might therefore be still alive
6225     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6226         element <= EL_PLAYER_IS_EXPLODING_4)
6227     {
6228       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6229       int explosion_element = EL_PLAYER_1 + player_nr;
6230       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6231       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6232
6233       if (level.use_explosion_element[player_nr])
6234         explosion_element = level.explosion_element[player_nr];
6235
6236       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6237                     element_info[explosion_element].content.e[xx][yy]);
6238     }
6239
6240     // restore probably existing indestructible background element
6241     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6242       element = Tile[x][y] = Back[x][y];
6243     Back[x][y] = 0;
6244
6245     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6246     GfxDir[x][y] = MV_NONE;
6247     ChangeDelay[x][y] = 0;
6248     ChangePage[x][y] = -1;
6249
6250     CustomValue[x][y] = 0;
6251
6252     InitField_WithBug2(x, y, FALSE);
6253
6254     TEST_DrawLevelField(x, y);
6255
6256     TestIfElementTouchesCustomElement(x, y);
6257
6258     if (GFX_CRUMBLED(element))
6259       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6260
6261     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6262       StorePlayer[x][y] = 0;
6263
6264     if (IS_PLAYER_ELEMENT(element))
6265       RelocatePlayer(x, y, element);
6266   }
6267   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6268   {
6269     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6270     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6271
6272     if (phase == 1)
6273       TEST_DrawLevelFieldCrumbled(x, y);
6274
6275     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6276     {
6277       DrawLevelElement(x, y, Back[x][y]);
6278       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6279     }
6280     else if (IS_WALKABLE_UNDER(Back[x][y]))
6281     {
6282       DrawLevelGraphic(x, y, graphic, frame);
6283       DrawLevelElementThruMask(x, y, Back[x][y]);
6284     }
6285     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6286       DrawLevelGraphic(x, y, graphic, frame);
6287   }
6288 }
6289
6290 static void DynaExplode(int ex, int ey)
6291 {
6292   int i, j;
6293   int dynabomb_element = Tile[ex][ey];
6294   int dynabomb_size = 1;
6295   boolean dynabomb_xl = FALSE;
6296   struct PlayerInfo *player;
6297   struct XY *xy = xy_topdown;
6298
6299   if (IS_ACTIVE_BOMB(dynabomb_element))
6300   {
6301     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302     dynabomb_size = player->dynabomb_size;
6303     dynabomb_xl = player->dynabomb_xl;
6304     player->dynabombs_left++;
6305   }
6306
6307   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6308
6309   for (i = 0; i < NUM_DIRECTIONS; i++)
6310   {
6311     for (j = 1; j <= dynabomb_size; j++)
6312     {
6313       int x = ex + j * xy[i].x;
6314       int y = ey + j * xy[i].y;
6315       int element;
6316
6317       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6318         break;
6319
6320       element = Tile[x][y];
6321
6322       // do not restart explosions of fields with active bombs
6323       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6324         continue;
6325
6326       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6327
6328       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329           !IS_DIGGABLE(element) && !dynabomb_xl)
6330         break;
6331     }
6332   }
6333 }
6334
6335 void Bang(int x, int y)
6336 {
6337   int element = MovingOrBlocked2Element(x, y);
6338   int explosion_type = EX_TYPE_NORMAL;
6339
6340   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6341   {
6342     struct PlayerInfo *player = PLAYERINFO(x, y);
6343
6344     element = Tile[x][y] = player->initial_element;
6345
6346     if (level.use_explosion_element[player->index_nr])
6347     {
6348       int explosion_element = level.explosion_element[player->index_nr];
6349
6350       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6351         explosion_type = EX_TYPE_CROSS;
6352       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6353         explosion_type = EX_TYPE_CENTER;
6354     }
6355   }
6356
6357   switch (element)
6358   {
6359     case EL_BUG:
6360     case EL_SPACESHIP:
6361     case EL_BD_BUTTERFLY:
6362     case EL_BD_FIREFLY:
6363     case EL_YAMYAM:
6364     case EL_DARK_YAMYAM:
6365     case EL_ROBOT:
6366     case EL_PACMAN:
6367     case EL_MOLE:
6368       RaiseScoreElement(element);
6369       break;
6370
6371     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6372     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6373     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6374     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6375     case EL_DYNABOMB_INCREASE_NUMBER:
6376     case EL_DYNABOMB_INCREASE_SIZE:
6377     case EL_DYNABOMB_INCREASE_POWER:
6378       explosion_type = EX_TYPE_DYNA;
6379       break;
6380
6381     case EL_DC_LANDMINE:
6382       explosion_type = EX_TYPE_CENTER;
6383       break;
6384
6385     case EL_PENGUIN:
6386     case EL_LAMP:
6387     case EL_LAMP_ACTIVE:
6388     case EL_AMOEBA_TO_DIAMOND:
6389       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6390         explosion_type = EX_TYPE_CENTER;
6391       break;
6392
6393     default:
6394       if (element_info[element].explosion_type == EXPLODES_CROSS)
6395         explosion_type = EX_TYPE_CROSS;
6396       else if (element_info[element].explosion_type == EXPLODES_1X1)
6397         explosion_type = EX_TYPE_CENTER;
6398       break;
6399   }
6400
6401   if (explosion_type == EX_TYPE_DYNA)
6402     DynaExplode(x, y);
6403   else
6404     Explode(x, y, EX_PHASE_START, explosion_type);
6405
6406   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6407 }
6408
6409 static void SplashAcid(int x, int y)
6410 {
6411   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6412       (!IN_LEV_FIELD(x - 1, y - 2) ||
6413        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6414     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6415
6416   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6417       (!IN_LEV_FIELD(x + 1, y - 2) ||
6418        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6419     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6420
6421   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6422 }
6423
6424 static void InitBeltMovement(void)
6425 {
6426   static int belt_base_element[4] =
6427   {
6428     EL_CONVEYOR_BELT_1_LEFT,
6429     EL_CONVEYOR_BELT_2_LEFT,
6430     EL_CONVEYOR_BELT_3_LEFT,
6431     EL_CONVEYOR_BELT_4_LEFT
6432   };
6433   static int belt_base_active_element[4] =
6434   {
6435     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6436     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6437     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6438     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6439   };
6440
6441   int x, y, i, j;
6442
6443   // set frame order for belt animation graphic according to belt direction
6444   for (i = 0; i < NUM_BELTS; i++)
6445   {
6446     int belt_nr = i;
6447
6448     for (j = 0; j < NUM_BELT_PARTS; j++)
6449     {
6450       int element = belt_base_active_element[belt_nr] + j;
6451       int graphic_1 = el2img(element);
6452       int graphic_2 = el2panelimg(element);
6453
6454       if (game.belt_dir[i] == MV_LEFT)
6455       {
6456         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6457         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6458       }
6459       else
6460       {
6461         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6462         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6463       }
6464     }
6465   }
6466
6467   SCAN_PLAYFIELD(x, y)
6468   {
6469     int element = Tile[x][y];
6470
6471     for (i = 0; i < NUM_BELTS; i++)
6472     {
6473       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6474       {
6475         int e_belt_nr = getBeltNrFromBeltElement(element);
6476         int belt_nr = i;
6477
6478         if (e_belt_nr == belt_nr)
6479         {
6480           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6481
6482           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6483         }
6484       }
6485     }
6486   }
6487 }
6488
6489 static void ToggleBeltSwitch(int x, int y)
6490 {
6491   static int belt_base_element[4] =
6492   {
6493     EL_CONVEYOR_BELT_1_LEFT,
6494     EL_CONVEYOR_BELT_2_LEFT,
6495     EL_CONVEYOR_BELT_3_LEFT,
6496     EL_CONVEYOR_BELT_4_LEFT
6497   };
6498   static int belt_base_active_element[4] =
6499   {
6500     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6501     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6502     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6503     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6504   };
6505   static int belt_base_switch_element[4] =
6506   {
6507     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6508     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6509     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6510     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6511   };
6512   static int belt_move_dir[4] =
6513   {
6514     MV_LEFT,
6515     MV_NONE,
6516     MV_RIGHT,
6517     MV_NONE,
6518   };
6519
6520   int element = Tile[x][y];
6521   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6522   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6523   int belt_dir = belt_move_dir[belt_dir_nr];
6524   int xx, yy, i;
6525
6526   if (!IS_BELT_SWITCH(element))
6527     return;
6528
6529   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6530   game.belt_dir[belt_nr] = belt_dir;
6531
6532   if (belt_dir_nr == 3)
6533     belt_dir_nr = 1;
6534
6535   // set frame order for belt animation graphic according to belt direction
6536   for (i = 0; i < NUM_BELT_PARTS; i++)
6537   {
6538     int element = belt_base_active_element[belt_nr] + i;
6539     int graphic_1 = el2img(element);
6540     int graphic_2 = el2panelimg(element);
6541
6542     if (belt_dir == MV_LEFT)
6543     {
6544       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6545       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6546     }
6547     else
6548     {
6549       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6550       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6551     }
6552   }
6553
6554   SCAN_PLAYFIELD(xx, yy)
6555   {
6556     int element = Tile[xx][yy];
6557
6558     if (IS_BELT_SWITCH(element))
6559     {
6560       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6561
6562       if (e_belt_nr == belt_nr)
6563       {
6564         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6565         TEST_DrawLevelField(xx, yy);
6566       }
6567     }
6568     else if (IS_BELT(element) && belt_dir != MV_NONE)
6569     {
6570       int e_belt_nr = getBeltNrFromBeltElement(element);
6571
6572       if (e_belt_nr == belt_nr)
6573       {
6574         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6575
6576         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6577         TEST_DrawLevelField(xx, yy);
6578       }
6579     }
6580     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6581     {
6582       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6583
6584       if (e_belt_nr == belt_nr)
6585       {
6586         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6587
6588         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6589         TEST_DrawLevelField(xx, yy);
6590       }
6591     }
6592   }
6593 }
6594
6595 static void ToggleSwitchgateSwitch(void)
6596 {
6597   int xx, yy;
6598
6599   game.switchgate_pos = !game.switchgate_pos;
6600
6601   SCAN_PLAYFIELD(xx, yy)
6602   {
6603     int element = Tile[xx][yy];
6604
6605     if (element == EL_SWITCHGATE_SWITCH_UP)
6606     {
6607       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6608       TEST_DrawLevelField(xx, yy);
6609     }
6610     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6611     {
6612       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6613       TEST_DrawLevelField(xx, yy);
6614     }
6615     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6616     {
6617       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6618       TEST_DrawLevelField(xx, yy);
6619     }
6620     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6621     {
6622       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6623       TEST_DrawLevelField(xx, yy);
6624     }
6625     else if (element == EL_SWITCHGATE_OPEN ||
6626              element == EL_SWITCHGATE_OPENING)
6627     {
6628       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6629
6630       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6631     }
6632     else if (element == EL_SWITCHGATE_CLOSED ||
6633              element == EL_SWITCHGATE_CLOSING)
6634     {
6635       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6636
6637       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6638     }
6639   }
6640 }
6641
6642 static int getInvisibleActiveFromInvisibleElement(int element)
6643 {
6644   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6645           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6646           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6647           element);
6648 }
6649
6650 static int getInvisibleFromInvisibleActiveElement(int element)
6651 {
6652   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6653           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6654           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6655           element);
6656 }
6657
6658 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6659 {
6660   int x, y;
6661
6662   SCAN_PLAYFIELD(x, y)
6663   {
6664     int element = Tile[x][y];
6665
6666     if (element == EL_LIGHT_SWITCH &&
6667         game.light_time_left > 0)
6668     {
6669       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6670       TEST_DrawLevelField(x, y);
6671     }
6672     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6673              game.light_time_left == 0)
6674     {
6675       Tile[x][y] = EL_LIGHT_SWITCH;
6676       TEST_DrawLevelField(x, y);
6677     }
6678     else if (element == EL_EMC_DRIPPER &&
6679              game.light_time_left > 0)
6680     {
6681       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6682       TEST_DrawLevelField(x, y);
6683     }
6684     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6685              game.light_time_left == 0)
6686     {
6687       Tile[x][y] = EL_EMC_DRIPPER;
6688       TEST_DrawLevelField(x, y);
6689     }
6690     else if (element == EL_INVISIBLE_STEELWALL ||
6691              element == EL_INVISIBLE_WALL ||
6692              element == EL_INVISIBLE_SAND)
6693     {
6694       if (game.light_time_left > 0)
6695         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6696
6697       TEST_DrawLevelField(x, y);
6698
6699       // uncrumble neighbour fields, if needed
6700       if (element == EL_INVISIBLE_SAND)
6701         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6702     }
6703     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6704              element == EL_INVISIBLE_WALL_ACTIVE ||
6705              element == EL_INVISIBLE_SAND_ACTIVE)
6706     {
6707       if (game.light_time_left == 0)
6708         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6709
6710       TEST_DrawLevelField(x, y);
6711
6712       // re-crumble neighbour fields, if needed
6713       if (element == EL_INVISIBLE_SAND)
6714         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6715     }
6716   }
6717 }
6718
6719 static void RedrawAllInvisibleElementsForLenses(void)
6720 {
6721   int x, y;
6722
6723   SCAN_PLAYFIELD(x, y)
6724   {
6725     int element = Tile[x][y];
6726
6727     if (element == EL_EMC_DRIPPER &&
6728         game.lenses_time_left > 0)
6729     {
6730       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6731       TEST_DrawLevelField(x, y);
6732     }
6733     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6734              game.lenses_time_left == 0)
6735     {
6736       Tile[x][y] = EL_EMC_DRIPPER;
6737       TEST_DrawLevelField(x, y);
6738     }
6739     else if (element == EL_INVISIBLE_STEELWALL ||
6740              element == EL_INVISIBLE_WALL ||
6741              element == EL_INVISIBLE_SAND)
6742     {
6743       if (game.lenses_time_left > 0)
6744         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6745
6746       TEST_DrawLevelField(x, y);
6747
6748       // uncrumble neighbour fields, if needed
6749       if (element == EL_INVISIBLE_SAND)
6750         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6751     }
6752     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6753              element == EL_INVISIBLE_WALL_ACTIVE ||
6754              element == EL_INVISIBLE_SAND_ACTIVE)
6755     {
6756       if (game.lenses_time_left == 0)
6757         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6758
6759       TEST_DrawLevelField(x, y);
6760
6761       // re-crumble neighbour fields, if needed
6762       if (element == EL_INVISIBLE_SAND)
6763         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6764     }
6765   }
6766 }
6767
6768 static void RedrawAllInvisibleElementsForMagnifier(void)
6769 {
6770   int x, y;
6771
6772   SCAN_PLAYFIELD(x, y)
6773   {
6774     int element = Tile[x][y];
6775
6776     if (element == EL_EMC_FAKE_GRASS &&
6777         game.magnify_time_left > 0)
6778     {
6779       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6780       TEST_DrawLevelField(x, y);
6781     }
6782     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6783              game.magnify_time_left == 0)
6784     {
6785       Tile[x][y] = EL_EMC_FAKE_GRASS;
6786       TEST_DrawLevelField(x, y);
6787     }
6788     else if (IS_GATE_GRAY(element) &&
6789              game.magnify_time_left > 0)
6790     {
6791       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6792                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6793                     IS_EM_GATE_GRAY(element) ?
6794                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6795                     IS_EMC_GATE_GRAY(element) ?
6796                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6797                     IS_DC_GATE_GRAY(element) ?
6798                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6799                     element);
6800       TEST_DrawLevelField(x, y);
6801     }
6802     else if (IS_GATE_GRAY_ACTIVE(element) &&
6803              game.magnify_time_left == 0)
6804     {
6805       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6806                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6807                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6808                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6809                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6810                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6811                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6812                     EL_DC_GATE_WHITE_GRAY :
6813                     element);
6814       TEST_DrawLevelField(x, y);
6815     }
6816   }
6817 }
6818
6819 static void ToggleLightSwitch(int x, int y)
6820 {
6821   int element = Tile[x][y];
6822
6823   game.light_time_left =
6824     (element == EL_LIGHT_SWITCH ?
6825      level.time_light * FRAMES_PER_SECOND : 0);
6826
6827   RedrawAllLightSwitchesAndInvisibleElements();
6828 }
6829
6830 static void ActivateTimegateSwitch(int x, int y)
6831 {
6832   int xx, yy;
6833
6834   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6835
6836   SCAN_PLAYFIELD(xx, yy)
6837   {
6838     int element = Tile[xx][yy];
6839
6840     if (element == EL_TIMEGATE_CLOSED ||
6841         element == EL_TIMEGATE_CLOSING)
6842     {
6843       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6844       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6845     }
6846
6847     /*
6848     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6849     {
6850       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6851       TEST_DrawLevelField(xx, yy);
6852     }
6853     */
6854
6855   }
6856
6857   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6858                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6859 }
6860
6861 static void Impact(int x, int y)
6862 {
6863   boolean last_line = (y == lev_fieldy - 1);
6864   boolean object_hit = FALSE;
6865   boolean impact = (last_line || object_hit);
6866   int element = Tile[x][y];
6867   int smashed = EL_STEELWALL;
6868
6869   if (!last_line)       // check if element below was hit
6870   {
6871     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6872       return;
6873
6874     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6875                                          MovDir[x][y + 1] != MV_DOWN ||
6876                                          MovPos[x][y + 1] <= TILEY / 2));
6877
6878     // do not smash moving elements that left the smashed field in time
6879     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6880         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6881       object_hit = FALSE;
6882
6883 #if USE_QUICKSAND_IMPACT_BUGFIX
6884     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6885     {
6886       RemoveMovingField(x, y + 1);
6887       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6888       Tile[x][y + 2] = EL_ROCK;
6889       TEST_DrawLevelField(x, y + 2);
6890
6891       object_hit = TRUE;
6892     }
6893
6894     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6895     {
6896       RemoveMovingField(x, y + 1);
6897       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6898       Tile[x][y + 2] = EL_ROCK;
6899       TEST_DrawLevelField(x, y + 2);
6900
6901       object_hit = TRUE;
6902     }
6903 #endif
6904
6905     if (object_hit)
6906       smashed = MovingOrBlocked2Element(x, y + 1);
6907
6908     impact = (last_line || object_hit);
6909   }
6910
6911   if (!last_line && smashed == EL_ACID) // element falls into acid
6912   {
6913     SplashAcid(x, y + 1);
6914     return;
6915   }
6916
6917   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6918   // only reset graphic animation if graphic really changes after impact
6919   if (impact &&
6920       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6921   {
6922     ResetGfxAnimation(x, y);
6923     TEST_DrawLevelField(x, y);
6924   }
6925
6926   if (impact && CAN_EXPLODE_IMPACT(element))
6927   {
6928     Bang(x, y);
6929     return;
6930   }
6931   else if (impact && element == EL_PEARL &&
6932            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6933   {
6934     ResetGfxAnimation(x, y);
6935
6936     Tile[x][y] = EL_PEARL_BREAKING;
6937     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6938     return;
6939   }
6940   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6941   {
6942     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6943
6944     return;
6945   }
6946
6947   if (impact && element == EL_AMOEBA_DROP)
6948   {
6949     if (object_hit && IS_PLAYER(x, y + 1))
6950       KillPlayerUnlessEnemyProtected(x, y + 1);
6951     else if (object_hit && smashed == EL_PENGUIN)
6952       Bang(x, y + 1);
6953     else
6954     {
6955       Tile[x][y] = EL_AMOEBA_GROWING;
6956       Store[x][y] = EL_AMOEBA_WET;
6957
6958       ResetRandomAnimationValue(x, y);
6959     }
6960     return;
6961   }
6962
6963   if (object_hit)               // check which object was hit
6964   {
6965     if ((CAN_PASS_MAGIC_WALL(element) && 
6966          (smashed == EL_MAGIC_WALL ||
6967           smashed == EL_BD_MAGIC_WALL)) ||
6968         (CAN_PASS_DC_MAGIC_WALL(element) &&
6969          smashed == EL_DC_MAGIC_WALL))
6970     {
6971       int xx, yy;
6972       int activated_magic_wall =
6973         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6974          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6975          EL_DC_MAGIC_WALL_ACTIVE);
6976
6977       // activate magic wall / mill
6978       SCAN_PLAYFIELD(xx, yy)
6979       {
6980         if (Tile[xx][yy] == smashed)
6981           Tile[xx][yy] = activated_magic_wall;
6982       }
6983
6984       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6985       game.magic_wall_active = TRUE;
6986
6987       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6988                             SND_MAGIC_WALL_ACTIVATING :
6989                             smashed == EL_BD_MAGIC_WALL ?
6990                             SND_BD_MAGIC_WALL_ACTIVATING :
6991                             SND_DC_MAGIC_WALL_ACTIVATING));
6992     }
6993
6994     if (IS_PLAYER(x, y + 1))
6995     {
6996       if (CAN_SMASH_PLAYER(element))
6997       {
6998         KillPlayerUnlessEnemyProtected(x, y + 1);
6999         return;
7000       }
7001     }
7002     else if (smashed == EL_PENGUIN)
7003     {
7004       if (CAN_SMASH_PLAYER(element))
7005       {
7006         Bang(x, y + 1);
7007         return;
7008       }
7009     }
7010     else if (element == EL_BD_DIAMOND)
7011     {
7012       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7013       {
7014         Bang(x, y + 1);
7015         return;
7016       }
7017     }
7018     else if (((element == EL_SP_INFOTRON ||
7019                element == EL_SP_ZONK) &&
7020               (smashed == EL_SP_SNIKSNAK ||
7021                smashed == EL_SP_ELECTRON ||
7022                smashed == EL_SP_DISK_ORANGE)) ||
7023              (element == EL_SP_INFOTRON &&
7024               smashed == EL_SP_DISK_YELLOW))
7025     {
7026       Bang(x, y + 1);
7027       return;
7028     }
7029     else if (CAN_SMASH_EVERYTHING(element))
7030     {
7031       if (IS_CLASSIC_ENEMY(smashed) ||
7032           CAN_EXPLODE_SMASHED(smashed))
7033       {
7034         Bang(x, y + 1);
7035         return;
7036       }
7037       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7038       {
7039         if (smashed == EL_LAMP ||
7040             smashed == EL_LAMP_ACTIVE)
7041         {
7042           Bang(x, y + 1);
7043           return;
7044         }
7045         else if (smashed == EL_NUT)
7046         {
7047           Tile[x][y + 1] = EL_NUT_BREAKING;
7048           PlayLevelSound(x, y, SND_NUT_BREAKING);
7049           RaiseScoreElement(EL_NUT);
7050           return;
7051         }
7052         else if (smashed == EL_PEARL)
7053         {
7054           ResetGfxAnimation(x, y);
7055
7056           Tile[x][y + 1] = EL_PEARL_BREAKING;
7057           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7058           return;
7059         }
7060         else if (smashed == EL_DIAMOND)
7061         {
7062           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7063           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7064           return;
7065         }
7066         else if (IS_BELT_SWITCH(smashed))
7067         {
7068           ToggleBeltSwitch(x, y + 1);
7069         }
7070         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7071                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7072                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7073                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7074         {
7075           ToggleSwitchgateSwitch();
7076         }
7077         else if (smashed == EL_LIGHT_SWITCH ||
7078                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7079         {
7080           ToggleLightSwitch(x, y + 1);
7081         }
7082         else
7083         {
7084           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7085
7086           CheckElementChangeBySide(x, y + 1, smashed, element,
7087                                    CE_SWITCHED, CH_SIDE_TOP);
7088           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7089                                             CH_SIDE_TOP);
7090         }
7091       }
7092       else
7093       {
7094         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7095       }
7096     }
7097   }
7098
7099   // play sound of magic wall / mill
7100   if (!last_line &&
7101       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7102        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7103        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7104   {
7105     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7106       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7107     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7108       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7109     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7110       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7111
7112     return;
7113   }
7114
7115   // play sound of object that hits the ground
7116   if (last_line || object_hit)
7117     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7118 }
7119
7120 static void TurnRoundExt(int x, int y)
7121 {
7122   static struct
7123   {
7124     int dx, dy;
7125   } move_xy[] =
7126   {
7127     {  0,  0 },
7128     { -1,  0 },
7129     { +1,  0 },
7130     {  0,  0 },
7131     {  0, -1 },
7132     {  0,  0 }, { 0, 0 }, { 0, 0 },
7133     {  0, +1 }
7134   };
7135   static struct
7136   {
7137     int left, right, back;
7138   } turn[] =
7139   {
7140     { 0,        0,              0        },
7141     { MV_DOWN,  MV_UP,          MV_RIGHT },
7142     { MV_UP,    MV_DOWN,        MV_LEFT  },
7143     { 0,        0,              0        },
7144     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7145     { 0,        0,              0        },
7146     { 0,        0,              0        },
7147     { 0,        0,              0        },
7148     { MV_RIGHT, MV_LEFT,        MV_UP    }
7149   };
7150
7151   int element = Tile[x][y];
7152   int move_pattern = element_info[element].move_pattern;
7153
7154   int old_move_dir = MovDir[x][y];
7155   int left_dir  = turn[old_move_dir].left;
7156   int right_dir = turn[old_move_dir].right;
7157   int back_dir  = turn[old_move_dir].back;
7158
7159   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7160   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7161   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7162   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7163
7164   int left_x  = x + left_dx,  left_y  = y + left_dy;
7165   int right_x = x + right_dx, right_y = y + right_dy;
7166   int move_x  = x + move_dx,  move_y  = y + move_dy;
7167
7168   int xx, yy;
7169
7170   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7171   {
7172     TestIfBadThingTouchesOtherBadThing(x, y);
7173
7174     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7175       MovDir[x][y] = right_dir;
7176     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7177       MovDir[x][y] = left_dir;
7178
7179     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7180       MovDelay[x][y] = 9;
7181     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7182       MovDelay[x][y] = 1;
7183   }
7184   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7185   {
7186     TestIfBadThingTouchesOtherBadThing(x, y);
7187
7188     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7189       MovDir[x][y] = left_dir;
7190     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7191       MovDir[x][y] = right_dir;
7192
7193     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7194       MovDelay[x][y] = 9;
7195     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7196       MovDelay[x][y] = 1;
7197   }
7198   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7199   {
7200     TestIfBadThingTouchesOtherBadThing(x, y);
7201
7202     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7203       MovDir[x][y] = left_dir;
7204     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7205       MovDir[x][y] = right_dir;
7206
7207     if (MovDir[x][y] != old_move_dir)
7208       MovDelay[x][y] = 9;
7209   }
7210   else if (element == EL_YAMYAM)
7211   {
7212     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7213     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7214
7215     if (can_turn_left && can_turn_right)
7216       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7217     else if (can_turn_left)
7218       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7219     else if (can_turn_right)
7220       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7221     else
7222       MovDir[x][y] = back_dir;
7223
7224     MovDelay[x][y] = 16 + 16 * RND(3);
7225   }
7226   else if (element == EL_DARK_YAMYAM)
7227   {
7228     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7229                                                          left_x, left_y);
7230     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7231                                                          right_x, right_y);
7232
7233     if (can_turn_left && can_turn_right)
7234       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7235     else if (can_turn_left)
7236       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7237     else if (can_turn_right)
7238       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7239     else
7240       MovDir[x][y] = back_dir;
7241
7242     MovDelay[x][y] = 16 + 16 * RND(3);
7243   }
7244   else if (element == EL_PACMAN)
7245   {
7246     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7247     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7248
7249     if (can_turn_left && can_turn_right)
7250       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7251     else if (can_turn_left)
7252       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7253     else if (can_turn_right)
7254       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7255     else
7256       MovDir[x][y] = back_dir;
7257
7258     MovDelay[x][y] = 6 + RND(40);
7259   }
7260   else if (element == EL_PIG)
7261   {
7262     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7263     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7264     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7265     boolean should_turn_left, should_turn_right, should_move_on;
7266     int rnd_value = 24;
7267     int rnd = RND(rnd_value);
7268
7269     should_turn_left = (can_turn_left &&
7270                         (!can_move_on ||
7271                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7272                                                    y + back_dy + left_dy)));
7273     should_turn_right = (can_turn_right &&
7274                          (!can_move_on ||
7275                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7276                                                     y + back_dy + right_dy)));
7277     should_move_on = (can_move_on &&
7278                       (!can_turn_left ||
7279                        !can_turn_right ||
7280                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7281                                                  y + move_dy + left_dy) ||
7282                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7283                                                  y + move_dy + right_dy)));
7284
7285     if (should_turn_left || should_turn_right || should_move_on)
7286     {
7287       if (should_turn_left && should_turn_right && should_move_on)
7288         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7289                         rnd < 2 * rnd_value / 3 ? right_dir :
7290                         old_move_dir);
7291       else if (should_turn_left && should_turn_right)
7292         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7293       else if (should_turn_left && should_move_on)
7294         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7295       else if (should_turn_right && should_move_on)
7296         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7297       else if (should_turn_left)
7298         MovDir[x][y] = left_dir;
7299       else if (should_turn_right)
7300         MovDir[x][y] = right_dir;
7301       else if (should_move_on)
7302         MovDir[x][y] = old_move_dir;
7303     }
7304     else if (can_move_on && rnd > rnd_value / 8)
7305       MovDir[x][y] = old_move_dir;
7306     else if (can_turn_left && can_turn_right)
7307       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7308     else if (can_turn_left && rnd > rnd_value / 8)
7309       MovDir[x][y] = left_dir;
7310     else if (can_turn_right && rnd > rnd_value/8)
7311       MovDir[x][y] = right_dir;
7312     else
7313       MovDir[x][y] = back_dir;
7314
7315     xx = x + move_xy[MovDir[x][y]].dx;
7316     yy = y + move_xy[MovDir[x][y]].dy;
7317
7318     if (!IN_LEV_FIELD(xx, yy) ||
7319         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7320       MovDir[x][y] = old_move_dir;
7321
7322     MovDelay[x][y] = 0;
7323   }
7324   else if (element == EL_DRAGON)
7325   {
7326     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7327     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7328     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7329     int rnd_value = 24;
7330     int rnd = RND(rnd_value);
7331
7332     if (can_move_on && rnd > rnd_value / 8)
7333       MovDir[x][y] = old_move_dir;
7334     else if (can_turn_left && can_turn_right)
7335       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7336     else if (can_turn_left && rnd > rnd_value / 8)
7337       MovDir[x][y] = left_dir;
7338     else if (can_turn_right && rnd > rnd_value / 8)
7339       MovDir[x][y] = right_dir;
7340     else
7341       MovDir[x][y] = back_dir;
7342
7343     xx = x + move_xy[MovDir[x][y]].dx;
7344     yy = y + move_xy[MovDir[x][y]].dy;
7345
7346     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7347       MovDir[x][y] = old_move_dir;
7348
7349     MovDelay[x][y] = 0;
7350   }
7351   else if (element == EL_MOLE)
7352   {
7353     boolean can_move_on =
7354       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7355                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7356                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7357     if (!can_move_on)
7358     {
7359       boolean can_turn_left =
7360         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7361                               IS_AMOEBOID(Tile[left_x][left_y])));
7362
7363       boolean can_turn_right =
7364         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7365                               IS_AMOEBOID(Tile[right_x][right_y])));
7366
7367       if (can_turn_left && can_turn_right)
7368         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7369       else if (can_turn_left)
7370         MovDir[x][y] = left_dir;
7371       else
7372         MovDir[x][y] = right_dir;
7373     }
7374
7375     if (MovDir[x][y] != old_move_dir)
7376       MovDelay[x][y] = 9;
7377   }
7378   else if (element == EL_BALLOON)
7379   {
7380     MovDir[x][y] = game.wind_direction;
7381     MovDelay[x][y] = 0;
7382   }
7383   else if (element == EL_SPRING)
7384   {
7385     if (MovDir[x][y] & MV_HORIZONTAL)
7386     {
7387       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7388           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7389       {
7390         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7391         ResetGfxAnimation(move_x, move_y);
7392         TEST_DrawLevelField(move_x, move_y);
7393
7394         MovDir[x][y] = back_dir;
7395       }
7396       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7397                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7398         MovDir[x][y] = MV_NONE;
7399     }
7400
7401     MovDelay[x][y] = 0;
7402   }
7403   else if (element == EL_ROBOT ||
7404            element == EL_SATELLITE ||
7405            element == EL_PENGUIN ||
7406            element == EL_EMC_ANDROID)
7407   {
7408     int attr_x = -1, attr_y = -1;
7409
7410     if (game.all_players_gone)
7411     {
7412       attr_x = game.exit_x;
7413       attr_y = game.exit_y;
7414     }
7415     else
7416     {
7417       int i;
7418
7419       for (i = 0; i < MAX_PLAYERS; i++)
7420       {
7421         struct PlayerInfo *player = &stored_player[i];
7422         int jx = player->jx, jy = player->jy;
7423
7424         if (!player->active)
7425           continue;
7426
7427         if (attr_x == -1 ||
7428             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7429         {
7430           attr_x = jx;
7431           attr_y = jy;
7432         }
7433       }
7434     }
7435
7436     if (element == EL_ROBOT &&
7437         game.robot_wheel_x >= 0 &&
7438         game.robot_wheel_y >= 0 &&
7439         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7440          game.engine_version < VERSION_IDENT(3,1,0,0)))
7441     {
7442       attr_x = game.robot_wheel_x;
7443       attr_y = game.robot_wheel_y;
7444     }
7445
7446     if (element == EL_PENGUIN)
7447     {
7448       int i;
7449       struct XY *xy = xy_topdown;
7450
7451       for (i = 0; i < NUM_DIRECTIONS; i++)
7452       {
7453         int ex = x + xy[i].x;
7454         int ey = y + xy[i].y;
7455
7456         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7457                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7458                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7459                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7460         {
7461           attr_x = ex;
7462           attr_y = ey;
7463           break;
7464         }
7465       }
7466     }
7467
7468     MovDir[x][y] = MV_NONE;
7469     if (attr_x < x)
7470       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7471     else if (attr_x > x)
7472       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7473     if (attr_y < y)
7474       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7475     else if (attr_y > y)
7476       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7477
7478     if (element == EL_ROBOT)
7479     {
7480       int newx, newy;
7481
7482       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7483         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7484       Moving2Blocked(x, y, &newx, &newy);
7485
7486       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7487         MovDelay[x][y] = 8 + 8 * !RND(3);
7488       else
7489         MovDelay[x][y] = 16;
7490     }
7491     else if (element == EL_PENGUIN)
7492     {
7493       int newx, newy;
7494
7495       MovDelay[x][y] = 1;
7496
7497       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7498       {
7499         boolean first_horiz = RND(2);
7500         int new_move_dir = MovDir[x][y];
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] =
7510           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7511         Moving2Blocked(x, y, &newx, &newy);
7512
7513         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7514           return;
7515
7516         MovDir[x][y] = old_move_dir;
7517         return;
7518       }
7519     }
7520     else if (element == EL_SATELLITE)
7521     {
7522       int newx, newy;
7523
7524       MovDelay[x][y] = 1;
7525
7526       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7527       {
7528         boolean first_horiz = RND(2);
7529         int new_move_dir = MovDir[x][y];
7530
7531         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] =
7539           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7540         Moving2Blocked(x, y, &newx, &newy);
7541
7542         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7543           return;
7544
7545         MovDir[x][y] = old_move_dir;
7546         return;
7547       }
7548     }
7549     else if (element == EL_EMC_ANDROID)
7550     {
7551       static int check_pos[16] =
7552       {
7553         -1,             //  0 => (invalid)
7554         7,              //  1 => MV_LEFT
7555         3,              //  2 => MV_RIGHT
7556         -1,             //  3 => (invalid)
7557         1,              //  4 =>            MV_UP
7558         0,              //  5 => MV_LEFT  | MV_UP
7559         2,              //  6 => MV_RIGHT | MV_UP
7560         -1,             //  7 => (invalid)
7561         5,              //  8 =>            MV_DOWN
7562         6,              //  9 => MV_LEFT  | MV_DOWN
7563         4,              // 10 => MV_RIGHT | MV_DOWN
7564         -1,             // 11 => (invalid)
7565         -1,             // 12 => (invalid)
7566         -1,             // 13 => (invalid)
7567         -1,             // 14 => (invalid)
7568         -1,             // 15 => (invalid)
7569       };
7570       static struct
7571       {
7572         int dx, dy;
7573         int dir;
7574       } check_xy[8] =
7575       {
7576         { -1, -1,       MV_LEFT  | MV_UP   },
7577         {  0, -1,                  MV_UP   },
7578         { +1, -1,       MV_RIGHT | MV_UP   },
7579         { +1,  0,       MV_RIGHT           },
7580         { +1, +1,       MV_RIGHT | MV_DOWN },
7581         {  0, +1,                  MV_DOWN },
7582         { -1, +1,       MV_LEFT  | MV_DOWN },
7583         { -1,  0,       MV_LEFT            },
7584       };
7585       int start_pos, check_order;
7586       boolean can_clone = FALSE;
7587       int i;
7588
7589       // check if there is any free field around current position
7590       for (i = 0; i < 8; i++)
7591       {
7592         int newx = x + check_xy[i].dx;
7593         int newy = y + check_xy[i].dy;
7594
7595         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7596         {
7597           can_clone = TRUE;
7598
7599           break;
7600         }
7601       }
7602
7603       if (can_clone)            // randomly find an element to clone
7604       {
7605         can_clone = FALSE;
7606
7607         start_pos = check_pos[RND(8)];
7608         check_order = (RND(2) ? -1 : +1);
7609
7610         for (i = 0; i < 8; i++)
7611         {
7612           int pos_raw = start_pos + i * check_order;
7613           int pos = (pos_raw + 8) % 8;
7614           int newx = x + check_xy[pos].dx;
7615           int newy = y + check_xy[pos].dy;
7616
7617           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7618           {
7619             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7620             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7621
7622             Store[x][y] = Tile[newx][newy];
7623
7624             can_clone = TRUE;
7625
7626             break;
7627           }
7628         }
7629       }
7630
7631       if (can_clone)            // randomly find a direction to move
7632       {
7633         can_clone = FALSE;
7634
7635         start_pos = check_pos[RND(8)];
7636         check_order = (RND(2) ? -1 : +1);
7637
7638         for (i = 0; i < 8; i++)
7639         {
7640           int pos_raw = start_pos + i * check_order;
7641           int pos = (pos_raw + 8) % 8;
7642           int newx = x + check_xy[pos].dx;
7643           int newy = y + check_xy[pos].dy;
7644           int new_move_dir = check_xy[pos].dir;
7645
7646           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7647           {
7648             MovDir[x][y] = new_move_dir;
7649             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7650
7651             can_clone = TRUE;
7652
7653             break;
7654           }
7655         }
7656       }
7657
7658       if (can_clone)            // cloning and moving successful
7659         return;
7660
7661       // cannot clone -- try to move towards player
7662
7663       start_pos = check_pos[MovDir[x][y] & 0x0f];
7664       check_order = (RND(2) ? -1 : +1);
7665
7666       for (i = 0; i < 3; i++)
7667       {
7668         // first check start_pos, then previous/next or (next/previous) pos
7669         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7670         int pos = (pos_raw + 8) % 8;
7671         int newx = x + check_xy[pos].dx;
7672         int newy = y + check_xy[pos].dy;
7673         int new_move_dir = check_xy[pos].dir;
7674
7675         if (IS_PLAYER(newx, newy))
7676           break;
7677
7678         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7679         {
7680           MovDir[x][y] = new_move_dir;
7681           MovDelay[x][y] = level.android_move_time * 8 + 1;
7682
7683           break;
7684         }
7685       }
7686     }
7687   }
7688   else if (move_pattern == MV_TURNING_LEFT ||
7689            move_pattern == MV_TURNING_RIGHT ||
7690            move_pattern == MV_TURNING_LEFT_RIGHT ||
7691            move_pattern == MV_TURNING_RIGHT_LEFT ||
7692            move_pattern == MV_TURNING_RANDOM ||
7693            move_pattern == MV_ALL_DIRECTIONS)
7694   {
7695     boolean can_turn_left =
7696       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7697     boolean can_turn_right =
7698       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7699
7700     if (element_info[element].move_stepsize == 0)       // "not moving"
7701       return;
7702
7703     if (move_pattern == MV_TURNING_LEFT)
7704       MovDir[x][y] = left_dir;
7705     else if (move_pattern == MV_TURNING_RIGHT)
7706       MovDir[x][y] = right_dir;
7707     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7708       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7709     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7710       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7711     else if (move_pattern == MV_TURNING_RANDOM)
7712       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7713                       can_turn_right && !can_turn_left ? right_dir :
7714                       RND(2) ? left_dir : right_dir);
7715     else if (can_turn_left && can_turn_right)
7716       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7717     else if (can_turn_left)
7718       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7719     else if (can_turn_right)
7720       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7721     else
7722       MovDir[x][y] = back_dir;
7723
7724     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7725   }
7726   else if (move_pattern == MV_HORIZONTAL ||
7727            move_pattern == MV_VERTICAL)
7728   {
7729     if (move_pattern & old_move_dir)
7730       MovDir[x][y] = back_dir;
7731     else if (move_pattern == MV_HORIZONTAL)
7732       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7733     else if (move_pattern == MV_VERTICAL)
7734       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7735
7736     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7737   }
7738   else if (move_pattern & MV_ANY_DIRECTION)
7739   {
7740     MovDir[x][y] = move_pattern;
7741     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7742   }
7743   else if (move_pattern & MV_WIND_DIRECTION)
7744   {
7745     MovDir[x][y] = game.wind_direction;
7746     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7747   }
7748   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7749   {
7750     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7751       MovDir[x][y] = left_dir;
7752     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7753       MovDir[x][y] = right_dir;
7754
7755     if (MovDir[x][y] != old_move_dir)
7756       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7757   }
7758   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7759   {
7760     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7761       MovDir[x][y] = right_dir;
7762     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763       MovDir[x][y] = left_dir;
7764
7765     if (MovDir[x][y] != old_move_dir)
7766       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7767   }
7768   else if (move_pattern == MV_TOWARDS_PLAYER ||
7769            move_pattern == MV_AWAY_FROM_PLAYER)
7770   {
7771     int attr_x = -1, attr_y = -1;
7772     int newx, newy;
7773     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7774
7775     if (game.all_players_gone)
7776     {
7777       attr_x = game.exit_x;
7778       attr_y = game.exit_y;
7779     }
7780     else
7781     {
7782       int i;
7783
7784       for (i = 0; i < MAX_PLAYERS; i++)
7785       {
7786         struct PlayerInfo *player = &stored_player[i];
7787         int jx = player->jx, jy = player->jy;
7788
7789         if (!player->active)
7790           continue;
7791
7792         if (attr_x == -1 ||
7793             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7794         {
7795           attr_x = jx;
7796           attr_y = jy;
7797         }
7798       }
7799     }
7800
7801     MovDir[x][y] = MV_NONE;
7802     if (attr_x < x)
7803       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7804     else if (attr_x > x)
7805       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7806     if (attr_y < y)
7807       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7808     else if (attr_y > y)
7809       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7810
7811     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7812
7813     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7814     {
7815       boolean first_horiz = RND(2);
7816       int new_move_dir = MovDir[x][y];
7817
7818       if (element_info[element].move_stepsize == 0)     // "not moving"
7819       {
7820         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7821         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7822
7823         return;
7824       }
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] =
7834         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7835       Moving2Blocked(x, y, &newx, &newy);
7836
7837       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7838         return;
7839
7840       MovDir[x][y] = old_move_dir;
7841     }
7842   }
7843   else if (move_pattern == MV_WHEN_PUSHED ||
7844            move_pattern == MV_WHEN_DROPPED)
7845   {
7846     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7847       MovDir[x][y] = MV_NONE;
7848
7849     MovDelay[x][y] = 0;
7850   }
7851   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7852   {
7853     struct XY *test_xy = xy_topdown;
7854     static int test_dir[4] =
7855     {
7856       MV_UP,
7857       MV_LEFT,
7858       MV_RIGHT,
7859       MV_DOWN
7860     };
7861     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7862     int move_preference = -1000000;     // start with very low preference
7863     int new_move_dir = MV_NONE;
7864     int start_test = RND(4);
7865     int i;
7866
7867     for (i = 0; i < NUM_DIRECTIONS; i++)
7868     {
7869       int j = (start_test + i) % 4;
7870       int move_dir = test_dir[j];
7871       int move_dir_preference;
7872
7873       xx = x + test_xy[j].x;
7874       yy = y + test_xy[j].y;
7875
7876       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7877           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7878       {
7879         new_move_dir = move_dir;
7880
7881         break;
7882       }
7883
7884       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7885         continue;
7886
7887       move_dir_preference = -1 * RunnerVisit[xx][yy];
7888       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7889         move_dir_preference = PlayerVisit[xx][yy];
7890
7891       if (move_dir_preference > move_preference)
7892       {
7893         // prefer field that has not been visited for the longest time
7894         move_preference = move_dir_preference;
7895         new_move_dir = move_dir;
7896       }
7897       else if (move_dir_preference == move_preference &&
7898                move_dir == old_move_dir)
7899       {
7900         // prefer last direction when all directions are preferred equally
7901         move_preference = move_dir_preference;
7902         new_move_dir = move_dir;
7903       }
7904     }
7905
7906     MovDir[x][y] = new_move_dir;
7907     if (old_move_dir != new_move_dir)
7908       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7909   }
7910 }
7911
7912 static void TurnRound(int x, int y)
7913 {
7914   int direction = MovDir[x][y];
7915
7916   TurnRoundExt(x, y);
7917
7918   GfxDir[x][y] = MovDir[x][y];
7919
7920   if (direction != MovDir[x][y])
7921     GfxFrame[x][y] = 0;
7922
7923   if (MovDelay[x][y])
7924     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7925
7926   ResetGfxFrame(x, y);
7927 }
7928
7929 static boolean JustBeingPushed(int x, int y)
7930 {
7931   int i;
7932
7933   for (i = 0; i < MAX_PLAYERS; i++)
7934   {
7935     struct PlayerInfo *player = &stored_player[i];
7936
7937     if (player->active && player->is_pushing && player->MovPos)
7938     {
7939       int next_jx = player->jx + (player->jx - player->last_jx);
7940       int next_jy = player->jy + (player->jy - player->last_jy);
7941
7942       if (x == next_jx && y == next_jy)
7943         return TRUE;
7944     }
7945   }
7946
7947   return FALSE;
7948 }
7949
7950 static void StartMoving(int x, int y)
7951 {
7952   boolean started_moving = FALSE;       // some elements can fall _and_ move
7953   int element = Tile[x][y];
7954
7955   if (Stop[x][y])
7956     return;
7957
7958   if (MovDelay[x][y] == 0)
7959     GfxAction[x][y] = ACTION_DEFAULT;
7960
7961   if (CAN_FALL(element) && y < lev_fieldy - 1)
7962   {
7963     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7964         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7965       if (JustBeingPushed(x, y))
7966         return;
7967
7968     if (element == EL_QUICKSAND_FULL)
7969     {
7970       if (IS_FREE(x, y + 1))
7971       {
7972         InitMovingField(x, y, MV_DOWN);
7973         started_moving = TRUE;
7974
7975         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7976 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7977         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7978           Store[x][y] = EL_ROCK;
7979 #else
7980         Store[x][y] = EL_ROCK;
7981 #endif
7982
7983         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7984       }
7985       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7986       {
7987         if (!MovDelay[x][y])
7988         {
7989           MovDelay[x][y] = TILEY + 1;
7990
7991           ResetGfxAnimation(x, y);
7992           ResetGfxAnimation(x, y + 1);
7993         }
7994
7995         if (MovDelay[x][y])
7996         {
7997           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7998           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7999
8000           MovDelay[x][y]--;
8001           if (MovDelay[x][y])
8002             return;
8003         }
8004
8005         Tile[x][y] = EL_QUICKSAND_EMPTY;
8006         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8007         Store[x][y + 1] = Store[x][y];
8008         Store[x][y] = 0;
8009
8010         PlayLevelSoundAction(x, y, ACTION_FILLING);
8011       }
8012       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8013       {
8014         if (!MovDelay[x][y])
8015         {
8016           MovDelay[x][y] = TILEY + 1;
8017
8018           ResetGfxAnimation(x, y);
8019           ResetGfxAnimation(x, y + 1);
8020         }
8021
8022         if (MovDelay[x][y])
8023         {
8024           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8025           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8026
8027           MovDelay[x][y]--;
8028           if (MovDelay[x][y])
8029             return;
8030         }
8031
8032         Tile[x][y] = EL_QUICKSAND_EMPTY;
8033         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8034         Store[x][y + 1] = Store[x][y];
8035         Store[x][y] = 0;
8036
8037         PlayLevelSoundAction(x, y, ACTION_FILLING);
8038       }
8039     }
8040     else if (element == EL_QUICKSAND_FAST_FULL)
8041     {
8042       if (IS_FREE(x, y + 1))
8043       {
8044         InitMovingField(x, y, MV_DOWN);
8045         started_moving = TRUE;
8046
8047         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8048 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8049         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8050           Store[x][y] = EL_ROCK;
8051 #else
8052         Store[x][y] = EL_ROCK;
8053 #endif
8054
8055         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8056       }
8057       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8058       {
8059         if (!MovDelay[x][y])
8060         {
8061           MovDelay[x][y] = TILEY + 1;
8062
8063           ResetGfxAnimation(x, y);
8064           ResetGfxAnimation(x, y + 1);
8065         }
8066
8067         if (MovDelay[x][y])
8068         {
8069           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8070           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8071
8072           MovDelay[x][y]--;
8073           if (MovDelay[x][y])
8074             return;
8075         }
8076
8077         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8078         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8079         Store[x][y + 1] = Store[x][y];
8080         Store[x][y] = 0;
8081
8082         PlayLevelSoundAction(x, y, ACTION_FILLING);
8083       }
8084       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8085       {
8086         if (!MovDelay[x][y])
8087         {
8088           MovDelay[x][y] = TILEY + 1;
8089
8090           ResetGfxAnimation(x, y);
8091           ResetGfxAnimation(x, y + 1);
8092         }
8093
8094         if (MovDelay[x][y])
8095         {
8096           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8097           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8098
8099           MovDelay[x][y]--;
8100           if (MovDelay[x][y])
8101             return;
8102         }
8103
8104         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8105         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8106         Store[x][y + 1] = Store[x][y];
8107         Store[x][y] = 0;
8108
8109         PlayLevelSoundAction(x, y, ACTION_FILLING);
8110       }
8111     }
8112     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8113              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8114     {
8115       InitMovingField(x, y, MV_DOWN);
8116       started_moving = TRUE;
8117
8118       Tile[x][y] = EL_QUICKSAND_FILLING;
8119       Store[x][y] = element;
8120
8121       PlayLevelSoundAction(x, y, ACTION_FILLING);
8122     }
8123     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8124              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8125     {
8126       InitMovingField(x, y, MV_DOWN);
8127       started_moving = TRUE;
8128
8129       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8130       Store[x][y] = element;
8131
8132       PlayLevelSoundAction(x, y, ACTION_FILLING);
8133     }
8134     else if (element == EL_MAGIC_WALL_FULL)
8135     {
8136       if (IS_FREE(x, y + 1))
8137       {
8138         InitMovingField(x, y, MV_DOWN);
8139         started_moving = TRUE;
8140
8141         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8142         Store[x][y] = EL_CHANGED(Store[x][y]);
8143       }
8144       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8145       {
8146         if (!MovDelay[x][y])
8147           MovDelay[x][y] = TILEY / 4 + 1;
8148
8149         if (MovDelay[x][y])
8150         {
8151           MovDelay[x][y]--;
8152           if (MovDelay[x][y])
8153             return;
8154         }
8155
8156         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8157         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8158         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8159         Store[x][y] = 0;
8160       }
8161     }
8162     else if (element == EL_BD_MAGIC_WALL_FULL)
8163     {
8164       if (IS_FREE(x, y + 1))
8165       {
8166         InitMovingField(x, y, MV_DOWN);
8167         started_moving = TRUE;
8168
8169         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8170         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8171       }
8172       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8173       {
8174         if (!MovDelay[x][y])
8175           MovDelay[x][y] = TILEY / 4 + 1;
8176
8177         if (MovDelay[x][y])
8178         {
8179           MovDelay[x][y]--;
8180           if (MovDelay[x][y])
8181             return;
8182         }
8183
8184         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8185         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8186         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8187         Store[x][y] = 0;
8188       }
8189     }
8190     else if (element == EL_DC_MAGIC_WALL_FULL)
8191     {
8192       if (IS_FREE(x, y + 1))
8193       {
8194         InitMovingField(x, y, MV_DOWN);
8195         started_moving = TRUE;
8196
8197         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8198         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8199       }
8200       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8201       {
8202         if (!MovDelay[x][y])
8203           MovDelay[x][y] = TILEY / 4 + 1;
8204
8205         if (MovDelay[x][y])
8206         {
8207           MovDelay[x][y]--;
8208           if (MovDelay[x][y])
8209             return;
8210         }
8211
8212         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8213         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8214         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8215         Store[x][y] = 0;
8216       }
8217     }
8218     else if ((CAN_PASS_MAGIC_WALL(element) &&
8219               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8220                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8221              (CAN_PASS_DC_MAGIC_WALL(element) &&
8222               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8223
8224     {
8225       InitMovingField(x, y, MV_DOWN);
8226       started_moving = TRUE;
8227
8228       Tile[x][y] =
8229         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8230          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8231          EL_DC_MAGIC_WALL_FILLING);
8232       Store[x][y] = element;
8233     }
8234     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8235     {
8236       SplashAcid(x, y + 1);
8237
8238       InitMovingField(x, y, MV_DOWN);
8239       started_moving = TRUE;
8240
8241       Store[x][y] = EL_ACID;
8242     }
8243     else if (
8244              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8245               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8246              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8247               CAN_FALL(element) && WasJustFalling[x][y] &&
8248               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8249
8250              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8251               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8252               (Tile[x][y + 1] == EL_BLOCKED)))
8253     {
8254       /* this is needed for a special case not covered by calling "Impact()"
8255          from "ContinueMoving()": if an element moves to a tile directly below
8256          another element which was just falling on that tile (which was empty
8257          in the previous frame), the falling element above would just stop
8258          instead of smashing the element below (in previous version, the above
8259          element was just checked for "moving" instead of "falling", resulting
8260          in incorrect smashes caused by horizontal movement of the above
8261          element; also, the case of the player being the element to smash was
8262          simply not covered here... :-/ ) */
8263
8264       CheckCollision[x][y] = 0;
8265       CheckImpact[x][y] = 0;
8266
8267       Impact(x, y);
8268     }
8269     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8270     {
8271       if (MovDir[x][y] == MV_NONE)
8272       {
8273         InitMovingField(x, y, MV_DOWN);
8274         started_moving = TRUE;
8275       }
8276     }
8277     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8278     {
8279       if (WasJustFalling[x][y]) // prevent animation from being restarted
8280         MovDir[x][y] = MV_DOWN;
8281
8282       InitMovingField(x, y, MV_DOWN);
8283       started_moving = TRUE;
8284     }
8285     else if (element == EL_AMOEBA_DROP)
8286     {
8287       Tile[x][y] = EL_AMOEBA_GROWING;
8288       Store[x][y] = EL_AMOEBA_WET;
8289     }
8290     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8291               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8292              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8293              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8294     {
8295       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8296                                 (IS_FREE(x - 1, y + 1) ||
8297                                  Tile[x - 1][y + 1] == EL_ACID));
8298       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8299                                 (IS_FREE(x + 1, y + 1) ||
8300                                  Tile[x + 1][y + 1] == EL_ACID));
8301       boolean can_fall_any  = (can_fall_left || can_fall_right);
8302       boolean can_fall_both = (can_fall_left && can_fall_right);
8303       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8304
8305       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8306       {
8307         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8308           can_fall_right = FALSE;
8309         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8310           can_fall_left = FALSE;
8311         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8312           can_fall_right = FALSE;
8313         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8314           can_fall_left = FALSE;
8315
8316         can_fall_any  = (can_fall_left || can_fall_right);
8317         can_fall_both = FALSE;
8318       }
8319
8320       if (can_fall_both)
8321       {
8322         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8323           can_fall_right = FALSE;       // slip down on left side
8324         else
8325           can_fall_left = !(can_fall_right = RND(2));
8326
8327         can_fall_both = FALSE;
8328       }
8329
8330       if (can_fall_any)
8331       {
8332         // if not determined otherwise, prefer left side for slipping down
8333         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8334         started_moving = TRUE;
8335       }
8336     }
8337     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8338     {
8339       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8340       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8341       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8342       int belt_dir = game.belt_dir[belt_nr];
8343
8344       if ((belt_dir == MV_LEFT  && left_is_free) ||
8345           (belt_dir == MV_RIGHT && right_is_free))
8346       {
8347         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8348
8349         InitMovingField(x, y, belt_dir);
8350         started_moving = TRUE;
8351
8352         Pushed[x][y] = TRUE;
8353         Pushed[nextx][y] = TRUE;
8354
8355         GfxAction[x][y] = ACTION_DEFAULT;
8356       }
8357       else
8358       {
8359         MovDir[x][y] = 0;       // if element was moving, stop it
8360       }
8361     }
8362   }
8363
8364   // not "else if" because of elements that can fall and move (EL_SPRING)
8365   if (CAN_MOVE(element) && !started_moving)
8366   {
8367     int move_pattern = element_info[element].move_pattern;
8368     int newx, newy;
8369
8370     Moving2Blocked(x, y, &newx, &newy);
8371
8372     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8373       return;
8374
8375     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8376         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8377     {
8378       WasJustMoving[x][y] = 0;
8379       CheckCollision[x][y] = 0;
8380
8381       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8382
8383       if (Tile[x][y] != element)        // element has changed
8384         return;
8385     }
8386
8387     if (!MovDelay[x][y])        // start new movement phase
8388     {
8389       // all objects that can change their move direction after each step
8390       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8391
8392       if (element != EL_YAMYAM &&
8393           element != EL_DARK_YAMYAM &&
8394           element != EL_PACMAN &&
8395           !(move_pattern & MV_ANY_DIRECTION) &&
8396           move_pattern != MV_TURNING_LEFT &&
8397           move_pattern != MV_TURNING_RIGHT &&
8398           move_pattern != MV_TURNING_LEFT_RIGHT &&
8399           move_pattern != MV_TURNING_RIGHT_LEFT &&
8400           move_pattern != MV_TURNING_RANDOM)
8401       {
8402         TurnRound(x, y);
8403
8404         if (MovDelay[x][y] && (element == EL_BUG ||
8405                                element == EL_SPACESHIP ||
8406                                element == EL_SP_SNIKSNAK ||
8407                                element == EL_SP_ELECTRON ||
8408                                element == EL_MOLE))
8409           TEST_DrawLevelField(x, y);
8410       }
8411     }
8412
8413     if (MovDelay[x][y])         // wait some time before next movement
8414     {
8415       MovDelay[x][y]--;
8416
8417       if (element == EL_ROBOT ||
8418           element == EL_YAMYAM ||
8419           element == EL_DARK_YAMYAM)
8420       {
8421         DrawLevelElementAnimationIfNeeded(x, y, element);
8422         PlayLevelSoundAction(x, y, ACTION_WAITING);
8423       }
8424       else if (element == EL_SP_ELECTRON)
8425         DrawLevelElementAnimationIfNeeded(x, y, element);
8426       else if (element == EL_DRAGON)
8427       {
8428         int i;
8429         int dir = MovDir[x][y];
8430         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8431         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8432         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8433                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8434                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8435                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8436         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8437
8438         GfxAction[x][y] = ACTION_ATTACKING;
8439
8440         if (IS_PLAYER(x, y))
8441           DrawPlayerField(x, y);
8442         else
8443           TEST_DrawLevelField(x, y);
8444
8445         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8446
8447         for (i = 1; i <= 3; i++)
8448         {
8449           int xx = x + i * dx;
8450           int yy = y + i * dy;
8451           int sx = SCREENX(xx);
8452           int sy = SCREENY(yy);
8453           int flame_graphic = graphic + (i - 1);
8454
8455           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8456             break;
8457
8458           if (MovDelay[x][y])
8459           {
8460             int flamed = MovingOrBlocked2Element(xx, yy);
8461
8462             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8463               Bang(xx, yy);
8464             else
8465               RemoveMovingField(xx, yy);
8466
8467             ChangeDelay[xx][yy] = 0;
8468
8469             Tile[xx][yy] = EL_FLAMES;
8470
8471             if (IN_SCR_FIELD(sx, sy))
8472             {
8473               TEST_DrawLevelFieldCrumbled(xx, yy);
8474               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8475             }
8476           }
8477           else
8478           {
8479             if (Tile[xx][yy] == EL_FLAMES)
8480               Tile[xx][yy] = EL_EMPTY;
8481             TEST_DrawLevelField(xx, yy);
8482           }
8483         }
8484       }
8485
8486       if (MovDelay[x][y])       // element still has to wait some time
8487       {
8488         PlayLevelSoundAction(x, y, ACTION_WAITING);
8489
8490         return;
8491       }
8492     }
8493
8494     // now make next step
8495
8496     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8497
8498     if (DONT_COLLIDE_WITH(element) &&
8499         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8500         !PLAYER_ENEMY_PROTECTED(newx, newy))
8501     {
8502       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8503
8504       return;
8505     }
8506
8507     else if (CAN_MOVE_INTO_ACID(element) &&
8508              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8509              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8510              (MovDir[x][y] == MV_DOWN ||
8511               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8512     {
8513       SplashAcid(newx, newy);
8514       Store[x][y] = EL_ACID;
8515     }
8516     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8517     {
8518       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8519           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8520           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8521           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8522       {
8523         RemoveField(x, y);
8524         TEST_DrawLevelField(x, y);
8525
8526         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8527         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8528           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8529
8530         game.friends_still_needed--;
8531         if (!game.friends_still_needed &&
8532             !game.GameOver &&
8533             game.all_players_gone)
8534           LevelSolved();
8535
8536         return;
8537       }
8538       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8539       {
8540         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8541           TEST_DrawLevelField(newx, newy);
8542         else
8543           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8544       }
8545       else if (!IS_FREE(newx, newy))
8546       {
8547         GfxAction[x][y] = ACTION_WAITING;
8548
8549         if (IS_PLAYER(x, y))
8550           DrawPlayerField(x, y);
8551         else
8552           TEST_DrawLevelField(x, y);
8553
8554         return;
8555       }
8556     }
8557     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8558     {
8559       if (IS_FOOD_PIG(Tile[newx][newy]))
8560       {
8561         if (IS_MOVING(newx, newy))
8562           RemoveMovingField(newx, newy);
8563         else
8564         {
8565           Tile[newx][newy] = EL_EMPTY;
8566           TEST_DrawLevelField(newx, newy);
8567         }
8568
8569         PlayLevelSound(x, y, SND_PIG_DIGGING);
8570       }
8571       else if (!IS_FREE(newx, newy))
8572       {
8573         if (IS_PLAYER(x, y))
8574           DrawPlayerField(x, y);
8575         else
8576           TEST_DrawLevelField(x, y);
8577
8578         return;
8579       }
8580     }
8581     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8582     {
8583       if (Store[x][y] != EL_EMPTY)
8584       {
8585         boolean can_clone = FALSE;
8586         int xx, yy;
8587
8588         // check if element to clone is still there
8589         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8590         {
8591           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8592           {
8593             can_clone = TRUE;
8594
8595             break;
8596           }
8597         }
8598
8599         // cannot clone or target field not free anymore -- do not clone
8600         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8601           Store[x][y] = EL_EMPTY;
8602       }
8603
8604       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8605       {
8606         if (IS_MV_DIAGONAL(MovDir[x][y]))
8607         {
8608           int diagonal_move_dir = MovDir[x][y];
8609           int stored = Store[x][y];
8610           int change_delay = 8;
8611           int graphic;
8612
8613           // android is moving diagonally
8614
8615           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8616
8617           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8618           GfxElement[x][y] = EL_EMC_ANDROID;
8619           GfxAction[x][y] = ACTION_SHRINKING;
8620           GfxDir[x][y] = diagonal_move_dir;
8621           ChangeDelay[x][y] = change_delay;
8622
8623           if (Store[x][y] == EL_EMPTY)
8624             Store[x][y] = GfxElementEmpty[x][y];
8625
8626           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8627                                    GfxDir[x][y]);
8628
8629           DrawLevelGraphicAnimation(x, y, graphic);
8630           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8631
8632           if (Tile[newx][newy] == EL_ACID)
8633           {
8634             SplashAcid(newx, newy);
8635
8636             return;
8637           }
8638
8639           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8640
8641           Store[newx][newy] = EL_EMC_ANDROID;
8642           GfxElement[newx][newy] = EL_EMC_ANDROID;
8643           GfxAction[newx][newy] = ACTION_GROWING;
8644           GfxDir[newx][newy] = diagonal_move_dir;
8645           ChangeDelay[newx][newy] = change_delay;
8646
8647           graphic = el_act_dir2img(GfxElement[newx][newy],
8648                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8649
8650           DrawLevelGraphicAnimation(newx, newy, graphic);
8651           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8652
8653           return;
8654         }
8655         else
8656         {
8657           Tile[newx][newy] = EL_EMPTY;
8658           TEST_DrawLevelField(newx, newy);
8659
8660           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8661         }
8662       }
8663       else if (!IS_FREE(newx, newy))
8664       {
8665         return;
8666       }
8667     }
8668     else if (IS_CUSTOM_ELEMENT(element) &&
8669              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8670     {
8671       if (!DigFieldByCE(newx, newy, element))
8672         return;
8673
8674       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8675       {
8676         RunnerVisit[x][y] = FrameCounter;
8677         PlayerVisit[x][y] /= 8;         // expire player visit path
8678       }
8679     }
8680     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8681     {
8682       if (!IS_FREE(newx, newy))
8683       {
8684         if (IS_PLAYER(x, y))
8685           DrawPlayerField(x, y);
8686         else
8687           TEST_DrawLevelField(x, y);
8688
8689         return;
8690       }
8691       else
8692       {
8693         boolean wanna_flame = !RND(10);
8694         int dx = newx - x, dy = newy - y;
8695         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8696         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8697         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8698                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8699         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8700                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8701
8702         if ((wanna_flame ||
8703              IS_CLASSIC_ENEMY(element1) ||
8704              IS_CLASSIC_ENEMY(element2)) &&
8705             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8706             element1 != EL_FLAMES && element2 != EL_FLAMES)
8707         {
8708           ResetGfxAnimation(x, y);
8709           GfxAction[x][y] = ACTION_ATTACKING;
8710
8711           if (IS_PLAYER(x, y))
8712             DrawPlayerField(x, y);
8713           else
8714             TEST_DrawLevelField(x, y);
8715
8716           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8717
8718           MovDelay[x][y] = 50;
8719
8720           Tile[newx][newy] = EL_FLAMES;
8721           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8722             Tile[newx1][newy1] = EL_FLAMES;
8723           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8724             Tile[newx2][newy2] = EL_FLAMES;
8725
8726           return;
8727         }
8728       }
8729     }
8730     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8731              Tile[newx][newy] == EL_DIAMOND)
8732     {
8733       if (IS_MOVING(newx, newy))
8734         RemoveMovingField(newx, newy);
8735       else
8736       {
8737         Tile[newx][newy] = EL_EMPTY;
8738         TEST_DrawLevelField(newx, newy);
8739       }
8740
8741       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8742     }
8743     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8744              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8745     {
8746       if (AmoebaNr[newx][newy])
8747       {
8748         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8749         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8750             Tile[newx][newy] == EL_BD_AMOEBA)
8751           AmoebaCnt[AmoebaNr[newx][newy]]--;
8752       }
8753
8754       if (IS_MOVING(newx, newy))
8755       {
8756         RemoveMovingField(newx, newy);
8757       }
8758       else
8759       {
8760         Tile[newx][newy] = EL_EMPTY;
8761         TEST_DrawLevelField(newx, newy);
8762       }
8763
8764       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8765     }
8766     else if ((element == EL_PACMAN || element == EL_MOLE)
8767              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8768     {
8769       if (AmoebaNr[newx][newy])
8770       {
8771         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8772         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8773             Tile[newx][newy] == EL_BD_AMOEBA)
8774           AmoebaCnt[AmoebaNr[newx][newy]]--;
8775       }
8776
8777       if (element == EL_MOLE)
8778       {
8779         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8780         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8781
8782         ResetGfxAnimation(x, y);
8783         GfxAction[x][y] = ACTION_DIGGING;
8784         TEST_DrawLevelField(x, y);
8785
8786         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8787
8788         return;                         // wait for shrinking amoeba
8789       }
8790       else      // element == EL_PACMAN
8791       {
8792         Tile[newx][newy] = EL_EMPTY;
8793         TEST_DrawLevelField(newx, newy);
8794         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8795       }
8796     }
8797     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8798              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8799               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8800     {
8801       // wait for shrinking amoeba to completely disappear
8802       return;
8803     }
8804     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8805     {
8806       // object was running against a wall
8807
8808       TurnRound(x, y);
8809
8810       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8811         DrawLevelElementAnimation(x, y, element);
8812
8813       if (DONT_TOUCH(element))
8814         TestIfBadThingTouchesPlayer(x, y);
8815
8816       return;
8817     }
8818
8819     InitMovingField(x, y, MovDir[x][y]);
8820
8821     PlayLevelSoundAction(x, y, ACTION_MOVING);
8822   }
8823
8824   if (MovDir[x][y])
8825     ContinueMoving(x, y);
8826 }
8827
8828 void ContinueMoving(int x, int y)
8829 {
8830   int element = Tile[x][y];
8831   struct ElementInfo *ei = &element_info[element];
8832   int direction = MovDir[x][y];
8833   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8834   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8835   int newx = x + dx, newy = y + dy;
8836   int stored = Store[x][y];
8837   int stored_new = Store[newx][newy];
8838   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8839   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8840   boolean last_line = (newy == lev_fieldy - 1);
8841   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8842
8843   if (pushed_by_player)         // special case: moving object pushed by player
8844   {
8845     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8846   }
8847   else if (use_step_delay)      // special case: moving object has step delay
8848   {
8849     if (!MovDelay[x][y])
8850       MovPos[x][y] += getElementMoveStepsize(x, y);
8851
8852     if (MovDelay[x][y])
8853       MovDelay[x][y]--;
8854     else
8855       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8856
8857     if (MovDelay[x][y])
8858     {
8859       TEST_DrawLevelField(x, y);
8860
8861       return;   // element is still waiting
8862     }
8863   }
8864   else                          // normal case: generically moving object
8865   {
8866     MovPos[x][y] += getElementMoveStepsize(x, y);
8867   }
8868
8869   if (ABS(MovPos[x][y]) < TILEX)
8870   {
8871     TEST_DrawLevelField(x, y);
8872
8873     return;     // element is still moving
8874   }
8875
8876   // element reached destination field
8877
8878   Tile[x][y] = EL_EMPTY;
8879   Tile[newx][newy] = element;
8880   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8881
8882   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8883   {
8884     element = Tile[newx][newy] = EL_ACID;
8885   }
8886   else if (element == EL_MOLE)
8887   {
8888     Tile[x][y] = EL_SAND;
8889
8890     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8891   }
8892   else if (element == EL_QUICKSAND_FILLING)
8893   {
8894     element = Tile[newx][newy] = get_next_element(element);
8895     Store[newx][newy] = Store[x][y];
8896   }
8897   else if (element == EL_QUICKSAND_EMPTYING)
8898   {
8899     Tile[x][y] = get_next_element(element);
8900     element = Tile[newx][newy] = Store[x][y];
8901   }
8902   else if (element == EL_QUICKSAND_FAST_FILLING)
8903   {
8904     element = Tile[newx][newy] = get_next_element(element);
8905     Store[newx][newy] = Store[x][y];
8906   }
8907   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8908   {
8909     Tile[x][y] = get_next_element(element);
8910     element = Tile[newx][newy] = Store[x][y];
8911   }
8912   else if (element == EL_MAGIC_WALL_FILLING)
8913   {
8914     element = Tile[newx][newy] = get_next_element(element);
8915     if (!game.magic_wall_active)
8916       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8917     Store[newx][newy] = Store[x][y];
8918   }
8919   else if (element == EL_MAGIC_WALL_EMPTYING)
8920   {
8921     Tile[x][y] = get_next_element(element);
8922     if (!game.magic_wall_active)
8923       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8924     element = Tile[newx][newy] = Store[x][y];
8925
8926     InitField(newx, newy, FALSE);
8927   }
8928   else if (element == EL_BD_MAGIC_WALL_FILLING)
8929   {
8930     element = Tile[newx][newy] = get_next_element(element);
8931     if (!game.magic_wall_active)
8932       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8933     Store[newx][newy] = Store[x][y];
8934   }
8935   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8936   {
8937     Tile[x][y] = get_next_element(element);
8938     if (!game.magic_wall_active)
8939       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8940     element = Tile[newx][newy] = Store[x][y];
8941
8942     InitField(newx, newy, FALSE);
8943   }
8944   else if (element == EL_DC_MAGIC_WALL_FILLING)
8945   {
8946     element = Tile[newx][newy] = get_next_element(element);
8947     if (!game.magic_wall_active)
8948       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8949     Store[newx][newy] = Store[x][y];
8950   }
8951   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8952   {
8953     Tile[x][y] = get_next_element(element);
8954     if (!game.magic_wall_active)
8955       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8956     element = Tile[newx][newy] = Store[x][y];
8957
8958     InitField(newx, newy, FALSE);
8959   }
8960   else if (element == EL_AMOEBA_DROPPING)
8961   {
8962     Tile[x][y] = get_next_element(element);
8963     element = Tile[newx][newy] = Store[x][y];
8964   }
8965   else if (element == EL_SOKOBAN_OBJECT)
8966   {
8967     if (Back[x][y])
8968       Tile[x][y] = Back[x][y];
8969
8970     if (Back[newx][newy])
8971       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8972
8973     Back[x][y] = Back[newx][newy] = 0;
8974   }
8975
8976   Store[x][y] = EL_EMPTY;
8977   MovPos[x][y] = 0;
8978   MovDir[x][y] = 0;
8979   MovDelay[x][y] = 0;
8980
8981   MovDelay[newx][newy] = 0;
8982
8983   if (CAN_CHANGE_OR_HAS_ACTION(element))
8984   {
8985     // copy element change control values to new field
8986     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8987     ChangePage[newx][newy]  = ChangePage[x][y];
8988     ChangeCount[newx][newy] = ChangeCount[x][y];
8989     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8990   }
8991
8992   CustomValue[newx][newy] = CustomValue[x][y];
8993
8994   ChangeDelay[x][y] = 0;
8995   ChangePage[x][y] = -1;
8996   ChangeCount[x][y] = 0;
8997   ChangeEvent[x][y] = -1;
8998
8999   CustomValue[x][y] = 0;
9000
9001   // copy animation control values to new field
9002   GfxFrame[newx][newy]  = GfxFrame[x][y];
9003   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
9004   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
9005   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
9006
9007   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9008
9009   // some elements can leave other elements behind after moving
9010   if (ei->move_leave_element != EL_EMPTY &&
9011       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9012       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9013   {
9014     int move_leave_element = ei->move_leave_element;
9015
9016     // this makes it possible to leave the removed element again
9017     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9018       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9019
9020     Tile[x][y] = move_leave_element;
9021
9022     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9023       MovDir[x][y] = direction;
9024
9025     InitField(x, y, FALSE);
9026
9027     if (GFX_CRUMBLED(Tile[x][y]))
9028       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9029
9030     if (IS_PLAYER_ELEMENT(move_leave_element))
9031       RelocatePlayer(x, y, move_leave_element);
9032   }
9033
9034   // do this after checking for left-behind element
9035   ResetGfxAnimation(x, y);      // reset animation values for old field
9036
9037   if (!CAN_MOVE(element) ||
9038       (CAN_FALL(element) && direction == MV_DOWN &&
9039        (element == EL_SPRING ||
9040         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9041         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9042     GfxDir[x][y] = MovDir[newx][newy] = 0;
9043
9044   TEST_DrawLevelField(x, y);
9045   TEST_DrawLevelField(newx, newy);
9046
9047   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9048
9049   // prevent pushed element from moving on in pushed direction
9050   if (pushed_by_player && CAN_MOVE(element) &&
9051       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9052       !(element_info[element].move_pattern & direction))
9053     TurnRound(newx, newy);
9054
9055   // prevent elements on conveyor belt from moving on in last direction
9056   if (pushed_by_conveyor && CAN_FALL(element) &&
9057       direction & MV_HORIZONTAL)
9058     MovDir[newx][newy] = 0;
9059
9060   if (!pushed_by_player)
9061   {
9062     int nextx = newx + dx, nexty = newy + dy;
9063     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9064
9065     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9066
9067     if (CAN_FALL(element) && direction == MV_DOWN)
9068       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9069
9070     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9071       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9072
9073     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9074       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9075   }
9076
9077   if (DONT_TOUCH(element))      // object may be nasty to player or others
9078   {
9079     TestIfBadThingTouchesPlayer(newx, newy);
9080     TestIfBadThingTouchesFriend(newx, newy);
9081
9082     if (!IS_CUSTOM_ELEMENT(element))
9083       TestIfBadThingTouchesOtherBadThing(newx, newy);
9084   }
9085   else if (element == EL_PENGUIN)
9086     TestIfFriendTouchesBadThing(newx, newy);
9087
9088   if (DONT_GET_HIT_BY(element))
9089   {
9090     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9091   }
9092
9093   // give the player one last chance (one more frame) to move away
9094   if (CAN_FALL(element) && direction == MV_DOWN &&
9095       (last_line || (!IS_FREE(x, newy + 1) &&
9096                      (!IS_PLAYER(x, newy + 1) ||
9097                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9098     Impact(x, newy);
9099
9100   if (pushed_by_player && !game.use_change_when_pushing_bug)
9101   {
9102     int push_side = MV_DIR_OPPOSITE(direction);
9103     struct PlayerInfo *player = PLAYERINFO(x, y);
9104
9105     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9106                                player->index_bit, push_side);
9107     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9108                                         player->index_bit, push_side);
9109   }
9110
9111   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9112     MovDelay[newx][newy] = 1;
9113
9114   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9115
9116   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9117   TestIfElementHitsCustomElement(newx, newy, direction);
9118   TestIfPlayerTouchesCustomElement(newx, newy);
9119   TestIfElementTouchesCustomElement(newx, newy);
9120
9121   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9122       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9123     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9124                              MV_DIR_OPPOSITE(direction));
9125 }
9126
9127 int AmoebaNeighbourNr(int ax, int ay)
9128 {
9129   int i;
9130   int element = Tile[ax][ay];
9131   int group_nr = 0;
9132   struct XY *xy = xy_topdown;
9133
9134   for (i = 0; i < NUM_DIRECTIONS; i++)
9135   {
9136     int x = ax + xy[i].x;
9137     int y = ay + xy[i].y;
9138
9139     if (!IN_LEV_FIELD(x, y))
9140       continue;
9141
9142     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9143       group_nr = AmoebaNr[x][y];
9144   }
9145
9146   return group_nr;
9147 }
9148
9149 static void AmoebaMerge(int ax, int ay)
9150 {
9151   int i, x, y, xx, yy;
9152   int new_group_nr = AmoebaNr[ax][ay];
9153   struct XY *xy = xy_topdown;
9154
9155   if (new_group_nr == 0)
9156     return;
9157
9158   for (i = 0; i < NUM_DIRECTIONS; i++)
9159   {
9160     x = ax + xy[i].x;
9161     y = ay + xy[i].y;
9162
9163     if (!IN_LEV_FIELD(x, y))
9164       continue;
9165
9166     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9167          Tile[x][y] == EL_BD_AMOEBA ||
9168          Tile[x][y] == EL_AMOEBA_DEAD) &&
9169         AmoebaNr[x][y] != new_group_nr)
9170     {
9171       int old_group_nr = AmoebaNr[x][y];
9172
9173       if (old_group_nr == 0)
9174         return;
9175
9176       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9177       AmoebaCnt[old_group_nr] = 0;
9178       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9179       AmoebaCnt2[old_group_nr] = 0;
9180
9181       SCAN_PLAYFIELD(xx, yy)
9182       {
9183         if (AmoebaNr[xx][yy] == old_group_nr)
9184           AmoebaNr[xx][yy] = new_group_nr;
9185       }
9186     }
9187   }
9188 }
9189
9190 void AmoebaToDiamond(int ax, int ay)
9191 {
9192   int i, x, y;
9193
9194   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9195   {
9196     int group_nr = AmoebaNr[ax][ay];
9197
9198 #ifdef DEBUG
9199     if (group_nr == 0)
9200     {
9201       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9202       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9203
9204       return;
9205     }
9206 #endif
9207
9208     SCAN_PLAYFIELD(x, y)
9209     {
9210       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9211       {
9212         AmoebaNr[x][y] = 0;
9213         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9214       }
9215     }
9216
9217     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9218                             SND_AMOEBA_TURNING_TO_GEM :
9219                             SND_AMOEBA_TURNING_TO_ROCK));
9220     Bang(ax, ay);
9221   }
9222   else
9223   {
9224     struct XY *xy = xy_topdown;
9225
9226     for (i = 0; i < NUM_DIRECTIONS; i++)
9227     {
9228       x = ax + xy[i].x;
9229       y = ay + xy[i].y;
9230
9231       if (!IN_LEV_FIELD(x, y))
9232         continue;
9233
9234       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9235       {
9236         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9237                               SND_AMOEBA_TURNING_TO_GEM :
9238                               SND_AMOEBA_TURNING_TO_ROCK));
9239         Bang(x, y);
9240       }
9241     }
9242   }
9243 }
9244
9245 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9246 {
9247   int x, y;
9248   int group_nr = AmoebaNr[ax][ay];
9249   boolean done = FALSE;
9250
9251 #ifdef DEBUG
9252   if (group_nr == 0)
9253   {
9254     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9255     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9256
9257     return;
9258   }
9259 #endif
9260
9261   SCAN_PLAYFIELD(x, y)
9262   {
9263     if (AmoebaNr[x][y] == group_nr &&
9264         (Tile[x][y] == EL_AMOEBA_DEAD ||
9265          Tile[x][y] == EL_BD_AMOEBA ||
9266          Tile[x][y] == EL_AMOEBA_GROWING))
9267     {
9268       AmoebaNr[x][y] = 0;
9269       Tile[x][y] = new_element;
9270       InitField(x, y, FALSE);
9271       TEST_DrawLevelField(x, y);
9272       done = TRUE;
9273     }
9274   }
9275
9276   if (done)
9277     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9278                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9279                             SND_BD_AMOEBA_TURNING_TO_GEM));
9280 }
9281
9282 static void AmoebaGrowing(int x, int y)
9283 {
9284   static DelayCounter sound_delay = { 0 };
9285
9286   if (!MovDelay[x][y])          // start new growing cycle
9287   {
9288     MovDelay[x][y] = 7;
9289
9290     if (DelayReached(&sound_delay))
9291     {
9292       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9293       sound_delay.value = 30;
9294     }
9295   }
9296
9297   if (MovDelay[x][y])           // wait some time before growing bigger
9298   {
9299     MovDelay[x][y]--;
9300     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9301     {
9302       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9303                                            6 - MovDelay[x][y]);
9304
9305       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9306     }
9307
9308     if (!MovDelay[x][y])
9309     {
9310       Tile[x][y] = Store[x][y];
9311       Store[x][y] = 0;
9312       TEST_DrawLevelField(x, y);
9313     }
9314   }
9315 }
9316
9317 static void AmoebaShrinking(int x, int y)
9318 {
9319   static DelayCounter sound_delay = { 0 };
9320
9321   if (!MovDelay[x][y])          // start new shrinking cycle
9322   {
9323     MovDelay[x][y] = 7;
9324
9325     if (DelayReached(&sound_delay))
9326       sound_delay.value = 30;
9327   }
9328
9329   if (MovDelay[x][y])           // wait some time before shrinking
9330   {
9331     MovDelay[x][y]--;
9332     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9333     {
9334       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9335                                            6 - MovDelay[x][y]);
9336
9337       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9338     }
9339
9340     if (!MovDelay[x][y])
9341     {
9342       Tile[x][y] = EL_EMPTY;
9343       TEST_DrawLevelField(x, y);
9344
9345       // don't let mole enter this field in this cycle;
9346       // (give priority to objects falling to this field from above)
9347       Stop[x][y] = TRUE;
9348     }
9349   }
9350 }
9351
9352 static void AmoebaReproduce(int ax, int ay)
9353 {
9354   int i;
9355   int element = Tile[ax][ay];
9356   int graphic = el2img(element);
9357   int newax = ax, neway = ay;
9358   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9359   struct XY *xy = xy_topdown;
9360
9361   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9362   {
9363     Tile[ax][ay] = EL_AMOEBA_DEAD;
9364     TEST_DrawLevelField(ax, ay);
9365     return;
9366   }
9367
9368   if (IS_ANIMATED(graphic))
9369     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9370
9371   if (!MovDelay[ax][ay])        // start making new amoeba field
9372     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9373
9374   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9375   {
9376     MovDelay[ax][ay]--;
9377     if (MovDelay[ax][ay])
9378       return;
9379   }
9380
9381   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9382   {
9383     int start = RND(4);
9384     int x = ax + xy[start].x;
9385     int y = ay + xy[start].y;
9386
9387     if (!IN_LEV_FIELD(x, y))
9388       return;
9389
9390     if (IS_FREE(x, y) ||
9391         CAN_GROW_INTO(Tile[x][y]) ||
9392         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9393         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9394     {
9395       newax = x;
9396       neway = y;
9397     }
9398
9399     if (newax == ax && neway == ay)
9400       return;
9401   }
9402   else                          // normal or "filled" (BD style) amoeba
9403   {
9404     int start = RND(4);
9405     boolean waiting_for_player = FALSE;
9406
9407     for (i = 0; i < NUM_DIRECTIONS; i++)
9408     {
9409       int j = (start + i) % 4;
9410       int x = ax + xy[j].x;
9411       int y = ay + xy[j].y;
9412
9413       if (!IN_LEV_FIELD(x, y))
9414         continue;
9415
9416       if (IS_FREE(x, y) ||
9417           CAN_GROW_INTO(Tile[x][y]) ||
9418           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9419           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9420       {
9421         newax = x;
9422         neway = y;
9423         break;
9424       }
9425       else if (IS_PLAYER(x, y))
9426         waiting_for_player = TRUE;
9427     }
9428
9429     if (newax == ax && neway == ay)             // amoeba cannot grow
9430     {
9431       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9432       {
9433         Tile[ax][ay] = EL_AMOEBA_DEAD;
9434         TEST_DrawLevelField(ax, ay);
9435         AmoebaCnt[AmoebaNr[ax][ay]]--;
9436
9437         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9438         {
9439           if (element == EL_AMOEBA_FULL)
9440             AmoebaToDiamond(ax, ay);
9441           else if (element == EL_BD_AMOEBA)
9442             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9443         }
9444       }
9445       return;
9446     }
9447     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9448     {
9449       // amoeba gets larger by growing in some direction
9450
9451       int new_group_nr = AmoebaNr[ax][ay];
9452
9453 #ifdef DEBUG
9454   if (new_group_nr == 0)
9455   {
9456     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9457           newax, neway);
9458     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9459
9460     return;
9461   }
9462 #endif
9463
9464       AmoebaNr[newax][neway] = new_group_nr;
9465       AmoebaCnt[new_group_nr]++;
9466       AmoebaCnt2[new_group_nr]++;
9467
9468       // if amoeba touches other amoeba(s) after growing, unify them
9469       AmoebaMerge(newax, neway);
9470
9471       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9472       {
9473         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9474         return;
9475       }
9476     }
9477   }
9478
9479   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9480       (neway == lev_fieldy - 1 && newax != ax))
9481   {
9482     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9483     Store[newax][neway] = element;
9484   }
9485   else if (neway == ay || element == EL_EMC_DRIPPER)
9486   {
9487     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9488
9489     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9490   }
9491   else
9492   {
9493     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9494     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9495     Store[ax][ay] = EL_AMOEBA_DROP;
9496     ContinueMoving(ax, ay);
9497     return;
9498   }
9499
9500   TEST_DrawLevelField(newax, neway);
9501 }
9502
9503 static void Life(int ax, int ay)
9504 {
9505   int x1, y1, x2, y2;
9506   int life_time = 40;
9507   int element = Tile[ax][ay];
9508   int graphic = el2img(element);
9509   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9510                          level.biomaze);
9511   boolean changed = FALSE;
9512
9513   if (IS_ANIMATED(graphic))
9514     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9515
9516   if (Stop[ax][ay])
9517     return;
9518
9519   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9520     MovDelay[ax][ay] = life_time;
9521
9522   if (MovDelay[ax][ay])         // wait some time before next cycle
9523   {
9524     MovDelay[ax][ay]--;
9525     if (MovDelay[ax][ay])
9526       return;
9527   }
9528
9529   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9530   {
9531     int xx = ax + x1, yy = ay + y1;
9532     int old_element = Tile[xx][yy];
9533     int num_neighbours = 0;
9534
9535     if (!IN_LEV_FIELD(xx, yy))
9536       continue;
9537
9538     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9539     {
9540       int x = xx + x2, y = yy + y2;
9541
9542       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9543         continue;
9544
9545       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9546       boolean is_neighbour = FALSE;
9547
9548       if (level.use_life_bugs)
9549         is_neighbour =
9550           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9551            (IS_FREE(x, y)                             &&  Stop[x][y]));
9552       else
9553         is_neighbour =
9554           (Last[x][y] == element || is_player_cell);
9555
9556       if (is_neighbour)
9557         num_neighbours++;
9558     }
9559
9560     boolean is_free = FALSE;
9561
9562     if (level.use_life_bugs)
9563       is_free = (IS_FREE(xx, yy));
9564     else
9565       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9566
9567     if (xx == ax && yy == ay)           // field in the middle
9568     {
9569       if (num_neighbours < life_parameter[0] ||
9570           num_neighbours > life_parameter[1])
9571       {
9572         Tile[xx][yy] = EL_EMPTY;
9573         if (Tile[xx][yy] != old_element)
9574           TEST_DrawLevelField(xx, yy);
9575         Stop[xx][yy] = TRUE;
9576         changed = TRUE;
9577       }
9578     }
9579     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9580     {                                   // free border field
9581       if (num_neighbours >= life_parameter[2] &&
9582           num_neighbours <= life_parameter[3])
9583       {
9584         Tile[xx][yy] = element;
9585         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9586         if (Tile[xx][yy] != old_element)
9587           TEST_DrawLevelField(xx, yy);
9588         Stop[xx][yy] = TRUE;
9589         changed = TRUE;
9590       }
9591     }
9592   }
9593
9594   if (changed)
9595     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9596                    SND_GAME_OF_LIFE_GROWING);
9597 }
9598
9599 static void InitRobotWheel(int x, int y)
9600 {
9601   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9602 }
9603
9604 static void RunRobotWheel(int x, int y)
9605 {
9606   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9607 }
9608
9609 static void StopRobotWheel(int x, int y)
9610 {
9611   if (game.robot_wheel_x == x &&
9612       game.robot_wheel_y == y)
9613   {
9614     game.robot_wheel_x = -1;
9615     game.robot_wheel_y = -1;
9616     game.robot_wheel_active = FALSE;
9617   }
9618 }
9619
9620 static void InitTimegateWheel(int x, int y)
9621 {
9622   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9623 }
9624
9625 static void RunTimegateWheel(int x, int y)
9626 {
9627   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9628 }
9629
9630 static void InitMagicBallDelay(int x, int y)
9631 {
9632   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9633 }
9634
9635 static void ActivateMagicBall(int bx, int by)
9636 {
9637   int x, y;
9638
9639   if (level.ball_random)
9640   {
9641     int pos_border = RND(8);    // select one of the eight border elements
9642     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9643     int xx = pos_content % 3;
9644     int yy = pos_content / 3;
9645
9646     x = bx - 1 + xx;
9647     y = by - 1 + yy;
9648
9649     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9650       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9651   }
9652   else
9653   {
9654     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9655     {
9656       int xx = x - bx + 1;
9657       int yy = y - by + 1;
9658
9659       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9660         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9661     }
9662   }
9663
9664   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9665 }
9666
9667 static void CheckExit(int x, int y)
9668 {
9669   if (game.gems_still_needed > 0 ||
9670       game.sokoban_fields_still_needed > 0 ||
9671       game.sokoban_objects_still_needed > 0 ||
9672       game.lights_still_needed > 0)
9673   {
9674     int element = Tile[x][y];
9675     int graphic = el2img(element);
9676
9677     if (IS_ANIMATED(graphic))
9678       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9679
9680     return;
9681   }
9682
9683   // do not re-open exit door closed after last player
9684   if (game.all_players_gone)
9685     return;
9686
9687   Tile[x][y] = EL_EXIT_OPENING;
9688
9689   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9690 }
9691
9692 static void CheckExitEM(int x, int y)
9693 {
9694   if (game.gems_still_needed > 0 ||
9695       game.sokoban_fields_still_needed > 0 ||
9696       game.sokoban_objects_still_needed > 0 ||
9697       game.lights_still_needed > 0)
9698   {
9699     int element = Tile[x][y];
9700     int graphic = el2img(element);
9701
9702     if (IS_ANIMATED(graphic))
9703       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9704
9705     return;
9706   }
9707
9708   // do not re-open exit door closed after last player
9709   if (game.all_players_gone)
9710     return;
9711
9712   Tile[x][y] = EL_EM_EXIT_OPENING;
9713
9714   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9715 }
9716
9717 static void CheckExitSteel(int x, int y)
9718 {
9719   if (game.gems_still_needed > 0 ||
9720       game.sokoban_fields_still_needed > 0 ||
9721       game.sokoban_objects_still_needed > 0 ||
9722       game.lights_still_needed > 0)
9723   {
9724     int element = Tile[x][y];
9725     int graphic = el2img(element);
9726
9727     if (IS_ANIMATED(graphic))
9728       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9729
9730     return;
9731   }
9732
9733   // do not re-open exit door closed after last player
9734   if (game.all_players_gone)
9735     return;
9736
9737   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9738
9739   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9740 }
9741
9742 static void CheckExitSteelEM(int x, int y)
9743 {
9744   if (game.gems_still_needed > 0 ||
9745       game.sokoban_fields_still_needed > 0 ||
9746       game.sokoban_objects_still_needed > 0 ||
9747       game.lights_still_needed > 0)
9748   {
9749     int element = Tile[x][y];
9750     int graphic = el2img(element);
9751
9752     if (IS_ANIMATED(graphic))
9753       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9754
9755     return;
9756   }
9757
9758   // do not re-open exit door closed after last player
9759   if (game.all_players_gone)
9760     return;
9761
9762   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9763
9764   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9765 }
9766
9767 static void CheckExitSP(int x, int y)
9768 {
9769   if (game.gems_still_needed > 0)
9770   {
9771     int element = Tile[x][y];
9772     int graphic = el2img(element);
9773
9774     if (IS_ANIMATED(graphic))
9775       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9776
9777     return;
9778   }
9779
9780   // do not re-open exit door closed after last player
9781   if (game.all_players_gone)
9782     return;
9783
9784   Tile[x][y] = EL_SP_EXIT_OPENING;
9785
9786   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9787 }
9788
9789 static void CloseAllOpenTimegates(void)
9790 {
9791   int x, y;
9792
9793   SCAN_PLAYFIELD(x, y)
9794   {
9795     int element = Tile[x][y];
9796
9797     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9798     {
9799       Tile[x][y] = EL_TIMEGATE_CLOSING;
9800
9801       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9802     }
9803   }
9804 }
9805
9806 static void DrawTwinkleOnField(int x, int y)
9807 {
9808   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9809     return;
9810
9811   if (Tile[x][y] == EL_BD_DIAMOND)
9812     return;
9813
9814   if (MovDelay[x][y] == 0)      // next animation frame
9815     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9816
9817   if (MovDelay[x][y] != 0)      // wait some time before next frame
9818   {
9819     MovDelay[x][y]--;
9820
9821     DrawLevelElementAnimation(x, y, Tile[x][y]);
9822
9823     if (MovDelay[x][y] != 0)
9824     {
9825       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9826                                            10 - MovDelay[x][y]);
9827
9828       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9829     }
9830   }
9831 }
9832
9833 static void WallGrowing(int x, int y)
9834 {
9835   int delay = 6;
9836
9837   if (!MovDelay[x][y])          // next animation frame
9838     MovDelay[x][y] = 3 * delay;
9839
9840   if (MovDelay[x][y])           // wait some time before next frame
9841   {
9842     MovDelay[x][y]--;
9843
9844     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9845     {
9846       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9847       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9848
9849       DrawLevelGraphic(x, y, graphic, frame);
9850     }
9851
9852     if (!MovDelay[x][y])
9853     {
9854       if (MovDir[x][y] == MV_LEFT)
9855       {
9856         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9857           TEST_DrawLevelField(x - 1, y);
9858       }
9859       else if (MovDir[x][y] == MV_RIGHT)
9860       {
9861         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9862           TEST_DrawLevelField(x + 1, y);
9863       }
9864       else if (MovDir[x][y] == MV_UP)
9865       {
9866         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9867           TEST_DrawLevelField(x, y - 1);
9868       }
9869       else
9870       {
9871         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9872           TEST_DrawLevelField(x, y + 1);
9873       }
9874
9875       Tile[x][y] = Store[x][y];
9876       Store[x][y] = 0;
9877       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9878       TEST_DrawLevelField(x, y);
9879     }
9880   }
9881 }
9882
9883 static void CheckWallGrowing(int ax, int ay)
9884 {
9885   int element = Tile[ax][ay];
9886   int graphic = el2img(element);
9887   boolean free_top    = FALSE;
9888   boolean free_bottom = FALSE;
9889   boolean free_left   = FALSE;
9890   boolean free_right  = FALSE;
9891   boolean stop_top    = FALSE;
9892   boolean stop_bottom = FALSE;
9893   boolean stop_left   = FALSE;
9894   boolean stop_right  = FALSE;
9895   boolean new_wall    = FALSE;
9896
9897   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9898                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9899                            element == EL_EXPANDABLE_STEELWALL_ANY);
9900
9901   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9902                              element == EL_EXPANDABLE_WALL_ANY ||
9903                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9904                              element == EL_EXPANDABLE_STEELWALL_ANY);
9905
9906   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9907                              element == EL_EXPANDABLE_WALL_ANY ||
9908                              element == EL_EXPANDABLE_WALL ||
9909                              element == EL_BD_EXPANDABLE_WALL ||
9910                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9911                              element == EL_EXPANDABLE_STEELWALL_ANY);
9912
9913   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9914                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9915
9916   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9917                              element == EL_EXPANDABLE_WALL ||
9918                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9919
9920   int wall_growing = (is_steelwall ?
9921                       EL_EXPANDABLE_STEELWALL_GROWING :
9922                       EL_EXPANDABLE_WALL_GROWING);
9923
9924   int gfx_wall_growing_up    = (is_steelwall ?
9925                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9926                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9927   int gfx_wall_growing_down  = (is_steelwall ?
9928                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9929                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9930   int gfx_wall_growing_left  = (is_steelwall ?
9931                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9932                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9933   int gfx_wall_growing_right = (is_steelwall ?
9934                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9935                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9936
9937   if (IS_ANIMATED(graphic))
9938     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9939
9940   if (!MovDelay[ax][ay])        // start building new wall
9941     MovDelay[ax][ay] = 6;
9942
9943   if (MovDelay[ax][ay])         // wait some time before building new wall
9944   {
9945     MovDelay[ax][ay]--;
9946     if (MovDelay[ax][ay])
9947       return;
9948   }
9949
9950   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9951     free_top = TRUE;
9952   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9953     free_bottom = TRUE;
9954   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9955     free_left = TRUE;
9956   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9957     free_right = TRUE;
9958
9959   if (grow_vertical)
9960   {
9961     if (free_top)
9962     {
9963       Tile[ax][ay - 1] = wall_growing;
9964       Store[ax][ay - 1] = element;
9965       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9966
9967       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9968         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9969
9970       new_wall = TRUE;
9971     }
9972
9973     if (free_bottom)
9974     {
9975       Tile[ax][ay + 1] = wall_growing;
9976       Store[ax][ay + 1] = element;
9977       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9978
9979       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9980         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9981
9982       new_wall = TRUE;
9983     }
9984   }
9985
9986   if (grow_horizontal)
9987   {
9988     if (free_left)
9989     {
9990       Tile[ax - 1][ay] = wall_growing;
9991       Store[ax - 1][ay] = element;
9992       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9993
9994       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9995         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9996
9997       new_wall = TRUE;
9998     }
9999
10000     if (free_right)
10001     {
10002       Tile[ax + 1][ay] = wall_growing;
10003       Store[ax + 1][ay] = element;
10004       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
10005
10006       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10007         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10008
10009       new_wall = TRUE;
10010     }
10011   }
10012
10013   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10014     TEST_DrawLevelField(ax, ay);
10015
10016   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10017     stop_top = TRUE;
10018   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10019     stop_bottom = TRUE;
10020   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10021     stop_left = TRUE;
10022   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10023     stop_right = TRUE;
10024
10025   if (((stop_top && stop_bottom) || stop_horizontal) &&
10026       ((stop_left && stop_right) || stop_vertical))
10027     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10028
10029   if (new_wall)
10030     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10031 }
10032
10033 static void CheckForDragon(int x, int y)
10034 {
10035   int i, j;
10036   boolean dragon_found = FALSE;
10037   struct XY *xy = xy_topdown;
10038
10039   for (i = 0; i < NUM_DIRECTIONS; i++)
10040   {
10041     for (j = 0; j < 4; j++)
10042     {
10043       int xx = x + j * xy[i].x;
10044       int yy = y + j * xy[i].y;
10045
10046       if (IN_LEV_FIELD(xx, yy) &&
10047           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10048       {
10049         if (Tile[xx][yy] == EL_DRAGON)
10050           dragon_found = TRUE;
10051       }
10052       else
10053         break;
10054     }
10055   }
10056
10057   if (!dragon_found)
10058   {
10059     for (i = 0; i < NUM_DIRECTIONS; i++)
10060     {
10061       for (j = 0; j < 3; j++)
10062       {
10063         int xx = x + j * xy[i].x;
10064         int yy = y + j * xy[i].y;
10065
10066         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10067         {
10068           Tile[xx][yy] = EL_EMPTY;
10069           TEST_DrawLevelField(xx, yy);
10070         }
10071         else
10072           break;
10073       }
10074     }
10075   }
10076 }
10077
10078 static void InitBuggyBase(int x, int y)
10079 {
10080   int element = Tile[x][y];
10081   int activating_delay = FRAMES_PER_SECOND / 4;
10082
10083   ChangeDelay[x][y] =
10084     (element == EL_SP_BUGGY_BASE ?
10085      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10086      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10087      activating_delay :
10088      element == EL_SP_BUGGY_BASE_ACTIVE ?
10089      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10090 }
10091
10092 static void WarnBuggyBase(int x, int y)
10093 {
10094   int i;
10095   struct XY *xy = xy_topdown;
10096
10097   for (i = 0; i < NUM_DIRECTIONS; i++)
10098   {
10099     int xx = x + xy[i].x;
10100     int yy = y + xy[i].y;
10101
10102     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10103     {
10104       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10105
10106       break;
10107     }
10108   }
10109 }
10110
10111 static void InitTrap(int x, int y)
10112 {
10113   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10114 }
10115
10116 static void ActivateTrap(int x, int y)
10117 {
10118   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10119 }
10120
10121 static void ChangeActiveTrap(int x, int y)
10122 {
10123   int graphic = IMG_TRAP_ACTIVE;
10124
10125   // if new animation frame was drawn, correct crumbled sand border
10126   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10127     TEST_DrawLevelFieldCrumbled(x, y);
10128 }
10129
10130 static int getSpecialActionElement(int element, int number, int base_element)
10131 {
10132   return (element != EL_EMPTY ? element :
10133           number != -1 ? base_element + number - 1 :
10134           EL_EMPTY);
10135 }
10136
10137 static int getModifiedActionNumber(int value_old, int operator, int operand,
10138                                    int value_min, int value_max)
10139 {
10140   int value_new = (operator == CA_MODE_SET      ? operand :
10141                    operator == CA_MODE_ADD      ? value_old + operand :
10142                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10143                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10144                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10145                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10146                    value_old);
10147
10148   return (value_new < value_min ? value_min :
10149           value_new > value_max ? value_max :
10150           value_new);
10151 }
10152
10153 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10154 {
10155   struct ElementInfo *ei = &element_info[element];
10156   struct ElementChangeInfo *change = &ei->change_page[page];
10157   int target_element = change->target_element;
10158   int action_type = change->action_type;
10159   int action_mode = change->action_mode;
10160   int action_arg = change->action_arg;
10161   int action_element = change->action_element;
10162   int i;
10163
10164   if (!change->has_action)
10165     return;
10166
10167   // ---------- determine action paramater values -----------------------------
10168
10169   int level_time_value =
10170     (level.time > 0 ? TimeLeft :
10171      TimePlayed);
10172
10173   int action_arg_element_raw =
10174     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10175      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10176      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10177      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10178      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10179      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10180      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10181      EL_EMPTY);
10182   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10183
10184   int action_arg_direction =
10185     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10186      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10187      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10188      change->actual_trigger_side :
10189      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10190      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10191      MV_NONE);
10192
10193   int action_arg_number_min =
10194     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10195      CA_ARG_MIN);
10196
10197   int action_arg_number_max =
10198     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10199      action_type == CA_SET_LEVEL_GEMS ? 999 :
10200      action_type == CA_SET_LEVEL_TIME ? 9999 :
10201      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10202      action_type == CA_SET_CE_VALUE ? 9999 :
10203      action_type == CA_SET_CE_SCORE ? 9999 :
10204      CA_ARG_MAX);
10205
10206   int action_arg_number_reset =
10207     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10208      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10209      action_type == CA_SET_LEVEL_TIME ? level.time :
10210      action_type == CA_SET_LEVEL_SCORE ? 0 :
10211      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10212      action_type == CA_SET_CE_SCORE ? 0 :
10213      0);
10214
10215   int action_arg_number =
10216     (action_arg <= CA_ARG_MAX ? action_arg :
10217      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10218      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10219      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10220      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10221      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10222      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10223      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10224      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10225      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10226      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10227      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10228      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10229      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10230      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10231      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10232      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10233      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10234      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10235      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10236      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10237      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10238      -1);
10239
10240   int action_arg_number_old =
10241     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10242      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10243      action_type == CA_SET_LEVEL_SCORE ? game.score :
10244      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10245      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10246      0);
10247
10248   int action_arg_number_new =
10249     getModifiedActionNumber(action_arg_number_old,
10250                             action_mode, action_arg_number,
10251                             action_arg_number_min, action_arg_number_max);
10252
10253   int trigger_player_bits =
10254     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10255      change->actual_trigger_player_bits : change->trigger_player);
10256
10257   int action_arg_player_bits =
10258     (action_arg >= CA_ARG_PLAYER_1 &&
10259      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10260      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10261      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10262      PLAYER_BITS_ANY);
10263
10264   // ---------- execute action  -----------------------------------------------
10265
10266   switch (action_type)
10267   {
10268     case CA_NO_ACTION:
10269     {
10270       return;
10271     }
10272
10273     // ---------- level actions  ----------------------------------------------
10274
10275     case CA_RESTART_LEVEL:
10276     {
10277       game.restart_level = TRUE;
10278
10279       break;
10280     }
10281
10282     case CA_SHOW_ENVELOPE:
10283     {
10284       int element = getSpecialActionElement(action_arg_element,
10285                                             action_arg_number, EL_ENVELOPE_1);
10286
10287       if (IS_ENVELOPE(element))
10288         local_player->show_envelope = element;
10289
10290       break;
10291     }
10292
10293     case CA_SET_LEVEL_TIME:
10294     {
10295       if (level.time > 0)       // only modify limited time value
10296       {
10297         TimeLeft = action_arg_number_new;
10298
10299         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10300
10301         DisplayGameControlValues();
10302
10303         if (!TimeLeft && game.time_limit)
10304           for (i = 0; i < MAX_PLAYERS; i++)
10305             KillPlayer(&stored_player[i]);
10306       }
10307
10308       break;
10309     }
10310
10311     case CA_SET_LEVEL_SCORE:
10312     {
10313       game.score = action_arg_number_new;
10314
10315       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10316
10317       DisplayGameControlValues();
10318
10319       break;
10320     }
10321
10322     case CA_SET_LEVEL_GEMS:
10323     {
10324       game.gems_still_needed = action_arg_number_new;
10325
10326       game.snapshot.collected_item = TRUE;
10327
10328       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10329
10330       DisplayGameControlValues();
10331
10332       break;
10333     }
10334
10335     case CA_SET_LEVEL_WIND:
10336     {
10337       game.wind_direction = action_arg_direction;
10338
10339       break;
10340     }
10341
10342     case CA_SET_LEVEL_RANDOM_SEED:
10343     {
10344       // ensure that setting a new random seed while playing is predictable
10345       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10346
10347       break;
10348     }
10349
10350     // ---------- player actions  ---------------------------------------------
10351
10352     case CA_MOVE_PLAYER:
10353     case CA_MOVE_PLAYER_NEW:
10354     {
10355       // automatically move to the next field in specified direction
10356       for (i = 0; i < MAX_PLAYERS; i++)
10357         if (trigger_player_bits & (1 << i))
10358           if (action_type == CA_MOVE_PLAYER ||
10359               stored_player[i].MovPos == 0)
10360             stored_player[i].programmed_action = action_arg_direction;
10361
10362       break;
10363     }
10364
10365     case CA_EXIT_PLAYER:
10366     {
10367       for (i = 0; i < MAX_PLAYERS; i++)
10368         if (action_arg_player_bits & (1 << i))
10369           ExitPlayer(&stored_player[i]);
10370
10371       if (game.players_still_needed == 0)
10372         LevelSolved();
10373
10374       break;
10375     }
10376
10377     case CA_KILL_PLAYER:
10378     {
10379       for (i = 0; i < MAX_PLAYERS; i++)
10380         if (action_arg_player_bits & (1 << i))
10381           KillPlayer(&stored_player[i]);
10382
10383       break;
10384     }
10385
10386     case CA_SET_PLAYER_KEYS:
10387     {
10388       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10389       int element = getSpecialActionElement(action_arg_element,
10390                                             action_arg_number, EL_KEY_1);
10391
10392       if (IS_KEY(element))
10393       {
10394         for (i = 0; i < MAX_PLAYERS; i++)
10395         {
10396           if (trigger_player_bits & (1 << i))
10397           {
10398             stored_player[i].key[KEY_NR(element)] = key_state;
10399
10400             DrawGameDoorValues();
10401           }
10402         }
10403       }
10404
10405       break;
10406     }
10407
10408     case CA_SET_PLAYER_SPEED:
10409     {
10410       for (i = 0; i < MAX_PLAYERS; i++)
10411       {
10412         if (trigger_player_bits & (1 << i))
10413         {
10414           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10415
10416           if (action_arg == CA_ARG_SPEED_FASTER &&
10417               stored_player[i].cannot_move)
10418           {
10419             action_arg_number = STEPSIZE_VERY_SLOW;
10420           }
10421           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10422                    action_arg == CA_ARG_SPEED_FASTER)
10423           {
10424             action_arg_number = 2;
10425             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10426                            CA_MODE_MULTIPLY);
10427           }
10428           else if (action_arg == CA_ARG_NUMBER_RESET)
10429           {
10430             action_arg_number = level.initial_player_stepsize[i];
10431           }
10432
10433           move_stepsize =
10434             getModifiedActionNumber(move_stepsize,
10435                                     action_mode,
10436                                     action_arg_number,
10437                                     action_arg_number_min,
10438                                     action_arg_number_max);
10439
10440           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10441         }
10442       }
10443
10444       break;
10445     }
10446
10447     case CA_SET_PLAYER_SHIELD:
10448     {
10449       for (i = 0; i < MAX_PLAYERS; i++)
10450       {
10451         if (trigger_player_bits & (1 << i))
10452         {
10453           if (action_arg == CA_ARG_SHIELD_OFF)
10454           {
10455             stored_player[i].shield_normal_time_left = 0;
10456             stored_player[i].shield_deadly_time_left = 0;
10457           }
10458           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10459           {
10460             stored_player[i].shield_normal_time_left = 999999;
10461           }
10462           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10463           {
10464             stored_player[i].shield_normal_time_left = 999999;
10465             stored_player[i].shield_deadly_time_left = 999999;
10466           }
10467         }
10468       }
10469
10470       break;
10471     }
10472
10473     case CA_SET_PLAYER_GRAVITY:
10474     {
10475       for (i = 0; i < MAX_PLAYERS; i++)
10476       {
10477         if (trigger_player_bits & (1 << i))
10478         {
10479           stored_player[i].gravity =
10480             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10481              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10482              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10483              stored_player[i].gravity);
10484         }
10485       }
10486
10487       break;
10488     }
10489
10490     case CA_SET_PLAYER_ARTWORK:
10491     {
10492       for (i = 0; i < MAX_PLAYERS; i++)
10493       {
10494         if (trigger_player_bits & (1 << i))
10495         {
10496           int artwork_element = action_arg_element;
10497
10498           if (action_arg == CA_ARG_ELEMENT_RESET)
10499             artwork_element =
10500               (level.use_artwork_element[i] ? level.artwork_element[i] :
10501                stored_player[i].element_nr);
10502
10503           if (stored_player[i].artwork_element != artwork_element)
10504             stored_player[i].Frame = 0;
10505
10506           stored_player[i].artwork_element = artwork_element;
10507
10508           SetPlayerWaiting(&stored_player[i], FALSE);
10509
10510           // set number of special actions for bored and sleeping animation
10511           stored_player[i].num_special_action_bored =
10512             get_num_special_action(artwork_element,
10513                                    ACTION_BORING_1, ACTION_BORING_LAST);
10514           stored_player[i].num_special_action_sleeping =
10515             get_num_special_action(artwork_element,
10516                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10517         }
10518       }
10519
10520       break;
10521     }
10522
10523     case CA_SET_PLAYER_INVENTORY:
10524     {
10525       for (i = 0; i < MAX_PLAYERS; i++)
10526       {
10527         struct PlayerInfo *player = &stored_player[i];
10528         int j, k;
10529
10530         if (trigger_player_bits & (1 << i))
10531         {
10532           int inventory_element = action_arg_element;
10533
10534           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10535               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10536               action_arg == CA_ARG_ELEMENT_ACTION)
10537           {
10538             int element = inventory_element;
10539             int collect_count = element_info[element].collect_count_initial;
10540
10541             if (!IS_CUSTOM_ELEMENT(element))
10542               collect_count = 1;
10543
10544             if (collect_count == 0)
10545               player->inventory_infinite_element = element;
10546             else
10547               for (k = 0; k < collect_count; k++)
10548                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10549                   player->inventory_element[player->inventory_size++] =
10550                     element;
10551           }
10552           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10553                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10554                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10555           {
10556             if (player->inventory_infinite_element != EL_UNDEFINED &&
10557                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10558                                      action_arg_element_raw))
10559               player->inventory_infinite_element = EL_UNDEFINED;
10560
10561             for (k = 0, j = 0; j < player->inventory_size; j++)
10562             {
10563               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10564                                         action_arg_element_raw))
10565                 player->inventory_element[k++] = player->inventory_element[j];
10566             }
10567
10568             player->inventory_size = k;
10569           }
10570           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10571           {
10572             if (player->inventory_size > 0)
10573             {
10574               for (j = 0; j < player->inventory_size - 1; j++)
10575                 player->inventory_element[j] = player->inventory_element[j + 1];
10576
10577               player->inventory_size--;
10578             }
10579           }
10580           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10581           {
10582             if (player->inventory_size > 0)
10583               player->inventory_size--;
10584           }
10585           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10586           {
10587             player->inventory_infinite_element = EL_UNDEFINED;
10588             player->inventory_size = 0;
10589           }
10590           else if (action_arg == CA_ARG_INVENTORY_RESET)
10591           {
10592             player->inventory_infinite_element = EL_UNDEFINED;
10593             player->inventory_size = 0;
10594
10595             if (level.use_initial_inventory[i])
10596             {
10597               for (j = 0; j < level.initial_inventory_size[i]; j++)
10598               {
10599                 int element = level.initial_inventory_content[i][j];
10600                 int collect_count = element_info[element].collect_count_initial;
10601
10602                 if (!IS_CUSTOM_ELEMENT(element))
10603                   collect_count = 1;
10604
10605                 if (collect_count == 0)
10606                   player->inventory_infinite_element = element;
10607                 else
10608                   for (k = 0; k < collect_count; k++)
10609                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10610                       player->inventory_element[player->inventory_size++] =
10611                         element;
10612               }
10613             }
10614           }
10615         }
10616       }
10617
10618       break;
10619     }
10620
10621     // ---------- CE actions  -------------------------------------------------
10622
10623     case CA_SET_CE_VALUE:
10624     {
10625       int last_ce_value = CustomValue[x][y];
10626
10627       CustomValue[x][y] = action_arg_number_new;
10628
10629       if (CustomValue[x][y] != last_ce_value)
10630       {
10631         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10632         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10633
10634         if (CustomValue[x][y] == 0)
10635         {
10636           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10637           ChangeCount[x][y] = 0;        // allow at least one more change
10638
10639           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10640           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10641         }
10642       }
10643
10644       break;
10645     }
10646
10647     case CA_SET_CE_SCORE:
10648     {
10649       int last_ce_score = ei->collect_score;
10650
10651       ei->collect_score = action_arg_number_new;
10652
10653       if (ei->collect_score != last_ce_score)
10654       {
10655         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10656         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10657
10658         if (ei->collect_score == 0)
10659         {
10660           int xx, yy;
10661
10662           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10663           ChangeCount[x][y] = 0;        // allow at least one more change
10664
10665           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10666           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10667
10668           /*
10669             This is a very special case that seems to be a mixture between
10670             CheckElementChange() and CheckTriggeredElementChange(): while
10671             the first one only affects single elements that are triggered
10672             directly, the second one affects multiple elements in the playfield
10673             that are triggered indirectly by another element. This is a third
10674             case: Changing the CE score always affects multiple identical CEs,
10675             so every affected CE must be checked, not only the single CE for
10676             which the CE score was changed in the first place (as every instance
10677             of that CE shares the same CE score, and therefore also can change)!
10678           */
10679           SCAN_PLAYFIELD(xx, yy)
10680           {
10681             if (Tile[xx][yy] == element)
10682               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10683                                  CE_SCORE_GETS_ZERO);
10684           }
10685         }
10686       }
10687
10688       break;
10689     }
10690
10691     case CA_SET_CE_ARTWORK:
10692     {
10693       int artwork_element = action_arg_element;
10694       boolean reset_frame = FALSE;
10695       int xx, yy;
10696
10697       if (action_arg == CA_ARG_ELEMENT_RESET)
10698         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10699                            element);
10700
10701       if (ei->gfx_element != artwork_element)
10702         reset_frame = TRUE;
10703
10704       ei->gfx_element = artwork_element;
10705
10706       SCAN_PLAYFIELD(xx, yy)
10707       {
10708         if (Tile[xx][yy] == element)
10709         {
10710           if (reset_frame)
10711           {
10712             ResetGfxAnimation(xx, yy);
10713             ResetRandomAnimationValue(xx, yy);
10714           }
10715
10716           TEST_DrawLevelField(xx, yy);
10717         }
10718       }
10719
10720       break;
10721     }
10722
10723     // ---------- engine actions  ---------------------------------------------
10724
10725     case CA_SET_ENGINE_SCAN_MODE:
10726     {
10727       InitPlayfieldScanMode(action_arg);
10728
10729       break;
10730     }
10731
10732     default:
10733       break;
10734   }
10735 }
10736
10737 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10738 {
10739   int old_element = Tile[x][y];
10740   int new_element = GetElementFromGroupElement(element);
10741   int previous_move_direction = MovDir[x][y];
10742   int last_ce_value = CustomValue[x][y];
10743   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10744   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10745   boolean add_player_onto_element = (new_element_is_player &&
10746                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10747                                      IS_WALKABLE(old_element));
10748
10749   if (!add_player_onto_element)
10750   {
10751     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10752       RemoveMovingField(x, y);
10753     else
10754       RemoveField(x, y);
10755
10756     Tile[x][y] = new_element;
10757
10758     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10759       MovDir[x][y] = previous_move_direction;
10760
10761     if (element_info[new_element].use_last_ce_value)
10762       CustomValue[x][y] = last_ce_value;
10763
10764     InitField_WithBug1(x, y, FALSE);
10765
10766     new_element = Tile[x][y];   // element may have changed
10767
10768     ResetGfxAnimation(x, y);
10769     ResetRandomAnimationValue(x, y);
10770
10771     TEST_DrawLevelField(x, y);
10772
10773     if (GFX_CRUMBLED(new_element))
10774       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10775
10776     if (old_element == EL_EXPLOSION)
10777     {
10778       Store[x][y] = Store2[x][y] = 0;
10779
10780       // check if new element replaces an exploding player, requiring cleanup
10781       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10782         StorePlayer[x][y] = 0;
10783     }
10784
10785     // check if element under the player changes from accessible to unaccessible
10786     // (needed for special case of dropping element which then changes)
10787     // (must be checked after creating new element for walkable group elements)
10788     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10789         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10790     {
10791       KillPlayer(PLAYERINFO(x, y));
10792
10793       return;
10794     }
10795   }
10796
10797   // "ChangeCount" not set yet to allow "entered by player" change one time
10798   if (new_element_is_player)
10799     RelocatePlayer(x, y, new_element);
10800
10801   if (is_change)
10802     ChangeCount[x][y]++;        // count number of changes in the same frame
10803
10804   TestIfBadThingTouchesPlayer(x, y);
10805   TestIfPlayerTouchesCustomElement(x, y);
10806   TestIfElementTouchesCustomElement(x, y);
10807 }
10808
10809 static void CreateField(int x, int y, int element)
10810 {
10811   CreateFieldExt(x, y, element, FALSE);
10812 }
10813
10814 static void CreateElementFromChange(int x, int y, int element)
10815 {
10816   element = GET_VALID_RUNTIME_ELEMENT(element);
10817
10818   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10819   {
10820     int old_element = Tile[x][y];
10821
10822     // prevent changed element from moving in same engine frame
10823     // unless both old and new element can either fall or move
10824     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10825         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10826       Stop[x][y] = TRUE;
10827   }
10828
10829   CreateFieldExt(x, y, element, TRUE);
10830 }
10831
10832 static boolean ChangeElement(int x, int y, int element, int page)
10833 {
10834   struct ElementInfo *ei = &element_info[element];
10835   struct ElementChangeInfo *change = &ei->change_page[page];
10836   int ce_value = CustomValue[x][y];
10837   int ce_score = ei->collect_score;
10838   int target_element;
10839   int old_element = Tile[x][y];
10840
10841   // always use default change event to prevent running into a loop
10842   if (ChangeEvent[x][y] == -1)
10843     ChangeEvent[x][y] = CE_DELAY;
10844
10845   if (ChangeEvent[x][y] == CE_DELAY)
10846   {
10847     // reset actual trigger element, trigger player and action element
10848     change->actual_trigger_element = EL_EMPTY;
10849     change->actual_trigger_player = EL_EMPTY;
10850     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10851     change->actual_trigger_side = CH_SIDE_NONE;
10852     change->actual_trigger_ce_value = 0;
10853     change->actual_trigger_ce_score = 0;
10854     change->actual_trigger_x = -1;
10855     change->actual_trigger_y = -1;
10856   }
10857
10858   // do not change elements more than a specified maximum number of changes
10859   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10860     return FALSE;
10861
10862   ChangeCount[x][y]++;          // count number of changes in the same frame
10863
10864   if (ei->has_anim_event)
10865     HandleGlobalAnimEventByElementChange(element, page, x, y,
10866                                          change->actual_trigger_x,
10867                                          change->actual_trigger_y);
10868
10869   if (change->explode)
10870   {
10871     Bang(x, y);
10872
10873     return TRUE;
10874   }
10875
10876   if (change->use_target_content)
10877   {
10878     boolean complete_replace = TRUE;
10879     boolean can_replace[3][3];
10880     int xx, yy;
10881
10882     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10883     {
10884       boolean is_empty;
10885       boolean is_walkable;
10886       boolean is_diggable;
10887       boolean is_collectible;
10888       boolean is_removable;
10889       boolean is_destructible;
10890       int ex = x + xx - 1;
10891       int ey = y + yy - 1;
10892       int content_element = change->target_content.e[xx][yy];
10893       int e;
10894
10895       can_replace[xx][yy] = TRUE;
10896
10897       if (ex == x && ey == y)   // do not check changing element itself
10898         continue;
10899
10900       if (content_element == EL_EMPTY_SPACE)
10901       {
10902         can_replace[xx][yy] = FALSE;    // do not replace border with space
10903
10904         continue;
10905       }
10906
10907       if (!IN_LEV_FIELD(ex, ey))
10908       {
10909         can_replace[xx][yy] = FALSE;
10910         complete_replace = FALSE;
10911
10912         continue;
10913       }
10914
10915       e = Tile[ex][ey];
10916
10917       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10918         e = MovingOrBlocked2Element(ex, ey);
10919
10920       is_empty = (IS_FREE(ex, ey) ||
10921                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10922
10923       is_walkable     = (is_empty || IS_WALKABLE(e));
10924       is_diggable     = (is_empty || IS_DIGGABLE(e));
10925       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10926       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10927       is_removable    = (is_diggable || is_collectible);
10928
10929       can_replace[xx][yy] =
10930         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10931           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10932           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10933           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10934           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10935           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10936          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10937
10938       if (!can_replace[xx][yy])
10939         complete_replace = FALSE;
10940     }
10941
10942     if (!change->only_if_complete || complete_replace)
10943     {
10944       boolean something_has_changed = FALSE;
10945
10946       if (change->only_if_complete && change->use_random_replace &&
10947           RND(100) < change->random_percentage)
10948         return FALSE;
10949
10950       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10951       {
10952         int ex = x + xx - 1;
10953         int ey = y + yy - 1;
10954         int content_element;
10955
10956         if (can_replace[xx][yy] && (!change->use_random_replace ||
10957                                     RND(100) < change->random_percentage))
10958         {
10959           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10960             RemoveMovingField(ex, ey);
10961
10962           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10963
10964           content_element = change->target_content.e[xx][yy];
10965           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10966                                               ce_value, ce_score);
10967
10968           CreateElementFromChange(ex, ey, target_element);
10969
10970           something_has_changed = TRUE;
10971
10972           // for symmetry reasons, freeze newly created border elements
10973           if (ex != x || ey != y)
10974             Stop[ex][ey] = TRUE;        // no more moving in this frame
10975         }
10976       }
10977
10978       if (something_has_changed)
10979       {
10980         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10981         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10982       }
10983     }
10984   }
10985   else
10986   {
10987     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10988                                         ce_value, ce_score);
10989
10990     if (element == EL_DIAGONAL_GROWING ||
10991         element == EL_DIAGONAL_SHRINKING)
10992     {
10993       target_element = Store[x][y];
10994
10995       Store[x][y] = EL_EMPTY;
10996     }
10997
10998     // special case: element changes to player (and may be kept if walkable)
10999     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
11000       CreateElementFromChange(x, y, EL_EMPTY);
11001
11002     CreateElementFromChange(x, y, target_element);
11003
11004     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11005     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11006   }
11007
11008   // this uses direct change before indirect change
11009   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11010
11011   return TRUE;
11012 }
11013
11014 static void HandleElementChange(int x, int y, int page)
11015 {
11016   int element = MovingOrBlocked2Element(x, y);
11017   struct ElementInfo *ei = &element_info[element];
11018   struct ElementChangeInfo *change = &ei->change_page[page];
11019   boolean handle_action_before_change = FALSE;
11020
11021 #ifdef DEBUG
11022   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11023       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11024   {
11025     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11026           x, y, element, element_info[element].token_name);
11027     Debug("game:playing:HandleElementChange", "This should never happen!");
11028   }
11029 #endif
11030
11031   // this can happen with classic bombs on walkable, changing elements
11032   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11033   {
11034     return;
11035   }
11036
11037   if (ChangeDelay[x][y] == 0)           // initialize element change
11038   {
11039     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11040
11041     if (change->can_change)
11042     {
11043       // !!! not clear why graphic animation should be reset at all here !!!
11044       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11045       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11046
11047       /*
11048         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11049
11050         When using an animation frame delay of 1 (this only happens with
11051         "sp_zonk.moving.left/right" in the classic graphics), the default
11052         (non-moving) animation shows wrong animation frames (while the
11053         moving animation, like "sp_zonk.moving.left/right", is correct,
11054         so this graphical bug never shows up with the classic graphics).
11055         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11056         be drawn instead of the correct frames 0,1,2,3. This is caused by
11057         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11058         an element change: First when the change delay ("ChangeDelay[][]")
11059         counter has reached zero after decrementing, then a second time in
11060         the next frame (after "GfxFrame[][]" was already incremented) when
11061         "ChangeDelay[][]" is reset to the initial delay value again.
11062
11063         This causes frame 0 to be drawn twice, while the last frame won't
11064         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11065
11066         As some animations may already be cleverly designed around this bug
11067         (at least the "Snake Bite" snake tail animation does this), it cannot
11068         simply be fixed here without breaking such existing animations.
11069         Unfortunately, it cannot easily be detected if a graphics set was
11070         designed "before" or "after" the bug was fixed. As a workaround,
11071         a new graphics set option "game.graphics_engine_version" was added
11072         to be able to specify the game's major release version for which the
11073         graphics set was designed, which can then be used to decide if the
11074         bugfix should be used (version 4 and above) or not (version 3 or
11075         below, or if no version was specified at all, as with old sets).
11076
11077         (The wrong/fixed animation frames can be tested with the test level set
11078         "test_gfxframe" and level "000", which contains a specially prepared
11079         custom element at level position (x/y) == (11/9) which uses the zonk
11080         animation mentioned above. Using "game.graphics_engine_version: 4"
11081         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11082         This can also be seen from the debug output for this test element.)
11083       */
11084
11085       // when a custom element is about to change (for example by change delay),
11086       // do not reset graphic animation when the custom element is moving
11087       if (game.graphics_engine_version < 4 &&
11088           !IS_MOVING(x, y))
11089       {
11090         ResetGfxAnimation(x, y);
11091         ResetRandomAnimationValue(x, y);
11092       }
11093
11094       if (change->pre_change_function)
11095         change->pre_change_function(x, y);
11096     }
11097   }
11098
11099   ChangeDelay[x][y]--;
11100
11101   if (ChangeDelay[x][y] != 0)           // continue element change
11102   {
11103     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11104
11105     // also needed if CE can not change, but has CE delay with CE action
11106     if (IS_ANIMATED(graphic))
11107       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11108
11109     if (change->can_change)
11110     {
11111       if (change->change_function)
11112         change->change_function(x, y);
11113     }
11114   }
11115   else                                  // finish element change
11116   {
11117     if (ChangePage[x][y] != -1)         // remember page from delayed change
11118     {
11119       page = ChangePage[x][y];
11120       ChangePage[x][y] = -1;
11121
11122       change = &ei->change_page[page];
11123     }
11124
11125     if (IS_MOVING(x, y))                // never change a running system ;-)
11126     {
11127       ChangeDelay[x][y] = 1;            // try change after next move step
11128       ChangePage[x][y] = page;          // remember page to use for change
11129
11130       return;
11131     }
11132
11133     // special case: set new level random seed before changing element
11134     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11135       handle_action_before_change = TRUE;
11136
11137     if (change->has_action && handle_action_before_change)
11138       ExecuteCustomElementAction(x, y, element, page);
11139
11140     if (change->can_change)
11141     {
11142       if (ChangeElement(x, y, element, page))
11143       {
11144         if (change->post_change_function)
11145           change->post_change_function(x, y);
11146       }
11147     }
11148
11149     if (change->has_action && !handle_action_before_change)
11150       ExecuteCustomElementAction(x, y, element, page);
11151   }
11152 }
11153
11154 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11155                                               int trigger_element,
11156                                               int trigger_event,
11157                                               int trigger_player,
11158                                               int trigger_side,
11159                                               int trigger_page)
11160 {
11161   boolean change_done_any = FALSE;
11162   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11163   int i;
11164
11165   if (!(trigger_events[trigger_element][trigger_event]))
11166     return FALSE;
11167
11168   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11169
11170   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11171   {
11172     int element = EL_CUSTOM_START + i;
11173     boolean change_done = FALSE;
11174     int p;
11175
11176     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11177         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11178       continue;
11179
11180     for (p = 0; p < element_info[element].num_change_pages; p++)
11181     {
11182       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11183
11184       if (change->can_change_or_has_action &&
11185           change->has_event[trigger_event] &&
11186           change->trigger_side & trigger_side &&
11187           change->trigger_player & trigger_player &&
11188           change->trigger_page & trigger_page_bits &&
11189           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11190       {
11191         change->actual_trigger_element = trigger_element;
11192         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11193         change->actual_trigger_player_bits = trigger_player;
11194         change->actual_trigger_side = trigger_side;
11195         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11196         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11197         change->actual_trigger_x = trigger_x;
11198         change->actual_trigger_y = trigger_y;
11199
11200         if ((change->can_change && !change_done) || change->has_action)
11201         {
11202           int x, y;
11203
11204           SCAN_PLAYFIELD(x, y)
11205           {
11206             if (Tile[x][y] == element)
11207             {
11208               if (change->can_change && !change_done)
11209               {
11210                 // if element already changed in this frame, not only prevent
11211                 // another element change (checked in ChangeElement()), but
11212                 // also prevent additional element actions for this element
11213
11214                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11215                     !level.use_action_after_change_bug)
11216                   continue;
11217
11218                 ChangeDelay[x][y] = 1;
11219                 ChangeEvent[x][y] = trigger_event;
11220
11221                 HandleElementChange(x, y, p);
11222               }
11223               else if (change->has_action)
11224               {
11225                 // if element already changed in this frame, not only prevent
11226                 // another element change (checked in ChangeElement()), but
11227                 // also prevent additional element actions for this element
11228
11229                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11230                     !level.use_action_after_change_bug)
11231                   continue;
11232
11233                 ExecuteCustomElementAction(x, y, element, p);
11234                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11235               }
11236             }
11237           }
11238
11239           if (change->can_change)
11240           {
11241             change_done = TRUE;
11242             change_done_any = TRUE;
11243           }
11244         }
11245       }
11246     }
11247   }
11248
11249   RECURSION_LOOP_DETECTION_END();
11250
11251   return change_done_any;
11252 }
11253
11254 static boolean CheckElementChangeExt(int x, int y,
11255                                      int element,
11256                                      int trigger_element,
11257                                      int trigger_event,
11258                                      int trigger_player,
11259                                      int trigger_side)
11260 {
11261   boolean change_done = FALSE;
11262   int p;
11263
11264   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11265       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11266     return FALSE;
11267
11268   if (Tile[x][y] == EL_BLOCKED)
11269   {
11270     Blocked2Moving(x, y, &x, &y);
11271     element = Tile[x][y];
11272   }
11273
11274   // check if element has already changed or is about to change after moving
11275   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11276        Tile[x][y] != element) ||
11277
11278       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11279        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11280         ChangePage[x][y] != -1)))
11281     return FALSE;
11282
11283   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11284
11285   for (p = 0; p < element_info[element].num_change_pages; p++)
11286   {
11287     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11288
11289     /* check trigger element for all events where the element that is checked
11290        for changing interacts with a directly adjacent element -- this is
11291        different to element changes that affect other elements to change on the
11292        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11293     boolean check_trigger_element =
11294       (trigger_event == CE_NEXT_TO_X ||
11295        trigger_event == CE_TOUCHING_X ||
11296        trigger_event == CE_HITTING_X ||
11297        trigger_event == CE_HIT_BY_X ||
11298        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11299
11300     if (change->can_change_or_has_action &&
11301         change->has_event[trigger_event] &&
11302         change->trigger_side & trigger_side &&
11303         change->trigger_player & trigger_player &&
11304         (!check_trigger_element ||
11305          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11306     {
11307       change->actual_trigger_element = trigger_element;
11308       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11309       change->actual_trigger_player_bits = trigger_player;
11310       change->actual_trigger_side = trigger_side;
11311       change->actual_trigger_ce_value = CustomValue[x][y];
11312       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11313       change->actual_trigger_x = x;
11314       change->actual_trigger_y = y;
11315
11316       // special case: trigger element not at (x,y) position for some events
11317       if (check_trigger_element)
11318       {
11319         static struct
11320         {
11321           int dx, dy;
11322         } move_xy[] =
11323           {
11324             {  0,  0 },
11325             { -1,  0 },
11326             { +1,  0 },
11327             {  0,  0 },
11328             {  0, -1 },
11329             {  0,  0 }, { 0, 0 }, { 0, 0 },
11330             {  0, +1 }
11331           };
11332
11333         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11334         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11335
11336         change->actual_trigger_ce_value = CustomValue[xx][yy];
11337         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11338         change->actual_trigger_x = xx;
11339         change->actual_trigger_y = yy;
11340       }
11341
11342       if (change->can_change && !change_done)
11343       {
11344         ChangeDelay[x][y] = 1;
11345         ChangeEvent[x][y] = trigger_event;
11346
11347         HandleElementChange(x, y, p);
11348
11349         change_done = TRUE;
11350       }
11351       else if (change->has_action)
11352       {
11353         ExecuteCustomElementAction(x, y, element, p);
11354         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11355       }
11356     }
11357   }
11358
11359   RECURSION_LOOP_DETECTION_END();
11360
11361   return change_done;
11362 }
11363
11364 static void PlayPlayerSound(struct PlayerInfo *player)
11365 {
11366   int jx = player->jx, jy = player->jy;
11367   int sound_element = player->artwork_element;
11368   int last_action = player->last_action_waiting;
11369   int action = player->action_waiting;
11370
11371   if (player->is_waiting)
11372   {
11373     if (action != last_action)
11374       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11375     else
11376       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11377   }
11378   else
11379   {
11380     if (action != last_action)
11381       StopSound(element_info[sound_element].sound[last_action]);
11382
11383     if (last_action == ACTION_SLEEPING)
11384       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11385   }
11386 }
11387
11388 static void PlayAllPlayersSound(void)
11389 {
11390   int i;
11391
11392   for (i = 0; i < MAX_PLAYERS; i++)
11393     if (stored_player[i].active)
11394       PlayPlayerSound(&stored_player[i]);
11395 }
11396
11397 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11398 {
11399   boolean last_waiting = player->is_waiting;
11400   int move_dir = player->MovDir;
11401
11402   player->dir_waiting = move_dir;
11403   player->last_action_waiting = player->action_waiting;
11404
11405   if (is_waiting)
11406   {
11407     if (!last_waiting)          // not waiting -> waiting
11408     {
11409       player->is_waiting = TRUE;
11410
11411       player->frame_counter_bored =
11412         FrameCounter +
11413         game.player_boring_delay_fixed +
11414         GetSimpleRandom(game.player_boring_delay_random);
11415       player->frame_counter_sleeping =
11416         FrameCounter +
11417         game.player_sleeping_delay_fixed +
11418         GetSimpleRandom(game.player_sleeping_delay_random);
11419
11420       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11421     }
11422
11423     if (game.player_sleeping_delay_fixed +
11424         game.player_sleeping_delay_random > 0 &&
11425         player->anim_delay_counter == 0 &&
11426         player->post_delay_counter == 0 &&
11427         FrameCounter >= player->frame_counter_sleeping)
11428       player->is_sleeping = TRUE;
11429     else if (game.player_boring_delay_fixed +
11430              game.player_boring_delay_random > 0 &&
11431              FrameCounter >= player->frame_counter_bored)
11432       player->is_bored = TRUE;
11433
11434     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11435                               player->is_bored ? ACTION_BORING :
11436                               ACTION_WAITING);
11437
11438     if (player->is_sleeping && player->use_murphy)
11439     {
11440       // special case for sleeping Murphy when leaning against non-free tile
11441
11442       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11443           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11444            !IS_MOVING(player->jx - 1, player->jy)))
11445         move_dir = MV_LEFT;
11446       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11447                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11448                 !IS_MOVING(player->jx + 1, player->jy)))
11449         move_dir = MV_RIGHT;
11450       else
11451         player->is_sleeping = FALSE;
11452
11453       player->dir_waiting = move_dir;
11454     }
11455
11456     if (player->is_sleeping)
11457     {
11458       if (player->num_special_action_sleeping > 0)
11459       {
11460         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11461         {
11462           int last_special_action = player->special_action_sleeping;
11463           int num_special_action = player->num_special_action_sleeping;
11464           int special_action =
11465             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11466              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11467              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11468              last_special_action + 1 : ACTION_SLEEPING);
11469           int special_graphic =
11470             el_act_dir2img(player->artwork_element, special_action, move_dir);
11471
11472           player->anim_delay_counter =
11473             graphic_info[special_graphic].anim_delay_fixed +
11474             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11475           player->post_delay_counter =
11476             graphic_info[special_graphic].post_delay_fixed +
11477             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11478
11479           player->special_action_sleeping = special_action;
11480         }
11481
11482         if (player->anim_delay_counter > 0)
11483         {
11484           player->action_waiting = player->special_action_sleeping;
11485           player->anim_delay_counter--;
11486         }
11487         else if (player->post_delay_counter > 0)
11488         {
11489           player->post_delay_counter--;
11490         }
11491       }
11492     }
11493     else if (player->is_bored)
11494     {
11495       if (player->num_special_action_bored > 0)
11496       {
11497         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11498         {
11499           int special_action =
11500             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11501           int special_graphic =
11502             el_act_dir2img(player->artwork_element, special_action, move_dir);
11503
11504           player->anim_delay_counter =
11505             graphic_info[special_graphic].anim_delay_fixed +
11506             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11507           player->post_delay_counter =
11508             graphic_info[special_graphic].post_delay_fixed +
11509             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11510
11511           player->special_action_bored = special_action;
11512         }
11513
11514         if (player->anim_delay_counter > 0)
11515         {
11516           player->action_waiting = player->special_action_bored;
11517           player->anim_delay_counter--;
11518         }
11519         else if (player->post_delay_counter > 0)
11520         {
11521           player->post_delay_counter--;
11522         }
11523       }
11524     }
11525   }
11526   else if (last_waiting)        // waiting -> not waiting
11527   {
11528     player->is_waiting = FALSE;
11529     player->is_bored = FALSE;
11530     player->is_sleeping = FALSE;
11531
11532     player->frame_counter_bored = -1;
11533     player->frame_counter_sleeping = -1;
11534
11535     player->anim_delay_counter = 0;
11536     player->post_delay_counter = 0;
11537
11538     player->dir_waiting = player->MovDir;
11539     player->action_waiting = ACTION_DEFAULT;
11540
11541     player->special_action_bored = ACTION_DEFAULT;
11542     player->special_action_sleeping = ACTION_DEFAULT;
11543   }
11544 }
11545
11546 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11547 {
11548   if ((!player->is_moving  && player->was_moving) ||
11549       (player->MovPos == 0 && player->was_moving) ||
11550       (player->is_snapping && !player->was_snapping) ||
11551       (player->is_dropping && !player->was_dropping))
11552   {
11553     if (!CheckSaveEngineSnapshotToList())
11554       return;
11555
11556     player->was_moving = FALSE;
11557     player->was_snapping = TRUE;
11558     player->was_dropping = TRUE;
11559   }
11560   else
11561   {
11562     if (player->is_moving)
11563       player->was_moving = TRUE;
11564
11565     if (!player->is_snapping)
11566       player->was_snapping = FALSE;
11567
11568     if (!player->is_dropping)
11569       player->was_dropping = FALSE;
11570   }
11571
11572   static struct MouseActionInfo mouse_action_last = { 0 };
11573   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11574   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11575
11576   if (new_released)
11577     CheckSaveEngineSnapshotToList();
11578
11579   mouse_action_last = mouse_action;
11580 }
11581
11582 static void CheckSingleStepMode(struct PlayerInfo *player)
11583 {
11584   if (tape.single_step && tape.recording && !tape.pausing)
11585   {
11586     // as it is called "single step mode", just return to pause mode when the
11587     // player stopped moving after one tile (or never starts moving at all)
11588     // (reverse logic needed here in case single step mode used in team mode)
11589     if (player->is_moving ||
11590         player->is_pushing ||
11591         player->is_dropping_pressed ||
11592         player->effective_mouse_action.button)
11593       game.enter_single_step_mode = FALSE;
11594   }
11595
11596   CheckSaveEngineSnapshot(player);
11597 }
11598
11599 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11600 {
11601   int left      = player_action & JOY_LEFT;
11602   int right     = player_action & JOY_RIGHT;
11603   int up        = player_action & JOY_UP;
11604   int down      = player_action & JOY_DOWN;
11605   int button1   = player_action & JOY_BUTTON_1;
11606   int button2   = player_action & JOY_BUTTON_2;
11607   int dx        = (left ? -1 : right ? 1 : 0);
11608   int dy        = (up   ? -1 : down  ? 1 : 0);
11609
11610   if (!player->active || tape.pausing)
11611     return 0;
11612
11613   if (player_action)
11614   {
11615     if (button1)
11616       SnapField(player, dx, dy);
11617     else
11618     {
11619       if (button2)
11620         DropElement(player);
11621
11622       MovePlayer(player, dx, dy);
11623     }
11624
11625     CheckSingleStepMode(player);
11626
11627     SetPlayerWaiting(player, FALSE);
11628
11629     return player_action;
11630   }
11631   else
11632   {
11633     // no actions for this player (no input at player's configured device)
11634
11635     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11636     SnapField(player, 0, 0);
11637     CheckGravityMovementWhenNotMoving(player);
11638
11639     if (player->MovPos == 0)
11640       SetPlayerWaiting(player, TRUE);
11641
11642     if (player->MovPos == 0)    // needed for tape.playing
11643       player->is_moving = FALSE;
11644
11645     player->is_dropping = FALSE;
11646     player->is_dropping_pressed = FALSE;
11647     player->drop_pressed_delay = 0;
11648
11649     CheckSingleStepMode(player);
11650
11651     return 0;
11652   }
11653 }
11654
11655 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11656                                          byte *tape_action)
11657 {
11658   if (!tape.use_mouse_actions)
11659     return;
11660
11661   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11662   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11663   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11664 }
11665
11666 static void SetTapeActionFromMouseAction(byte *tape_action,
11667                                          struct MouseActionInfo *mouse_action)
11668 {
11669   if (!tape.use_mouse_actions)
11670     return;
11671
11672   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11673   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11674   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11675 }
11676
11677 static void CheckLevelSolved(void)
11678 {
11679   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11680   {
11681     if (game_bd.level_solved &&
11682         !game_bd.game_over)                             // game won
11683     {
11684       LevelSolved();
11685
11686       game_bd.game_over = TRUE;
11687
11688       game.all_players_gone = TRUE;
11689     }
11690
11691     if (game_bd.game_over)                              // game lost
11692       game.all_players_gone = TRUE;
11693   }
11694   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11695   {
11696     if (game_em.level_solved &&
11697         !game_em.game_over)                             // game won
11698     {
11699       LevelSolved();
11700
11701       game_em.game_over = TRUE;
11702
11703       game.all_players_gone = TRUE;
11704     }
11705
11706     if (game_em.game_over)                              // game lost
11707       game.all_players_gone = TRUE;
11708   }
11709   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11710   {
11711     if (game_sp.level_solved &&
11712         !game_sp.game_over)                             // game won
11713     {
11714       LevelSolved();
11715
11716       game_sp.game_over = TRUE;
11717
11718       game.all_players_gone = TRUE;
11719     }
11720
11721     if (game_sp.game_over)                              // game lost
11722       game.all_players_gone = TRUE;
11723   }
11724   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11725   {
11726     if (game_mm.level_solved &&
11727         !game_mm.game_over)                             // game won
11728     {
11729       LevelSolved();
11730
11731       game_mm.game_over = TRUE;
11732
11733       game.all_players_gone = TRUE;
11734     }
11735
11736     if (game_mm.game_over)                              // game lost
11737       game.all_players_gone = TRUE;
11738   }
11739 }
11740
11741 static void PlayTimeoutSound(int seconds_left)
11742 {
11743   // will be played directly by BD engine (for classic bonus time sounds)
11744   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11745     return;
11746
11747   // try to use individual "running out of time" sound for each second left
11748   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11749
11750   // if special sound per second not defined, use default sound
11751   if (getSoundInfoEntryFilename(sound) == NULL)
11752     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11753
11754   // if out of time, but player still alive, play special "timeout" sound, if defined
11755   if (seconds_left == 0 && !checkGameFailed())
11756     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11757       sound = SND_GAME_TIMEOUT;
11758
11759   PlaySound(sound);
11760 }
11761
11762 static void CheckLevelTime_StepCounter(void)
11763 {
11764   int i;
11765
11766   TimePlayed++;
11767
11768   if (TimeLeft > 0)
11769   {
11770     TimeLeft--;
11771
11772     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11773       PlayTimeoutSound(TimeLeft);
11774
11775     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11776
11777     DisplayGameControlValues();
11778
11779     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11780       for (i = 0; i < MAX_PLAYERS; i++)
11781         KillPlayer(&stored_player[i]);
11782   }
11783   else if (game.no_level_time_limit && !game.all_players_gone)
11784   {
11785     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11786
11787     DisplayGameControlValues();
11788   }
11789 }
11790
11791 static void CheckLevelTime(void)
11792 {
11793   int frames_per_second = FRAMES_PER_SECOND;
11794   int i;
11795
11796   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11797   {
11798     // level time may be running slower in native BD engine
11799     frames_per_second = getFramesPerSecond_BD();
11800
11801     // if native engine time changed, force main engine time change
11802     if (getTimeLeft_BD() < TimeLeft)
11803       TimeFrames = frames_per_second;
11804
11805     // if last second running, wait for native engine time to exactly reach zero
11806     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11807       TimeFrames = frames_per_second - 1;
11808   }
11809
11810   if (TimeFrames >= frames_per_second)
11811   {
11812     TimeFrames = 0;
11813
11814     for (i = 0; i < MAX_PLAYERS; i++)
11815     {
11816       struct PlayerInfo *player = &stored_player[i];
11817
11818       if (SHIELD_ON(player))
11819       {
11820         player->shield_normal_time_left--;
11821
11822         if (player->shield_deadly_time_left > 0)
11823           player->shield_deadly_time_left--;
11824       }
11825     }
11826
11827     if (!game.LevelSolved && !level.use_step_counter)
11828     {
11829       TimePlayed++;
11830
11831       if (TimeLeft > 0)
11832       {
11833         TimeLeft--;
11834
11835         if (TimeLeft <= 10 && game.time_limit)
11836           PlayTimeoutSound(TimeLeft);
11837
11838         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11839            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11840
11841         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11842
11843         if (!TimeLeft && game.time_limit)
11844         {
11845           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11846           {
11847             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11848               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11849           }
11850           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11851           {
11852             game_em.lev->killed_out_of_time = TRUE;
11853           }
11854           else
11855           {
11856             for (i = 0; i < MAX_PLAYERS; i++)
11857               KillPlayer(&stored_player[i]);
11858           }
11859         }
11860       }
11861       else if (game.no_level_time_limit && !game.all_players_gone)
11862       {
11863         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11864       }
11865
11866       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11867     }
11868   }
11869
11870   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11871   {
11872     TapeTimeFrames = 0;
11873     TapeTime++;
11874
11875     if (tape.recording || tape.playing)
11876       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11877   }
11878
11879   if (tape.recording || tape.playing)
11880     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11881
11882   UpdateAndDisplayGameControlValues();
11883 }
11884
11885 void AdvanceFrameAndPlayerCounters(int player_nr)
11886 {
11887   int i;
11888
11889   // handle game and tape time differently for native BD game engine
11890
11891   // tape time is running in native BD engine even if player is not hatched yet
11892   if (!checkGameRunning())
11893     return;
11894
11895   // advance frame counters (global frame counter and tape time frame counter)
11896   FrameCounter++;
11897   TapeTimeFrames++;
11898
11899   // level time is running in native BD engine after player is being hatched
11900   if (!checkGamePlaying())
11901     return;
11902
11903   // advance time frame counter (used to control available time to solve level)
11904   TimeFrames++;
11905
11906   // advance player counters (counters for move delay, move animation etc.)
11907   for (i = 0; i < MAX_PLAYERS; i++)
11908   {
11909     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11910     int move_delay_value = stored_player[i].move_delay_value;
11911     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11912
11913     if (!advance_player_counters)       // not all players may be affected
11914       continue;
11915
11916     if (move_frames == 0)       // less than one move per game frame
11917     {
11918       int stepsize = TILEX / move_delay_value;
11919       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11920       int count = (stored_player[i].is_moving ?
11921                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11922
11923       if (count % delay == 0)
11924         move_frames = 1;
11925     }
11926
11927     stored_player[i].Frame += move_frames;
11928
11929     if (stored_player[i].MovPos != 0)
11930       stored_player[i].StepFrame += move_frames;
11931
11932     if (stored_player[i].move_delay > 0)
11933       stored_player[i].move_delay--;
11934
11935     // due to bugs in previous versions, counter must count up, not down
11936     if (stored_player[i].push_delay != -1)
11937       stored_player[i].push_delay++;
11938
11939     if (stored_player[i].drop_delay > 0)
11940       stored_player[i].drop_delay--;
11941
11942     if (stored_player[i].is_dropping_pressed)
11943       stored_player[i].drop_pressed_delay++;
11944   }
11945 }
11946
11947 void AdvanceFrameCounter(void)
11948 {
11949   FrameCounter++;
11950 }
11951
11952 void AdvanceGfxFrame(void)
11953 {
11954   int x, y;
11955
11956   SCAN_PLAYFIELD(x, y)
11957   {
11958     GfxFrame[x][y]++;
11959   }
11960 }
11961
11962 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11963                               struct MouseActionInfo *mouse_action_last)
11964 {
11965   if (mouse_action->button)
11966   {
11967     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11968     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11969     int x = mouse_action->lx;
11970     int y = mouse_action->ly;
11971     int element = Tile[x][y];
11972
11973     if (new_button)
11974     {
11975       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11976       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11977                                          ch_button);
11978     }
11979
11980     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11981     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11982                                        ch_button);
11983
11984     if (level.use_step_counter)
11985     {
11986       boolean counted_click = FALSE;
11987
11988       // element clicked that can change when clicked/pressed
11989       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11990           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11991            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11992         counted_click = TRUE;
11993
11994       // element clicked that can trigger change when clicked/pressed
11995       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11996           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11997         counted_click = TRUE;
11998
11999       if (new_button && counted_click)
12000         CheckLevelTime_StepCounter();
12001     }
12002   }
12003 }
12004
12005 void StartGameActions(boolean init_network_game, boolean record_tape,
12006                       int random_seed)
12007 {
12008   unsigned int new_random_seed = InitRND(random_seed);
12009
12010   if (record_tape)
12011     TapeStartRecording(new_random_seed);
12012
12013   if (setup.auto_pause_on_start && !tape.pausing)
12014     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12015
12016   if (init_network_game)
12017   {
12018     SendToServer_LevelFile();
12019     SendToServer_StartPlaying();
12020
12021     return;
12022   }
12023
12024   InitGame();
12025 }
12026
12027 static void GameActionsExt(void)
12028 {
12029 #if 0
12030   static unsigned int game_frame_delay = 0;
12031 #endif
12032   unsigned int game_frame_delay_value;
12033   byte *recorded_player_action;
12034   byte summarized_player_action = 0;
12035   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12036   int i;
12037
12038   // detect endless loops, caused by custom element programming
12039   if (recursion_loop_detected && recursion_loop_depth == 0)
12040   {
12041     char *message = getStringCat3("Internal Error! Element ",
12042                                   EL_NAME(recursion_loop_element),
12043                                   " caused endless loop! Quit the game?");
12044
12045     Warn("element '%s' caused endless loop in game engine",
12046          EL_NAME(recursion_loop_element));
12047
12048     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12049
12050     recursion_loop_detected = FALSE;    // if game should be continued
12051
12052     free(message);
12053
12054     return;
12055   }
12056
12057   if (game.restart_level)
12058     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12059
12060   CheckLevelSolved();
12061
12062   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12063     GameWon();
12064
12065   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12066     TapeStop();
12067
12068   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12069     return;
12070
12071   game_frame_delay_value =
12072     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12073
12074   if (tape.playing && tape.warp_forward && !tape.pausing)
12075     game_frame_delay_value = 0;
12076
12077   SetVideoFrameDelay(game_frame_delay_value);
12078
12079   // (de)activate virtual buttons depending on current game status
12080   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12081   {
12082     if (game.all_players_gone)  // if no players there to be controlled anymore
12083       SetOverlayActive(FALSE);
12084     else if (!tape.playing)     // if game continues after tape stopped playing
12085       SetOverlayActive(TRUE);
12086   }
12087
12088 #if 0
12089 #if 0
12090   // ---------- main game synchronization point ----------
12091
12092   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12093
12094   Debug("game:playing:skip", "skip == %d", skip);
12095
12096 #else
12097   // ---------- main game synchronization point ----------
12098
12099   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12100 #endif
12101 #endif
12102
12103   if (network_playing && !network_player_action_received)
12104   {
12105     // try to get network player actions in time
12106
12107     // last chance to get network player actions without main loop delay
12108     HandleNetworking();
12109
12110     // game was quit by network peer
12111     if (game_status != GAME_MODE_PLAYING)
12112       return;
12113
12114     // check if network player actions still missing and game still running
12115     if (!network_player_action_received && !checkGameEnded())
12116       return;           // failed to get network player actions in time
12117
12118     // do not yet reset "network_player_action_received" (for tape.pausing)
12119   }
12120
12121   if (tape.pausing)
12122     return;
12123
12124   // at this point we know that we really continue executing the game
12125
12126   network_player_action_received = FALSE;
12127
12128   // when playing tape, read previously recorded player input from tape data
12129   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12130
12131   local_player->effective_mouse_action = local_player->mouse_action;
12132
12133   if (recorded_player_action != NULL)
12134     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12135                                  recorded_player_action);
12136
12137   // TapePlayAction() may return NULL when toggling to "pause before death"
12138   if (tape.pausing)
12139     return;
12140
12141   if (tape.set_centered_player)
12142   {
12143     game.centered_player_nr_next = tape.centered_player_nr_next;
12144     game.set_centered_player = TRUE;
12145   }
12146
12147   for (i = 0; i < MAX_PLAYERS; i++)
12148   {
12149     summarized_player_action |= stored_player[i].action;
12150
12151     if (!network_playing && (game.team_mode || tape.playing))
12152       stored_player[i].effective_action = stored_player[i].action;
12153   }
12154
12155   if (network_playing && !checkGameEnded())
12156     SendToServer_MovePlayer(summarized_player_action);
12157
12158   // summarize all actions at local players mapped input device position
12159   // (this allows using different input devices in single player mode)
12160   if (!network.enabled && !game.team_mode)
12161     stored_player[map_player_action[local_player->index_nr]].effective_action =
12162       summarized_player_action;
12163
12164   // summarize all actions at centered player in local team mode
12165   if (tape.recording &&
12166       setup.team_mode && !network.enabled &&
12167       setup.input_on_focus &&
12168       game.centered_player_nr != -1)
12169   {
12170     for (i = 0; i < MAX_PLAYERS; i++)
12171       stored_player[map_player_action[i]].effective_action =
12172         (i == game.centered_player_nr ? summarized_player_action : 0);
12173   }
12174
12175   if (recorded_player_action != NULL)
12176     for (i = 0; i < MAX_PLAYERS; i++)
12177       stored_player[i].effective_action = recorded_player_action[i];
12178
12179   for (i = 0; i < MAX_PLAYERS; i++)
12180   {
12181     tape_action[i] = stored_player[i].effective_action;
12182
12183     /* (this may happen in the RND game engine if a player was not present on
12184        the playfield on level start, but appeared later from a custom element */
12185     if (setup.team_mode &&
12186         tape.recording &&
12187         tape_action[i] &&
12188         !tape.player_participates[i])
12189       tape.player_participates[i] = TRUE;
12190   }
12191
12192   SetTapeActionFromMouseAction(tape_action,
12193                                &local_player->effective_mouse_action);
12194
12195   // only record actions from input devices, but not programmed actions
12196   if (tape.recording)
12197     TapeRecordAction(tape_action);
12198
12199   // remember if game was played (especially after tape stopped playing)
12200   if (!tape.playing && summarized_player_action && !checkGameFailed())
12201     game.GamePlayed = TRUE;
12202
12203 #if USE_NEW_PLAYER_ASSIGNMENTS
12204   // !!! also map player actions in single player mode !!!
12205   // if (game.team_mode)
12206   if (1)
12207   {
12208     byte mapped_action[MAX_PLAYERS];
12209
12210 #if DEBUG_PLAYER_ACTIONS
12211     for (i = 0; i < MAX_PLAYERS; i++)
12212       DebugContinued("", "%d, ", stored_player[i].effective_action);
12213 #endif
12214
12215     for (i = 0; i < MAX_PLAYERS; i++)
12216       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12217
12218     for (i = 0; i < MAX_PLAYERS; i++)
12219       stored_player[i].effective_action = mapped_action[i];
12220
12221 #if DEBUG_PLAYER_ACTIONS
12222     DebugContinued("", "=> ");
12223     for (i = 0; i < MAX_PLAYERS; i++)
12224       DebugContinued("", "%d, ", stored_player[i].effective_action);
12225     DebugContinued("game:playing:player", "\n");
12226 #endif
12227   }
12228 #if DEBUG_PLAYER_ACTIONS
12229   else
12230   {
12231     for (i = 0; i < MAX_PLAYERS; i++)
12232       DebugContinued("", "%d, ", stored_player[i].effective_action);
12233     DebugContinued("game:playing:player", "\n");
12234   }
12235 #endif
12236 #endif
12237
12238   for (i = 0; i < MAX_PLAYERS; i++)
12239   {
12240     // allow engine snapshot in case of changed movement attempt
12241     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12242         (stored_player[i].effective_action & KEY_MOTION))
12243       game.snapshot.changed_action = TRUE;
12244
12245     // allow engine snapshot in case of snapping/dropping attempt
12246     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12247         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12248       game.snapshot.changed_action = TRUE;
12249
12250     game.snapshot.last_action[i] = stored_player[i].effective_action;
12251   }
12252
12253   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12254   {
12255     GameActions_BD_Main();
12256   }
12257   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12258   {
12259     GameActions_EM_Main();
12260   }
12261   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12262   {
12263     GameActions_SP_Main();
12264   }
12265   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12266   {
12267     GameActions_MM_Main();
12268   }
12269   else
12270   {
12271     GameActions_RND_Main();
12272   }
12273
12274   BlitScreenToBitmap(backbuffer);
12275
12276   CheckLevelSolved();
12277   CheckLevelTime();
12278
12279   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12280
12281   if (global.show_frames_per_second)
12282   {
12283     static unsigned int fps_counter = 0;
12284     static int fps_frames = 0;
12285     unsigned int fps_delay_ms = Counter() - fps_counter;
12286
12287     fps_frames++;
12288
12289     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12290     {
12291       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12292
12293       fps_frames = 0;
12294       fps_counter = Counter();
12295
12296       // always draw FPS to screen after FPS value was updated
12297       redraw_mask |= REDRAW_FPS;
12298     }
12299
12300     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12301     if (GetDrawDeactivationMask() == REDRAW_NONE)
12302       redraw_mask |= REDRAW_FPS;
12303   }
12304 }
12305
12306 static void GameActions_CheckSaveEngineSnapshot(void)
12307 {
12308   if (!game.snapshot.save_snapshot)
12309     return;
12310
12311   // clear flag for saving snapshot _before_ saving snapshot
12312   game.snapshot.save_snapshot = FALSE;
12313
12314   SaveEngineSnapshotToList();
12315 }
12316
12317 void GameActions(void)
12318 {
12319   GameActionsExt();
12320
12321   GameActions_CheckSaveEngineSnapshot();
12322 }
12323
12324 void GameActions_BD_Main(void)
12325 {
12326   byte effective_action[MAX_PLAYERS];
12327   int i;
12328
12329   for (i = 0; i < MAX_PLAYERS; i++)
12330     effective_action[i] = stored_player[i].effective_action;
12331
12332   GameActions_BD(effective_action);
12333 }
12334
12335 void GameActions_EM_Main(void)
12336 {
12337   byte effective_action[MAX_PLAYERS];
12338   int i;
12339
12340   for (i = 0; i < MAX_PLAYERS; i++)
12341     effective_action[i] = stored_player[i].effective_action;
12342
12343   GameActions_EM(effective_action);
12344 }
12345
12346 void GameActions_SP_Main(void)
12347 {
12348   byte effective_action[MAX_PLAYERS];
12349   int i;
12350
12351   for (i = 0; i < MAX_PLAYERS; i++)
12352     effective_action[i] = stored_player[i].effective_action;
12353
12354   GameActions_SP(effective_action);
12355
12356   for (i = 0; i < MAX_PLAYERS; i++)
12357   {
12358     if (stored_player[i].force_dropping)
12359       stored_player[i].action |= KEY_BUTTON_DROP;
12360
12361     stored_player[i].force_dropping = FALSE;
12362   }
12363 }
12364
12365 void GameActions_MM_Main(void)
12366 {
12367   AdvanceGfxFrame();
12368
12369   GameActions_MM(local_player->effective_mouse_action);
12370 }
12371
12372 void GameActions_RND_Main(void)
12373 {
12374   GameActions_RND();
12375 }
12376
12377 void GameActions_RND(void)
12378 {
12379   static struct MouseActionInfo mouse_action_last = { 0 };
12380   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12381   int magic_wall_x = 0, magic_wall_y = 0;
12382   int i, x, y, element, graphic, last_gfx_frame;
12383
12384   InitPlayfieldScanModeVars();
12385
12386   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12387   {
12388     SCAN_PLAYFIELD(x, y)
12389     {
12390       ChangeCount[x][y] = 0;
12391       ChangeEvent[x][y] = -1;
12392     }
12393   }
12394
12395   if (game.set_centered_player)
12396   {
12397     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12398
12399     // switching to "all players" only possible if all players fit to screen
12400     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12401     {
12402       game.centered_player_nr_next = game.centered_player_nr;
12403       game.set_centered_player = FALSE;
12404     }
12405
12406     // do not switch focus to non-existing (or non-active) player
12407     if (game.centered_player_nr_next >= 0 &&
12408         !stored_player[game.centered_player_nr_next].active)
12409     {
12410       game.centered_player_nr_next = game.centered_player_nr;
12411       game.set_centered_player = FALSE;
12412     }
12413   }
12414
12415   if (game.set_centered_player &&
12416       ScreenMovPos == 0)        // screen currently aligned at tile position
12417   {
12418     int sx, sy;
12419
12420     if (game.centered_player_nr_next == -1)
12421     {
12422       setScreenCenteredToAllPlayers(&sx, &sy);
12423     }
12424     else
12425     {
12426       sx = stored_player[game.centered_player_nr_next].jx;
12427       sy = stored_player[game.centered_player_nr_next].jy;
12428     }
12429
12430     game.centered_player_nr = game.centered_player_nr_next;
12431     game.set_centered_player = FALSE;
12432
12433     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12434     DrawGameDoorValues();
12435   }
12436
12437   // check single step mode (set flag and clear again if any player is active)
12438   game.enter_single_step_mode =
12439     (tape.single_step && tape.recording && !tape.pausing);
12440
12441   for (i = 0; i < MAX_PLAYERS; i++)
12442   {
12443     int actual_player_action = stored_player[i].effective_action;
12444
12445 #if 1
12446     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12447        - rnd_equinox_tetrachloride 048
12448        - rnd_equinox_tetrachloride_ii 096
12449        - rnd_emanuel_schmieg 002
12450        - doctor_sloan_ww 001, 020
12451     */
12452     if (stored_player[i].MovPos == 0)
12453       CheckGravityMovement(&stored_player[i]);
12454 #endif
12455
12456     // overwrite programmed action with tape action
12457     if (stored_player[i].programmed_action)
12458       actual_player_action = stored_player[i].programmed_action;
12459
12460     PlayerActions(&stored_player[i], actual_player_action);
12461
12462     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12463   }
12464
12465   // single step pause mode may already have been toggled by "ScrollPlayer()"
12466   if (game.enter_single_step_mode && !tape.pausing)
12467     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12468
12469   ScrollScreen(NULL, SCROLL_GO_ON);
12470
12471   /* for backwards compatibility, the following code emulates a fixed bug that
12472      occured when pushing elements (causing elements that just made their last
12473      pushing step to already (if possible) make their first falling step in the
12474      same game frame, which is bad); this code is also needed to use the famous
12475      "spring push bug" which is used in older levels and might be wanted to be
12476      used also in newer levels, but in this case the buggy pushing code is only
12477      affecting the "spring" element and no other elements */
12478
12479   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12480   {
12481     for (i = 0; i < MAX_PLAYERS; i++)
12482     {
12483       struct PlayerInfo *player = &stored_player[i];
12484       int x = player->jx;
12485       int y = player->jy;
12486
12487       if (player->active && player->is_pushing && player->is_moving &&
12488           IS_MOVING(x, y) &&
12489           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12490            Tile[x][y] == EL_SPRING))
12491       {
12492         ContinueMoving(x, y);
12493
12494         // continue moving after pushing (this is actually a bug)
12495         if (!IS_MOVING(x, y))
12496           Stop[x][y] = FALSE;
12497       }
12498     }
12499   }
12500
12501   SCAN_PLAYFIELD(x, y)
12502   {
12503     Last[x][y] = Tile[x][y];
12504
12505     ChangeCount[x][y] = 0;
12506     ChangeEvent[x][y] = -1;
12507
12508     // this must be handled before main playfield loop
12509     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12510     {
12511       MovDelay[x][y]--;
12512       if (MovDelay[x][y] <= 0)
12513         RemoveField(x, y);
12514     }
12515
12516     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12517     {
12518       MovDelay[x][y]--;
12519       if (MovDelay[x][y] <= 0)
12520       {
12521         int element = Store[x][y];
12522         int move_direction = MovDir[x][y];
12523         int player_index_bit = Store2[x][y];
12524
12525         Store[x][y] = 0;
12526         Store2[x][y] = 0;
12527
12528         RemoveField(x, y);
12529         TEST_DrawLevelField(x, y);
12530
12531         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12532
12533         if (IS_ENVELOPE(element))
12534           local_player->show_envelope = element;
12535       }
12536     }
12537
12538 #if DEBUG
12539     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12540     {
12541       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12542             x, y);
12543       Debug("game:playing:GameActions_RND", "This should never happen!");
12544
12545       ChangePage[x][y] = -1;
12546     }
12547 #endif
12548
12549     Stop[x][y] = FALSE;
12550     if (WasJustMoving[x][y] > 0)
12551       WasJustMoving[x][y]--;
12552     if (WasJustFalling[x][y] > 0)
12553       WasJustFalling[x][y]--;
12554     if (CheckCollision[x][y] > 0)
12555       CheckCollision[x][y]--;
12556     if (CheckImpact[x][y] > 0)
12557       CheckImpact[x][y]--;
12558
12559     GfxFrame[x][y]++;
12560
12561     /* reset finished pushing action (not done in ContinueMoving() to allow
12562        continuous pushing animation for elements with zero push delay) */
12563     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12564     {
12565       ResetGfxAnimation(x, y);
12566       TEST_DrawLevelField(x, y);
12567     }
12568
12569 #if DEBUG
12570     if (IS_BLOCKED(x, y))
12571     {
12572       int oldx, oldy;
12573
12574       Blocked2Moving(x, y, &oldx, &oldy);
12575       if (!IS_MOVING(oldx, oldy))
12576       {
12577         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12578         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12579         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12580         Debug("game:playing:GameActions_RND", "This should never happen!");
12581       }
12582     }
12583 #endif
12584   }
12585
12586   HandleMouseAction(&mouse_action, &mouse_action_last);
12587
12588   SCAN_PLAYFIELD(x, y)
12589   {
12590     element = Tile[x][y];
12591     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12592     last_gfx_frame = GfxFrame[x][y];
12593
12594     if (element == EL_EMPTY)
12595       graphic = el2img(GfxElementEmpty[x][y]);
12596
12597     ResetGfxFrame(x, y);
12598
12599     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12600       DrawLevelGraphicAnimation(x, y, graphic);
12601
12602     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12603         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12604       ResetRandomAnimationValue(x, y);
12605
12606     SetRandomAnimationValue(x, y);
12607
12608     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12609
12610     if (IS_INACTIVE(element))
12611     {
12612       if (IS_ANIMATED(graphic))
12613         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12614
12615       continue;
12616     }
12617
12618     // this may take place after moving, so 'element' may have changed
12619     if (IS_CHANGING(x, y) &&
12620         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12621     {
12622       int page = element_info[element].event_page_nr[CE_DELAY];
12623
12624       HandleElementChange(x, y, page);
12625
12626       element = Tile[x][y];
12627       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12628     }
12629
12630     CheckNextToConditions(x, y);
12631
12632     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12633     {
12634       StartMoving(x, y);
12635
12636       element = Tile[x][y];
12637       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12638
12639       if (IS_ANIMATED(graphic) &&
12640           !IS_MOVING(x, y) &&
12641           !Stop[x][y])
12642         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12643
12644       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12645         TEST_DrawTwinkleOnField(x, y);
12646     }
12647     else if (element == EL_ACID)
12648     {
12649       if (!Stop[x][y])
12650         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12651     }
12652     else if ((element == EL_EXIT_OPEN ||
12653               element == EL_EM_EXIT_OPEN ||
12654               element == EL_SP_EXIT_OPEN ||
12655               element == EL_STEEL_EXIT_OPEN ||
12656               element == EL_EM_STEEL_EXIT_OPEN ||
12657               element == EL_SP_TERMINAL ||
12658               element == EL_SP_TERMINAL_ACTIVE ||
12659               element == EL_EXTRA_TIME ||
12660               element == EL_SHIELD_NORMAL ||
12661               element == EL_SHIELD_DEADLY) &&
12662              IS_ANIMATED(graphic))
12663       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12664     else if (IS_MOVING(x, y))
12665       ContinueMoving(x, y);
12666     else if (IS_ACTIVE_BOMB(element))
12667       CheckDynamite(x, y);
12668     else if (element == EL_AMOEBA_GROWING)
12669       AmoebaGrowing(x, y);
12670     else if (element == EL_AMOEBA_SHRINKING)
12671       AmoebaShrinking(x, y);
12672
12673 #if !USE_NEW_AMOEBA_CODE
12674     else if (IS_AMOEBALIVE(element))
12675       AmoebaReproduce(x, y);
12676 #endif
12677
12678     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12679       Life(x, y);
12680     else if (element == EL_EXIT_CLOSED)
12681       CheckExit(x, y);
12682     else if (element == EL_EM_EXIT_CLOSED)
12683       CheckExitEM(x, y);
12684     else if (element == EL_STEEL_EXIT_CLOSED)
12685       CheckExitSteel(x, y);
12686     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12687       CheckExitSteelEM(x, y);
12688     else if (element == EL_SP_EXIT_CLOSED)
12689       CheckExitSP(x, y);
12690     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12691              element == EL_EXPANDABLE_STEELWALL_GROWING)
12692       WallGrowing(x, y);
12693     else if (element == EL_EXPANDABLE_WALL ||
12694              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12695              element == EL_EXPANDABLE_WALL_VERTICAL ||
12696              element == EL_EXPANDABLE_WALL_ANY ||
12697              element == EL_BD_EXPANDABLE_WALL ||
12698              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12699              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12700              element == EL_EXPANDABLE_STEELWALL_ANY)
12701       CheckWallGrowing(x, y);
12702     else if (element == EL_FLAMES)
12703       CheckForDragon(x, y);
12704     else if (element == EL_EXPLOSION)
12705       ; // drawing of correct explosion animation is handled separately
12706     else if (element == EL_ELEMENT_SNAPPING ||
12707              element == EL_DIAGONAL_SHRINKING ||
12708              element == EL_DIAGONAL_GROWING)
12709     {
12710       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12711
12712       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12713     }
12714     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12715       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12716
12717     if (IS_BELT_ACTIVE(element))
12718       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12719
12720     if (game.magic_wall_active)
12721     {
12722       int jx = local_player->jx, jy = local_player->jy;
12723
12724       // play the element sound at the position nearest to the player
12725       if ((element == EL_MAGIC_WALL_FULL ||
12726            element == EL_MAGIC_WALL_ACTIVE ||
12727            element == EL_MAGIC_WALL_EMPTYING ||
12728            element == EL_BD_MAGIC_WALL_FULL ||
12729            element == EL_BD_MAGIC_WALL_ACTIVE ||
12730            element == EL_BD_MAGIC_WALL_EMPTYING ||
12731            element == EL_DC_MAGIC_WALL_FULL ||
12732            element == EL_DC_MAGIC_WALL_ACTIVE ||
12733            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12734           ABS(x - jx) + ABS(y - jy) <
12735           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12736       {
12737         magic_wall_x = x;
12738         magic_wall_y = y;
12739       }
12740     }
12741   }
12742
12743 #if USE_NEW_AMOEBA_CODE
12744   // new experimental amoeba growth stuff
12745   if (!(FrameCounter % 8))
12746   {
12747     static unsigned int random = 1684108901;
12748
12749     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12750     {
12751       x = RND(lev_fieldx);
12752       y = RND(lev_fieldy);
12753       element = Tile[x][y];
12754
12755       if (!IS_PLAYER(x, y) &&
12756           (element == EL_EMPTY ||
12757            CAN_GROW_INTO(element) ||
12758            element == EL_QUICKSAND_EMPTY ||
12759            element == EL_QUICKSAND_FAST_EMPTY ||
12760            element == EL_ACID_SPLASH_LEFT ||
12761            element == EL_ACID_SPLASH_RIGHT))
12762       {
12763         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12764             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12765             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12766             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12767           Tile[x][y] = EL_AMOEBA_DROP;
12768       }
12769
12770       random = random * 129 + 1;
12771     }
12772   }
12773 #endif
12774
12775   game.explosions_delayed = FALSE;
12776
12777   SCAN_PLAYFIELD(x, y)
12778   {
12779     element = Tile[x][y];
12780
12781     if (ExplodeField[x][y])
12782       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12783     else if (element == EL_EXPLOSION)
12784       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12785
12786     ExplodeField[x][y] = EX_TYPE_NONE;
12787   }
12788
12789   game.explosions_delayed = TRUE;
12790
12791   if (game.magic_wall_active)
12792   {
12793     if (!(game.magic_wall_time_left % 4))
12794     {
12795       int element = Tile[magic_wall_x][magic_wall_y];
12796
12797       if (element == EL_BD_MAGIC_WALL_FULL ||
12798           element == EL_BD_MAGIC_WALL_ACTIVE ||
12799           element == EL_BD_MAGIC_WALL_EMPTYING)
12800         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12801       else if (element == EL_DC_MAGIC_WALL_FULL ||
12802                element == EL_DC_MAGIC_WALL_ACTIVE ||
12803                element == EL_DC_MAGIC_WALL_EMPTYING)
12804         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12805       else
12806         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12807     }
12808
12809     if (game.magic_wall_time_left > 0)
12810     {
12811       game.magic_wall_time_left--;
12812
12813       if (!game.magic_wall_time_left)
12814       {
12815         SCAN_PLAYFIELD(x, y)
12816         {
12817           element = Tile[x][y];
12818
12819           if (element == EL_MAGIC_WALL_ACTIVE ||
12820               element == EL_MAGIC_WALL_FULL)
12821           {
12822             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12823             TEST_DrawLevelField(x, y);
12824           }
12825           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12826                    element == EL_BD_MAGIC_WALL_FULL)
12827           {
12828             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12829             TEST_DrawLevelField(x, y);
12830           }
12831           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12832                    element == EL_DC_MAGIC_WALL_FULL)
12833           {
12834             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12835             TEST_DrawLevelField(x, y);
12836           }
12837         }
12838
12839         game.magic_wall_active = FALSE;
12840       }
12841     }
12842   }
12843
12844   if (game.light_time_left > 0)
12845   {
12846     game.light_time_left--;
12847
12848     if (game.light_time_left == 0)
12849       RedrawAllLightSwitchesAndInvisibleElements();
12850   }
12851
12852   if (game.timegate_time_left > 0)
12853   {
12854     game.timegate_time_left--;
12855
12856     if (game.timegate_time_left == 0)
12857       CloseAllOpenTimegates();
12858   }
12859
12860   if (game.lenses_time_left > 0)
12861   {
12862     game.lenses_time_left--;
12863
12864     if (game.lenses_time_left == 0)
12865       RedrawAllInvisibleElementsForLenses();
12866   }
12867
12868   if (game.magnify_time_left > 0)
12869   {
12870     game.magnify_time_left--;
12871
12872     if (game.magnify_time_left == 0)
12873       RedrawAllInvisibleElementsForMagnifier();
12874   }
12875
12876   for (i = 0; i < MAX_PLAYERS; i++)
12877   {
12878     struct PlayerInfo *player = &stored_player[i];
12879
12880     if (SHIELD_ON(player))
12881     {
12882       if (player->shield_deadly_time_left)
12883         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12884       else if (player->shield_normal_time_left)
12885         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12886     }
12887   }
12888
12889 #if USE_DELAYED_GFX_REDRAW
12890   SCAN_PLAYFIELD(x, y)
12891   {
12892     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12893     {
12894       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12895          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12896
12897       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12898         DrawLevelField(x, y);
12899
12900       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12901         DrawLevelFieldCrumbled(x, y);
12902
12903       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12904         DrawLevelFieldCrumbledNeighbours(x, y);
12905
12906       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12907         DrawTwinkleOnField(x, y);
12908     }
12909
12910     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12911   }
12912 #endif
12913
12914   DrawAllPlayers();
12915   PlayAllPlayersSound();
12916
12917   for (i = 0; i < MAX_PLAYERS; i++)
12918   {
12919     struct PlayerInfo *player = &stored_player[i];
12920
12921     if (player->show_envelope != 0 && (!player->active ||
12922                                        player->MovPos == 0))
12923     {
12924       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12925
12926       player->show_envelope = 0;
12927     }
12928   }
12929
12930   // use random number generator in every frame to make it less predictable
12931   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12932     RND(1);
12933
12934   mouse_action_last = mouse_action;
12935 }
12936
12937 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12938 {
12939   int min_x = x, min_y = y, max_x = x, max_y = y;
12940   int scr_fieldx = getScreenFieldSizeX();
12941   int scr_fieldy = getScreenFieldSizeY();
12942   int i;
12943
12944   for (i = 0; i < MAX_PLAYERS; i++)
12945   {
12946     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12947
12948     if (!stored_player[i].active || &stored_player[i] == player)
12949       continue;
12950
12951     min_x = MIN(min_x, jx);
12952     min_y = MIN(min_y, jy);
12953     max_x = MAX(max_x, jx);
12954     max_y = MAX(max_y, jy);
12955   }
12956
12957   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12958 }
12959
12960 static boolean AllPlayersInVisibleScreen(void)
12961 {
12962   int i;
12963
12964   for (i = 0; i < MAX_PLAYERS; i++)
12965   {
12966     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12967
12968     if (!stored_player[i].active)
12969       continue;
12970
12971     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12972       return FALSE;
12973   }
12974
12975   return TRUE;
12976 }
12977
12978 void ScrollLevel(int dx, int dy)
12979 {
12980   int scroll_offset = 2 * TILEX_VAR;
12981   int x, y;
12982
12983   BlitBitmap(drawto_field, drawto_field,
12984              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12985              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12986              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12987              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12988              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12989              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12990
12991   if (dx != 0)
12992   {
12993     x = (dx == 1 ? BX1 : BX2);
12994     for (y = BY1; y <= BY2; y++)
12995       DrawScreenField(x, y);
12996   }
12997
12998   if (dy != 0)
12999   {
13000     y = (dy == 1 ? BY1 : BY2);
13001     for (x = BX1; x <= BX2; x++)
13002       DrawScreenField(x, y);
13003   }
13004
13005   redraw_mask |= REDRAW_FIELD;
13006 }
13007
13008 static boolean canFallDown(struct PlayerInfo *player)
13009 {
13010   int jx = player->jx, jy = player->jy;
13011
13012   return (IN_LEV_FIELD(jx, jy + 1) &&
13013           (IS_FREE(jx, jy + 1) ||
13014            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13015           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13016           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13017 }
13018
13019 static boolean canPassField(int x, int y, int move_dir)
13020 {
13021   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13022   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13023   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13024   int nextx = x + dx;
13025   int nexty = y + dy;
13026   int element = Tile[x][y];
13027
13028   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13029           !CAN_MOVE(element) &&
13030           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13031           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13032           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13033 }
13034
13035 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13036 {
13037   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13038   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13039   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13040   int newx = x + dx;
13041   int newy = y + dy;
13042
13043   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13044           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13045           (IS_DIGGABLE(Tile[newx][newy]) ||
13046            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13047            canPassField(newx, newy, move_dir)));
13048 }
13049
13050 static void CheckGravityMovement(struct PlayerInfo *player)
13051 {
13052   if (player->gravity && !player->programmed_action)
13053   {
13054     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13055     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13056     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13057     int jx = player->jx, jy = player->jy;
13058     boolean player_is_moving_to_valid_field =
13059       (!player_is_snapping &&
13060        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13061         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13062     boolean player_can_fall_down = canFallDown(player);
13063
13064     if (player_can_fall_down &&
13065         !player_is_moving_to_valid_field)
13066       player->programmed_action = MV_DOWN;
13067   }
13068 }
13069
13070 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13071 {
13072   return CheckGravityMovement(player);
13073
13074   if (player->gravity && !player->programmed_action)
13075   {
13076     int jx = player->jx, jy = player->jy;
13077     boolean field_under_player_is_free =
13078       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13079     boolean player_is_standing_on_valid_field =
13080       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13081        (IS_WALKABLE(Tile[jx][jy]) &&
13082         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13083
13084     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13085       player->programmed_action = MV_DOWN;
13086   }
13087 }
13088
13089 /*
13090   MovePlayerOneStep()
13091   -----------------------------------------------------------------------------
13092   dx, dy:               direction (non-diagonal) to try to move the player to
13093   real_dx, real_dy:     direction as read from input device (can be diagonal)
13094 */
13095
13096 boolean MovePlayerOneStep(struct PlayerInfo *player,
13097                           int dx, int dy, int real_dx, int real_dy)
13098 {
13099   int jx = player->jx, jy = player->jy;
13100   int new_jx = jx + dx, new_jy = jy + dy;
13101   int can_move;
13102   boolean player_can_move = !player->cannot_move;
13103
13104   if (!player->active || (!dx && !dy))
13105     return MP_NO_ACTION;
13106
13107   player->MovDir = (dx < 0 ? MV_LEFT :
13108                     dx > 0 ? MV_RIGHT :
13109                     dy < 0 ? MV_UP :
13110                     dy > 0 ? MV_DOWN :  MV_NONE);
13111
13112   if (!IN_LEV_FIELD(new_jx, new_jy))
13113     return MP_NO_ACTION;
13114
13115   if (!player_can_move)
13116   {
13117     if (player->MovPos == 0)
13118     {
13119       player->is_moving = FALSE;
13120       player->is_digging = FALSE;
13121       player->is_collecting = FALSE;
13122       player->is_snapping = FALSE;
13123       player->is_pushing = FALSE;
13124     }
13125   }
13126
13127   if (!network.enabled && game.centered_player_nr == -1 &&
13128       !AllPlayersInSight(player, new_jx, new_jy))
13129     return MP_NO_ACTION;
13130
13131   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13132   if (can_move != MP_MOVING)
13133     return can_move;
13134
13135   // check if DigField() has caused relocation of the player
13136   if (player->jx != jx || player->jy != jy)
13137     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13138
13139   StorePlayer[jx][jy] = 0;
13140   player->last_jx = jx;
13141   player->last_jy = jy;
13142   player->jx = new_jx;
13143   player->jy = new_jy;
13144   StorePlayer[new_jx][new_jy] = player->element_nr;
13145
13146   if (player->move_delay_value_next != -1)
13147   {
13148     player->move_delay_value = player->move_delay_value_next;
13149     player->move_delay_value_next = -1;
13150   }
13151
13152   player->MovPos =
13153     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13154
13155   player->step_counter++;
13156
13157   PlayerVisit[jx][jy] = FrameCounter;
13158
13159   player->is_moving = TRUE;
13160
13161 #if 1
13162   // should better be called in MovePlayer(), but this breaks some tapes
13163   ScrollPlayer(player, SCROLL_INIT);
13164 #endif
13165
13166   return MP_MOVING;
13167 }
13168
13169 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13170 {
13171   int jx = player->jx, jy = player->jy;
13172   int old_jx = jx, old_jy = jy;
13173   int moved = MP_NO_ACTION;
13174
13175   if (!player->active)
13176     return FALSE;
13177
13178   if (!dx && !dy)
13179   {
13180     if (player->MovPos == 0)
13181     {
13182       player->is_moving = FALSE;
13183       player->is_digging = FALSE;
13184       player->is_collecting = FALSE;
13185       player->is_snapping = FALSE;
13186       player->is_pushing = FALSE;
13187     }
13188
13189     return FALSE;
13190   }
13191
13192   if (player->move_delay > 0)
13193     return FALSE;
13194
13195   player->move_delay = -1;              // set to "uninitialized" value
13196
13197   // store if player is automatically moved to next field
13198   player->is_auto_moving = (player->programmed_action != MV_NONE);
13199
13200   // remove the last programmed player action
13201   player->programmed_action = 0;
13202
13203   if (player->MovPos)
13204   {
13205     // should only happen if pre-1.2 tape recordings are played
13206     // this is only for backward compatibility
13207
13208     int original_move_delay_value = player->move_delay_value;
13209
13210 #if DEBUG
13211     Debug("game:playing:MovePlayer",
13212           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13213           tape.counter);
13214 #endif
13215
13216     // scroll remaining steps with finest movement resolution
13217     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13218
13219     while (player->MovPos)
13220     {
13221       ScrollPlayer(player, SCROLL_GO_ON);
13222       ScrollScreen(NULL, SCROLL_GO_ON);
13223
13224       AdvanceFrameAndPlayerCounters(player->index_nr);
13225
13226       DrawAllPlayers();
13227       BackToFront_WithFrameDelay(0);
13228     }
13229
13230     player->move_delay_value = original_move_delay_value;
13231   }
13232
13233   player->is_active = FALSE;
13234
13235   if (player->last_move_dir & MV_HORIZONTAL)
13236   {
13237     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13238       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13239   }
13240   else
13241   {
13242     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13243       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13244   }
13245
13246   if (!moved && !player->is_active)
13247   {
13248     player->is_moving = FALSE;
13249     player->is_digging = FALSE;
13250     player->is_collecting = FALSE;
13251     player->is_snapping = FALSE;
13252     player->is_pushing = FALSE;
13253   }
13254
13255   jx = player->jx;
13256   jy = player->jy;
13257
13258   if (moved & MP_MOVING && !ScreenMovPos &&
13259       (player->index_nr == game.centered_player_nr ||
13260        game.centered_player_nr == -1))
13261   {
13262     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13263
13264     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13265     {
13266       // actual player has left the screen -- scroll in that direction
13267       if (jx != old_jx)         // player has moved horizontally
13268         scroll_x += (jx - old_jx);
13269       else                      // player has moved vertically
13270         scroll_y += (jy - old_jy);
13271     }
13272     else
13273     {
13274       int offset_raw = game.scroll_delay_value;
13275
13276       if (jx != old_jx)         // player has moved horizontally
13277       {
13278         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13279         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13280         int new_scroll_x = jx - MIDPOSX + offset_x;
13281
13282         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13283             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13284           scroll_x = new_scroll_x;
13285
13286         // don't scroll over playfield boundaries
13287         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13288
13289         // don't scroll more than one field at a time
13290         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13291
13292         // don't scroll against the player's moving direction
13293         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13294             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13295           scroll_x = old_scroll_x;
13296       }
13297       else                      // player has moved vertically
13298       {
13299         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13300         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13301         int new_scroll_y = jy - MIDPOSY + offset_y;
13302
13303         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13304             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13305           scroll_y = new_scroll_y;
13306
13307         // don't scroll over playfield boundaries
13308         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13309
13310         // don't scroll more than one field at a time
13311         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13312
13313         // don't scroll against the player's moving direction
13314         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13315             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13316           scroll_y = old_scroll_y;
13317       }
13318     }
13319
13320     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13321     {
13322       if (!network.enabled && game.centered_player_nr == -1 &&
13323           !AllPlayersInVisibleScreen())
13324       {
13325         scroll_x = old_scroll_x;
13326         scroll_y = old_scroll_y;
13327       }
13328       else
13329       {
13330         ScrollScreen(player, SCROLL_INIT);
13331         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13332       }
13333     }
13334   }
13335
13336   player->StepFrame = 0;
13337
13338   if (moved & MP_MOVING)
13339   {
13340     if (old_jx != jx && old_jy == jy)
13341       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13342     else if (old_jx == jx && old_jy != jy)
13343       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13344
13345     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13346
13347     player->last_move_dir = player->MovDir;
13348     player->is_moving = TRUE;
13349     player->is_snapping = FALSE;
13350     player->is_switching = FALSE;
13351     player->is_dropping = FALSE;
13352     player->is_dropping_pressed = FALSE;
13353     player->drop_pressed_delay = 0;
13354
13355 #if 0
13356     // should better be called here than above, but this breaks some tapes
13357     ScrollPlayer(player, SCROLL_INIT);
13358 #endif
13359   }
13360   else
13361   {
13362     CheckGravityMovementWhenNotMoving(player);
13363
13364     player->is_moving = FALSE;
13365
13366     /* at this point, the player is allowed to move, but cannot move right now
13367        (e.g. because of something blocking the way) -- ensure that the player
13368        is also allowed to move in the next frame (in old versions before 3.1.1,
13369        the player was forced to wait again for eight frames before next try) */
13370
13371     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13372       player->move_delay = 0;   // allow direct movement in the next frame
13373   }
13374
13375   if (player->move_delay == -1)         // not yet initialized by DigField()
13376     player->move_delay = player->move_delay_value;
13377
13378   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13379   {
13380     TestIfPlayerTouchesBadThing(jx, jy);
13381     TestIfPlayerTouchesCustomElement(jx, jy);
13382   }
13383
13384   if (!player->active)
13385     RemovePlayer(player);
13386
13387   return moved;
13388 }
13389
13390 void ScrollPlayer(struct PlayerInfo *player, int mode)
13391 {
13392   int jx = player->jx, jy = player->jy;
13393   int last_jx = player->last_jx, last_jy = player->last_jy;
13394   int move_stepsize = TILEX / player->move_delay_value;
13395
13396   if (!player->active)
13397     return;
13398
13399   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13400     return;
13401
13402   if (mode == SCROLL_INIT)
13403   {
13404     player->actual_frame_counter.count = FrameCounter;
13405     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13406
13407     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13408         Tile[last_jx][last_jy] == EL_EMPTY)
13409     {
13410       int last_field_block_delay = 0;   // start with no blocking at all
13411       int block_delay_adjustment = player->block_delay_adjustment;
13412
13413       // if player blocks last field, add delay for exactly one move
13414       if (player->block_last_field)
13415       {
13416         last_field_block_delay += player->move_delay_value;
13417
13418         // when blocking enabled, prevent moving up despite gravity
13419         if (player->gravity && player->MovDir == MV_UP)
13420           block_delay_adjustment = -1;
13421       }
13422
13423       // add block delay adjustment (also possible when not blocking)
13424       last_field_block_delay += block_delay_adjustment;
13425
13426       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13427       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13428     }
13429
13430     if (player->MovPos != 0)    // player has not yet reached destination
13431       return;
13432   }
13433   else if (!FrameReached(&player->actual_frame_counter))
13434     return;
13435
13436   if (player->MovPos != 0)
13437   {
13438     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13439     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13440
13441     // before DrawPlayer() to draw correct player graphic for this case
13442     if (player->MovPos == 0)
13443       CheckGravityMovement(player);
13444   }
13445
13446   if (player->MovPos == 0)      // player reached destination field
13447   {
13448     if (player->move_delay_reset_counter > 0)
13449     {
13450       player->move_delay_reset_counter--;
13451
13452       if (player->move_delay_reset_counter == 0)
13453       {
13454         // continue with normal speed after quickly moving through gate
13455         HALVE_PLAYER_SPEED(player);
13456
13457         // be able to make the next move without delay
13458         player->move_delay = 0;
13459       }
13460     }
13461
13462     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13463         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13464         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13465         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13466         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13467         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13468         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13469         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13470     {
13471       ExitPlayer(player);
13472
13473       if (game.players_still_needed == 0 &&
13474           (game.friends_still_needed == 0 ||
13475            IS_SP_ELEMENT(Tile[jx][jy])))
13476         LevelSolved();
13477     }
13478
13479     player->last_jx = jx;
13480     player->last_jy = jy;
13481
13482     // this breaks one level: "machine", level 000
13483     {
13484       int move_direction = player->MovDir;
13485       int enter_side = MV_DIR_OPPOSITE(move_direction);
13486       int leave_side = move_direction;
13487       int old_jx = last_jx;
13488       int old_jy = last_jy;
13489       int old_element = Tile[old_jx][old_jy];
13490       int new_element = Tile[jx][jy];
13491
13492       if (IS_CUSTOM_ELEMENT(old_element))
13493         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13494                                    CE_LEFT_BY_PLAYER,
13495                                    player->index_bit, leave_side);
13496
13497       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13498                                           CE_PLAYER_LEAVES_X,
13499                                           player->index_bit, leave_side);
13500
13501       // needed because pushed element has not yet reached its destination,
13502       // so it would trigger a change event at its previous field location
13503       if (!player->is_pushing)
13504       {
13505         if (IS_CUSTOM_ELEMENT(new_element))
13506           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13507                                      player->index_bit, enter_side);
13508
13509         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13510                                             CE_PLAYER_ENTERS_X,
13511                                             player->index_bit, enter_side);
13512       }
13513
13514       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13515                                         CE_MOVE_OF_X, move_direction);
13516     }
13517
13518     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13519     {
13520       TestIfPlayerTouchesBadThing(jx, jy);
13521       TestIfPlayerTouchesCustomElement(jx, jy);
13522
13523       // needed because pushed element has not yet reached its destination,
13524       // so it would trigger a change event at its previous field location
13525       if (!player->is_pushing)
13526         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13527
13528       if (level.finish_dig_collect &&
13529           (player->is_digging || player->is_collecting))
13530       {
13531         int last_element = player->last_removed_element;
13532         int move_direction = player->MovDir;
13533         int enter_side = MV_DIR_OPPOSITE(move_direction);
13534         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13535                             CE_PLAYER_COLLECTS_X);
13536
13537         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13538                                             player->index_bit, enter_side);
13539
13540         player->last_removed_element = EL_UNDEFINED;
13541       }
13542
13543       if (!player->active)
13544         RemovePlayer(player);
13545     }
13546
13547     if (level.use_step_counter)
13548       CheckLevelTime_StepCounter();
13549
13550     if (tape.single_step && tape.recording && !tape.pausing &&
13551         !player->programmed_action)
13552       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13553
13554     if (!player->programmed_action)
13555       CheckSaveEngineSnapshot(player);
13556   }
13557 }
13558
13559 void ScrollScreen(struct PlayerInfo *player, int mode)
13560 {
13561   static DelayCounter screen_frame_counter = { 0 };
13562
13563   if (mode == SCROLL_INIT)
13564   {
13565     // set scrolling step size according to actual player's moving speed
13566     ScrollStepSize = TILEX / player->move_delay_value;
13567
13568     screen_frame_counter.count = FrameCounter;
13569     screen_frame_counter.value = 1;
13570
13571     ScreenMovDir = player->MovDir;
13572     ScreenMovPos = player->MovPos;
13573     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13574     return;
13575   }
13576   else if (!FrameReached(&screen_frame_counter))
13577     return;
13578
13579   if (ScreenMovPos)
13580   {
13581     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13582     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13583     redraw_mask |= REDRAW_FIELD;
13584   }
13585   else
13586     ScreenMovDir = MV_NONE;
13587 }
13588
13589 void CheckNextToConditions(int x, int y)
13590 {
13591   int element = Tile[x][y];
13592
13593   if (IS_PLAYER(x, y))
13594     TestIfPlayerNextToCustomElement(x, y);
13595
13596   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13597       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13598     TestIfElementNextToCustomElement(x, y);
13599 }
13600
13601 void TestIfPlayerNextToCustomElement(int x, int y)
13602 {
13603   struct XY *xy = xy_topdown;
13604   static int trigger_sides[4][2] =
13605   {
13606     // center side       border side
13607     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13608     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13609     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13610     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13611   };
13612   int i;
13613
13614   if (!IS_PLAYER(x, y))
13615     return;
13616
13617   struct PlayerInfo *player = PLAYERINFO(x, y);
13618
13619   if (player->is_moving)
13620     return;
13621
13622   for (i = 0; i < NUM_DIRECTIONS; i++)
13623   {
13624     int xx = x + xy[i].x;
13625     int yy = y + xy[i].y;
13626     int border_side = trigger_sides[i][1];
13627     int border_element;
13628
13629     if (!IN_LEV_FIELD(xx, yy))
13630       continue;
13631
13632     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13633       continue;         // center and border element not connected
13634
13635     border_element = Tile[xx][yy];
13636
13637     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13638                                player->index_bit, border_side);
13639     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13640                                         CE_PLAYER_NEXT_TO_X,
13641                                         player->index_bit, border_side);
13642
13643     /* use player element that is initially defined in the level playfield,
13644        not the player element that corresponds to the runtime player number
13645        (example: a level that contains EL_PLAYER_3 as the only player would
13646        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13647
13648     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13649                              CE_NEXT_TO_X, border_side);
13650   }
13651 }
13652
13653 void TestIfPlayerTouchesCustomElement(int x, int y)
13654 {
13655   struct XY *xy = xy_topdown;
13656   static int trigger_sides[4][2] =
13657   {
13658     // center side       border side
13659     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13660     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13661     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13662     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13663   };
13664   static int touch_dir[4] =
13665   {
13666     MV_LEFT | MV_RIGHT,
13667     MV_UP   | MV_DOWN,
13668     MV_UP   | MV_DOWN,
13669     MV_LEFT | MV_RIGHT
13670   };
13671   int center_element = Tile[x][y];      // should always be non-moving!
13672   int i;
13673
13674   for (i = 0; i < NUM_DIRECTIONS; i++)
13675   {
13676     int xx = x + xy[i].x;
13677     int yy = y + xy[i].y;
13678     int center_side = trigger_sides[i][0];
13679     int border_side = trigger_sides[i][1];
13680     int border_element;
13681
13682     if (!IN_LEV_FIELD(xx, yy))
13683       continue;
13684
13685     if (IS_PLAYER(x, y))                // player found at center element
13686     {
13687       struct PlayerInfo *player = PLAYERINFO(x, y);
13688
13689       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13690         border_element = Tile[xx][yy];          // may be moving!
13691       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13692         border_element = Tile[xx][yy];
13693       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13694         border_element = MovingOrBlocked2Element(xx, yy);
13695       else
13696         continue;               // center and border element do not touch
13697
13698       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13699                                  player->index_bit, border_side);
13700       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13701                                           CE_PLAYER_TOUCHES_X,
13702                                           player->index_bit, border_side);
13703
13704       {
13705         /* use player element that is initially defined in the level playfield,
13706            not the player element that corresponds to the runtime player number
13707            (example: a level that contains EL_PLAYER_3 as the only player would
13708            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13709         int player_element = PLAYERINFO(x, y)->initial_element;
13710
13711         // as element "X" is the player here, check opposite (center) side
13712         CheckElementChangeBySide(xx, yy, border_element, player_element,
13713                                  CE_TOUCHING_X, center_side);
13714       }
13715     }
13716     else if (IS_PLAYER(xx, yy))         // player found at border element
13717     {
13718       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13719
13720       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13721       {
13722         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13723           continue;             // center and border element do not touch
13724       }
13725
13726       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13727                                  player->index_bit, center_side);
13728       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13729                                           CE_PLAYER_TOUCHES_X,
13730                                           player->index_bit, center_side);
13731
13732       {
13733         /* use player element that is initially defined in the level playfield,
13734            not the player element that corresponds to the runtime player number
13735            (example: a level that contains EL_PLAYER_3 as the only player would
13736            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13737         int player_element = PLAYERINFO(xx, yy)->initial_element;
13738
13739         // as element "X" is the player here, check opposite (border) side
13740         CheckElementChangeBySide(x, y, center_element, player_element,
13741                                  CE_TOUCHING_X, border_side);
13742       }
13743
13744       break;
13745     }
13746   }
13747 }
13748
13749 void TestIfElementNextToCustomElement(int x, int y)
13750 {
13751   struct XY *xy = xy_topdown;
13752   static int trigger_sides[4][2] =
13753   {
13754     // center side      border side
13755     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13756     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13757     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13758     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13759   };
13760   int center_element = Tile[x][y];      // should always be non-moving!
13761   int i;
13762
13763   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13764     return;
13765
13766   for (i = 0; i < NUM_DIRECTIONS; i++)
13767   {
13768     int xx = x + xy[i].x;
13769     int yy = y + xy[i].y;
13770     int border_side = trigger_sides[i][1];
13771     int border_element;
13772
13773     if (!IN_LEV_FIELD(xx, yy))
13774       continue;
13775
13776     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13777       continue;                 // center and border element not connected
13778
13779     border_element = Tile[xx][yy];
13780
13781     // check for change of center element (but change it only once)
13782     if (CheckElementChangeBySide(x, y, center_element, border_element,
13783                                  CE_NEXT_TO_X, border_side))
13784       break;
13785   }
13786 }
13787
13788 void TestIfElementTouchesCustomElement(int x, int y)
13789 {
13790   struct XY *xy = xy_topdown;
13791   static int trigger_sides[4][2] =
13792   {
13793     // center side      border side
13794     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13795     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13796     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13797     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13798   };
13799   static int touch_dir[4] =
13800   {
13801     MV_LEFT | MV_RIGHT,
13802     MV_UP   | MV_DOWN,
13803     MV_UP   | MV_DOWN,
13804     MV_LEFT | MV_RIGHT
13805   };
13806   boolean change_center_element = FALSE;
13807   int center_element = Tile[x][y];      // should always be non-moving!
13808   int border_element_old[NUM_DIRECTIONS];
13809   int i;
13810
13811   for (i = 0; i < NUM_DIRECTIONS; i++)
13812   {
13813     int xx = x + xy[i].x;
13814     int yy = y + xy[i].y;
13815     int border_element;
13816
13817     border_element_old[i] = -1;
13818
13819     if (!IN_LEV_FIELD(xx, yy))
13820       continue;
13821
13822     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13823       border_element = Tile[xx][yy];    // may be moving!
13824     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13825       border_element = Tile[xx][yy];
13826     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13827       border_element = MovingOrBlocked2Element(xx, yy);
13828     else
13829       continue;                 // center and border element do not touch
13830
13831     border_element_old[i] = border_element;
13832   }
13833
13834   for (i = 0; i < NUM_DIRECTIONS; i++)
13835   {
13836     int xx = x + xy[i].x;
13837     int yy = y + xy[i].y;
13838     int center_side = trigger_sides[i][0];
13839     int border_element = border_element_old[i];
13840
13841     if (border_element == -1)
13842       continue;
13843
13844     // check for change of border element
13845     CheckElementChangeBySide(xx, yy, border_element, center_element,
13846                              CE_TOUCHING_X, center_side);
13847
13848     // (center element cannot be player, so we don't have to check this here)
13849   }
13850
13851   for (i = 0; i < NUM_DIRECTIONS; i++)
13852   {
13853     int xx = x + xy[i].x;
13854     int yy = y + xy[i].y;
13855     int border_side = trigger_sides[i][1];
13856     int border_element = border_element_old[i];
13857
13858     if (border_element == -1)
13859       continue;
13860
13861     // check for change of center element (but change it only once)
13862     if (!change_center_element)
13863       change_center_element =
13864         CheckElementChangeBySide(x, y, center_element, border_element,
13865                                  CE_TOUCHING_X, border_side);
13866
13867     if (IS_PLAYER(xx, yy))
13868     {
13869       /* use player element that is initially defined in the level playfield,
13870          not the player element that corresponds to the runtime player number
13871          (example: a level that contains EL_PLAYER_3 as the only player would
13872          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13873       int player_element = PLAYERINFO(xx, yy)->initial_element;
13874
13875       // as element "X" is the player here, check opposite (border) side
13876       CheckElementChangeBySide(x, y, center_element, player_element,
13877                                CE_TOUCHING_X, border_side);
13878     }
13879   }
13880 }
13881
13882 void TestIfElementHitsCustomElement(int x, int y, int direction)
13883 {
13884   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13885   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13886   int hitx = x + dx, hity = y + dy;
13887   int hitting_element = Tile[x][y];
13888   int touched_element;
13889
13890   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13891     return;
13892
13893   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13894                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13895
13896   if (IN_LEV_FIELD(hitx, hity))
13897   {
13898     int opposite_direction = MV_DIR_OPPOSITE(direction);
13899     int hitting_side = direction;
13900     int touched_side = opposite_direction;
13901     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13902                           MovDir[hitx][hity] != direction ||
13903                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13904
13905     object_hit = TRUE;
13906
13907     if (object_hit)
13908     {
13909       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13910                                CE_HITTING_X, touched_side);
13911
13912       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13913                                CE_HIT_BY_X, hitting_side);
13914
13915       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13916                                CE_HIT_BY_SOMETHING, opposite_direction);
13917
13918       if (IS_PLAYER(hitx, hity))
13919       {
13920         /* use player element that is initially defined in the level playfield,
13921            not the player element that corresponds to the runtime player number
13922            (example: a level that contains EL_PLAYER_3 as the only player would
13923            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13924         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13925
13926         CheckElementChangeBySide(x, y, hitting_element, player_element,
13927                                  CE_HITTING_X, touched_side);
13928       }
13929     }
13930   }
13931
13932   // "hitting something" is also true when hitting the playfield border
13933   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13934                            CE_HITTING_SOMETHING, direction);
13935 }
13936
13937 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13938 {
13939   int i, kill_x = -1, kill_y = -1;
13940
13941   int bad_element = -1;
13942   struct XY *test_xy = xy_topdown;
13943   static int test_dir[4] =
13944   {
13945     MV_UP,
13946     MV_LEFT,
13947     MV_RIGHT,
13948     MV_DOWN
13949   };
13950
13951   for (i = 0; i < NUM_DIRECTIONS; i++)
13952   {
13953     int test_x, test_y, test_move_dir, test_element;
13954
13955     test_x = good_x + test_xy[i].x;
13956     test_y = good_y + test_xy[i].y;
13957
13958     if (!IN_LEV_FIELD(test_x, test_y))
13959       continue;
13960
13961     test_move_dir =
13962       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13963
13964     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13965
13966     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13967        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13968     */
13969     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13970         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13971     {
13972       kill_x = test_x;
13973       kill_y = test_y;
13974       bad_element = test_element;
13975
13976       break;
13977     }
13978   }
13979
13980   if (kill_x != -1 || kill_y != -1)
13981   {
13982     if (IS_PLAYER(good_x, good_y))
13983     {
13984       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13985
13986       if (player->shield_deadly_time_left > 0 &&
13987           !IS_INDESTRUCTIBLE(bad_element))
13988         Bang(kill_x, kill_y);
13989       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13990         KillPlayer(player);
13991     }
13992     else
13993       Bang(good_x, good_y);
13994   }
13995 }
13996
13997 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13998 {
13999   int i, kill_x = -1, kill_y = -1;
14000   int bad_element = Tile[bad_x][bad_y];
14001   struct XY *test_xy = xy_topdown;
14002   static int touch_dir[4] =
14003   {
14004     MV_LEFT | MV_RIGHT,
14005     MV_UP   | MV_DOWN,
14006     MV_UP   | MV_DOWN,
14007     MV_LEFT | MV_RIGHT
14008   };
14009   static int test_dir[4] =
14010   {
14011     MV_UP,
14012     MV_LEFT,
14013     MV_RIGHT,
14014     MV_DOWN
14015   };
14016
14017   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14018     return;
14019
14020   for (i = 0; i < NUM_DIRECTIONS; i++)
14021   {
14022     int test_x, test_y, test_move_dir, test_element;
14023
14024     test_x = bad_x + test_xy[i].x;
14025     test_y = bad_y + test_xy[i].y;
14026
14027     if (!IN_LEV_FIELD(test_x, test_y))
14028       continue;
14029
14030     test_move_dir =
14031       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14032
14033     test_element = Tile[test_x][test_y];
14034
14035     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14036        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14037     */
14038     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14039         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14040     {
14041       // good thing is player or penguin that does not move away
14042       if (IS_PLAYER(test_x, test_y))
14043       {
14044         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14045
14046         if (bad_element == EL_ROBOT && player->is_moving)
14047           continue;     // robot does not kill player if he is moving
14048
14049         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14050         {
14051           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14052             continue;           // center and border element do not touch
14053         }
14054
14055         kill_x = test_x;
14056         kill_y = test_y;
14057
14058         break;
14059       }
14060       else if (test_element == EL_PENGUIN)
14061       {
14062         kill_x = test_x;
14063         kill_y = test_y;
14064
14065         break;
14066       }
14067     }
14068   }
14069
14070   if (kill_x != -1 || kill_y != -1)
14071   {
14072     if (IS_PLAYER(kill_x, kill_y))
14073     {
14074       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14075
14076       if (player->shield_deadly_time_left > 0 &&
14077           !IS_INDESTRUCTIBLE(bad_element))
14078         Bang(bad_x, bad_y);
14079       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14080         KillPlayer(player);
14081     }
14082     else
14083       Bang(kill_x, kill_y);
14084   }
14085 }
14086
14087 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14088 {
14089   int bad_element = Tile[bad_x][bad_y];
14090   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14091   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14092   int test_x = bad_x + dx, test_y = bad_y + dy;
14093   int test_move_dir, test_element;
14094   int kill_x = -1, kill_y = -1;
14095
14096   if (!IN_LEV_FIELD(test_x, test_y))
14097     return;
14098
14099   test_move_dir =
14100     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14101
14102   test_element = Tile[test_x][test_y];
14103
14104   if (test_move_dir != bad_move_dir)
14105   {
14106     // good thing can be player or penguin that does not move away
14107     if (IS_PLAYER(test_x, test_y))
14108     {
14109       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14110
14111       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14112          player as being hit when he is moving towards the bad thing, because
14113          the "get hit by" condition would be lost after the player stops) */
14114       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14115         return;         // player moves away from bad thing
14116
14117       kill_x = test_x;
14118       kill_y = test_y;
14119     }
14120     else if (test_element == EL_PENGUIN)
14121     {
14122       kill_x = test_x;
14123       kill_y = test_y;
14124     }
14125   }
14126
14127   if (kill_x != -1 || kill_y != -1)
14128   {
14129     if (IS_PLAYER(kill_x, kill_y))
14130     {
14131       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14132
14133       if (player->shield_deadly_time_left > 0 &&
14134           !IS_INDESTRUCTIBLE(bad_element))
14135         Bang(bad_x, bad_y);
14136       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14137         KillPlayer(player);
14138     }
14139     else
14140       Bang(kill_x, kill_y);
14141   }
14142 }
14143
14144 void TestIfPlayerTouchesBadThing(int x, int y)
14145 {
14146   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14147 }
14148
14149 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14150 {
14151   TestIfGoodThingHitsBadThing(x, y, move_dir);
14152 }
14153
14154 void TestIfBadThingTouchesPlayer(int x, int y)
14155 {
14156   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14157 }
14158
14159 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14160 {
14161   TestIfBadThingHitsGoodThing(x, y, move_dir);
14162 }
14163
14164 void TestIfFriendTouchesBadThing(int x, int y)
14165 {
14166   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14167 }
14168
14169 void TestIfBadThingTouchesFriend(int x, int y)
14170 {
14171   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14172 }
14173
14174 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14175 {
14176   int i, kill_x = bad_x, kill_y = bad_y;
14177   struct XY *xy = xy_topdown;
14178
14179   for (i = 0; i < NUM_DIRECTIONS; i++)
14180   {
14181     int x, y, element;
14182
14183     x = bad_x + xy[i].x;
14184     y = bad_y + xy[i].y;
14185     if (!IN_LEV_FIELD(x, y))
14186       continue;
14187
14188     element = Tile[x][y];
14189     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14190         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14191     {
14192       kill_x = x;
14193       kill_y = y;
14194       break;
14195     }
14196   }
14197
14198   if (kill_x != bad_x || kill_y != bad_y)
14199     Bang(bad_x, bad_y);
14200 }
14201
14202 void KillPlayer(struct PlayerInfo *player)
14203 {
14204   int jx = player->jx, jy = player->jy;
14205
14206   if (!player->active)
14207     return;
14208
14209 #if 0
14210   Debug("game:playing:KillPlayer",
14211         "0: killed == %d, active == %d, reanimated == %d",
14212         player->killed, player->active, player->reanimated);
14213 #endif
14214
14215   /* the following code was introduced to prevent an infinite loop when calling
14216      -> Bang()
14217      -> CheckTriggeredElementChangeExt()
14218      -> ExecuteCustomElementAction()
14219      -> KillPlayer()
14220      -> (infinitely repeating the above sequence of function calls)
14221      which occurs when killing the player while having a CE with the setting
14222      "kill player X when explosion of <player X>"; the solution using a new
14223      field "player->killed" was chosen for backwards compatibility, although
14224      clever use of the fields "player->active" etc. would probably also work */
14225 #if 1
14226   if (player->killed)
14227     return;
14228 #endif
14229
14230   player->killed = TRUE;
14231
14232   // remove accessible field at the player's position
14233   RemoveField(jx, jy);
14234
14235   // deactivate shield (else Bang()/Explode() would not work right)
14236   player->shield_normal_time_left = 0;
14237   player->shield_deadly_time_left = 0;
14238
14239 #if 0
14240   Debug("game:playing:KillPlayer",
14241         "1: killed == %d, active == %d, reanimated == %d",
14242         player->killed, player->active, player->reanimated);
14243 #endif
14244
14245   Bang(jx, jy);
14246
14247 #if 0
14248   Debug("game:playing:KillPlayer",
14249         "2: killed == %d, active == %d, reanimated == %d",
14250         player->killed, player->active, player->reanimated);
14251 #endif
14252
14253   if (player->reanimated)       // killed player may have been reanimated
14254     player->killed = player->reanimated = FALSE;
14255   else
14256     BuryPlayer(player);
14257 }
14258
14259 static void KillPlayerUnlessEnemyProtected(int x, int y)
14260 {
14261   if (!PLAYER_ENEMY_PROTECTED(x, y))
14262     KillPlayer(PLAYERINFO(x, y));
14263 }
14264
14265 static void KillPlayerUnlessExplosionProtected(int x, int y)
14266 {
14267   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14268     KillPlayer(PLAYERINFO(x, y));
14269 }
14270
14271 void BuryPlayer(struct PlayerInfo *player)
14272 {
14273   int jx = player->jx, jy = player->jy;
14274
14275   if (!player->active)
14276     return;
14277
14278   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14279
14280   RemovePlayer(player);
14281
14282   player->buried = TRUE;
14283
14284   if (game.all_players_gone)
14285     game.GameOver = TRUE;
14286 }
14287
14288 void RemovePlayer(struct PlayerInfo *player)
14289 {
14290   int jx = player->jx, jy = player->jy;
14291   int i, found = FALSE;
14292
14293   player->present = FALSE;
14294   player->active = FALSE;
14295
14296   // required for some CE actions (even if the player is not active anymore)
14297   player->MovPos = 0;
14298
14299   if (!ExplodeField[jx][jy])
14300     StorePlayer[jx][jy] = 0;
14301
14302   if (player->is_moving)
14303     TEST_DrawLevelField(player->last_jx, player->last_jy);
14304
14305   for (i = 0; i < MAX_PLAYERS; i++)
14306     if (stored_player[i].active)
14307       found = TRUE;
14308
14309   if (!found)
14310   {
14311     game.all_players_gone = TRUE;
14312     game.GameOver = TRUE;
14313   }
14314
14315   game.exit_x = game.robot_wheel_x = jx;
14316   game.exit_y = game.robot_wheel_y = jy;
14317 }
14318
14319 void ExitPlayer(struct PlayerInfo *player)
14320 {
14321   DrawPlayer(player);   // needed here only to cleanup last field
14322   RemovePlayer(player);
14323
14324   if (game.players_still_needed > 0)
14325     game.players_still_needed--;
14326 }
14327
14328 static void SetFieldForSnapping(int x, int y, int element, int direction,
14329                                 int player_index_bit)
14330 {
14331   struct ElementInfo *ei = &element_info[element];
14332   int direction_bit = MV_DIR_TO_BIT(direction);
14333   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14334   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14335                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14336
14337   Tile[x][y] = EL_ELEMENT_SNAPPING;
14338   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14339   MovDir[x][y] = direction;
14340   Store[x][y] = element;
14341   Store2[x][y] = player_index_bit;
14342
14343   ResetGfxAnimation(x, y);
14344
14345   GfxElement[x][y] = element;
14346   GfxAction[x][y] = action;
14347   GfxDir[x][y] = direction;
14348   GfxFrame[x][y] = -1;
14349 }
14350
14351 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14352                                    int player_index_bit)
14353 {
14354   TestIfElementTouchesCustomElement(x, y);      // for empty space
14355
14356   if (level.finish_dig_collect)
14357   {
14358     int dig_side = MV_DIR_OPPOSITE(direction);
14359     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14360                         CE_PLAYER_COLLECTS_X);
14361
14362     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14363                                         player_index_bit, dig_side);
14364     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14365                                         player_index_bit, dig_side);
14366   }
14367 }
14368
14369 /*
14370   =============================================================================
14371   checkDiagonalPushing()
14372   -----------------------------------------------------------------------------
14373   check if diagonal input device direction results in pushing of object
14374   (by checking if the alternative direction is walkable, diggable, ...)
14375   =============================================================================
14376 */
14377
14378 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14379                                     int x, int y, int real_dx, int real_dy)
14380 {
14381   int jx, jy, dx, dy, xx, yy;
14382
14383   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14384     return TRUE;
14385
14386   // diagonal direction: check alternative direction
14387   jx = player->jx;
14388   jy = player->jy;
14389   dx = x - jx;
14390   dy = y - jy;
14391   xx = jx + (dx == 0 ? real_dx : 0);
14392   yy = jy + (dy == 0 ? real_dy : 0);
14393
14394   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14395 }
14396
14397 /*
14398   =============================================================================
14399   DigField()
14400   -----------------------------------------------------------------------------
14401   x, y:                 field next to player (non-diagonal) to try to dig to
14402   real_dx, real_dy:     direction as read from input device (can be diagonal)
14403   =============================================================================
14404 */
14405
14406 static int DigField(struct PlayerInfo *player,
14407                     int oldx, int oldy, int x, int y,
14408                     int real_dx, int real_dy, int mode)
14409 {
14410   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14411   boolean player_was_pushing = player->is_pushing;
14412   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14413   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14414   int jx = oldx, jy = oldy;
14415   int dx = x - jx, dy = y - jy;
14416   int nextx = x + dx, nexty = y + dy;
14417   int move_direction = (dx == -1 ? MV_LEFT  :
14418                         dx == +1 ? MV_RIGHT :
14419                         dy == -1 ? MV_UP    :
14420                         dy == +1 ? MV_DOWN  : MV_NONE);
14421   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14422   int dig_side = MV_DIR_OPPOSITE(move_direction);
14423   int old_element = Tile[jx][jy];
14424   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14425   int collect_count;
14426
14427   if (is_player)                // function can also be called by EL_PENGUIN
14428   {
14429     if (player->MovPos == 0)
14430     {
14431       player->is_digging = FALSE;
14432       player->is_collecting = FALSE;
14433     }
14434
14435     if (player->MovPos == 0)    // last pushing move finished
14436       player->is_pushing = FALSE;
14437
14438     if (mode == DF_NO_PUSH)     // player just stopped pushing
14439     {
14440       player->is_switching = FALSE;
14441       player->push_delay = -1;
14442
14443       return MP_NO_ACTION;
14444     }
14445   }
14446   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14447     old_element = Back[jx][jy];
14448
14449   // in case of element dropped at player position, check background
14450   else if (Back[jx][jy] != EL_EMPTY &&
14451            game.engine_version >= VERSION_IDENT(2,2,0,0))
14452     old_element = Back[jx][jy];
14453
14454   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14455     return MP_NO_ACTION;        // field has no opening in this direction
14456
14457   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14458     return MP_NO_ACTION;        // field has no opening in this direction
14459
14460   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14461   {
14462     SplashAcid(x, y);
14463
14464     Tile[jx][jy] = player->artwork_element;
14465     InitMovingField(jx, jy, MV_DOWN);
14466     Store[jx][jy] = EL_ACID;
14467     ContinueMoving(jx, jy);
14468     BuryPlayer(player);
14469
14470     return MP_DONT_RUN_INTO;
14471   }
14472
14473   if (player_can_move && DONT_RUN_INTO(element))
14474   {
14475     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14476
14477     return MP_DONT_RUN_INTO;
14478   }
14479
14480   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14481     return MP_NO_ACTION;
14482
14483   collect_count = element_info[element].collect_count_initial;
14484
14485   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14486     return MP_NO_ACTION;
14487
14488   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14489     player_can_move = player_can_move_or_snap;
14490
14491   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14492       game.engine_version >= VERSION_IDENT(2,2,0,0))
14493   {
14494     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14495                                player->index_bit, dig_side);
14496     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14497                                         player->index_bit, dig_side);
14498
14499     if (element == EL_DC_LANDMINE)
14500       Bang(x, y);
14501
14502     if (Tile[x][y] != element)          // field changed by snapping
14503       return MP_ACTION;
14504
14505     return MP_NO_ACTION;
14506   }
14507
14508   if (player->gravity && is_player && !player->is_auto_moving &&
14509       canFallDown(player) && move_direction != MV_DOWN &&
14510       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14511     return MP_NO_ACTION;        // player cannot walk here due to gravity
14512
14513   if (player_can_move &&
14514       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14515   {
14516     int sound_element = SND_ELEMENT(element);
14517     int sound_action = ACTION_WALKING;
14518
14519     if (IS_RND_GATE(element))
14520     {
14521       if (!player->key[RND_GATE_NR(element)])
14522         return MP_NO_ACTION;
14523     }
14524     else if (IS_RND_GATE_GRAY(element))
14525     {
14526       if (!player->key[RND_GATE_GRAY_NR(element)])
14527         return MP_NO_ACTION;
14528     }
14529     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14530     {
14531       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14532         return MP_NO_ACTION;
14533     }
14534     else if (element == EL_EXIT_OPEN ||
14535              element == EL_EM_EXIT_OPEN ||
14536              element == EL_EM_EXIT_OPENING ||
14537              element == EL_STEEL_EXIT_OPEN ||
14538              element == EL_EM_STEEL_EXIT_OPEN ||
14539              element == EL_EM_STEEL_EXIT_OPENING ||
14540              element == EL_SP_EXIT_OPEN ||
14541              element == EL_SP_EXIT_OPENING)
14542     {
14543       sound_action = ACTION_PASSING;    // player is passing exit
14544     }
14545     else if (element == EL_EMPTY)
14546     {
14547       sound_action = ACTION_MOVING;             // nothing to walk on
14548     }
14549
14550     // play sound from background or player, whatever is available
14551     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14552       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14553     else
14554       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14555   }
14556   else if (player_can_move &&
14557            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14558   {
14559     if (!ACCESS_FROM(element, opposite_direction))
14560       return MP_NO_ACTION;      // field not accessible from this direction
14561
14562     if (CAN_MOVE(element))      // only fixed elements can be passed!
14563       return MP_NO_ACTION;
14564
14565     if (IS_EM_GATE(element))
14566     {
14567       if (!player->key[EM_GATE_NR(element)])
14568         return MP_NO_ACTION;
14569     }
14570     else if (IS_EM_GATE_GRAY(element))
14571     {
14572       if (!player->key[EM_GATE_GRAY_NR(element)])
14573         return MP_NO_ACTION;
14574     }
14575     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14576     {
14577       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14578         return MP_NO_ACTION;
14579     }
14580     else if (IS_EMC_GATE(element))
14581     {
14582       if (!player->key[EMC_GATE_NR(element)])
14583         return MP_NO_ACTION;
14584     }
14585     else if (IS_EMC_GATE_GRAY(element))
14586     {
14587       if (!player->key[EMC_GATE_GRAY_NR(element)])
14588         return MP_NO_ACTION;
14589     }
14590     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14591     {
14592       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14593         return MP_NO_ACTION;
14594     }
14595     else if (element == EL_DC_GATE_WHITE ||
14596              element == EL_DC_GATE_WHITE_GRAY ||
14597              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14598     {
14599       if (player->num_white_keys == 0)
14600         return MP_NO_ACTION;
14601
14602       player->num_white_keys--;
14603     }
14604     else if (IS_SP_PORT(element))
14605     {
14606       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14607           element == EL_SP_GRAVITY_PORT_RIGHT ||
14608           element == EL_SP_GRAVITY_PORT_UP ||
14609           element == EL_SP_GRAVITY_PORT_DOWN)
14610         player->gravity = !player->gravity;
14611       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14612                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14613                element == EL_SP_GRAVITY_ON_PORT_UP ||
14614                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14615         player->gravity = TRUE;
14616       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14617                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14618                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14619                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14620         player->gravity = FALSE;
14621     }
14622
14623     // automatically move to the next field with double speed
14624     player->programmed_action = move_direction;
14625
14626     if (player->move_delay_reset_counter == 0)
14627     {
14628       player->move_delay_reset_counter = 2;     // two double speed steps
14629
14630       DOUBLE_PLAYER_SPEED(player);
14631     }
14632
14633     PlayLevelSoundAction(x, y, ACTION_PASSING);
14634   }
14635   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14636   {
14637     RemoveField(x, y);
14638
14639     if (mode != DF_SNAP)
14640     {
14641       GfxElement[x][y] = GFX_ELEMENT(element);
14642       player->is_digging = TRUE;
14643     }
14644
14645     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14646
14647     // use old behaviour for old levels (digging)
14648     if (!level.finish_dig_collect)
14649     {
14650       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14651                                           player->index_bit, dig_side);
14652
14653       // if digging triggered player relocation, finish digging tile
14654       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14655         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14656     }
14657
14658     if (mode == DF_SNAP)
14659     {
14660       if (level.block_snap_field)
14661         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14662       else
14663         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14664
14665       // use old behaviour for old levels (snapping)
14666       if (!level.finish_dig_collect)
14667         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14668                                             player->index_bit, dig_side);
14669     }
14670   }
14671   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14672   {
14673     RemoveField(x, y);
14674
14675     if (is_player && mode != DF_SNAP)
14676     {
14677       GfxElement[x][y] = element;
14678       player->is_collecting = TRUE;
14679     }
14680
14681     if (element == EL_SPEED_PILL)
14682     {
14683       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14684     }
14685     else if (element == EL_EXTRA_TIME && level.time > 0)
14686     {
14687       TimeLeft += level.extra_time;
14688
14689       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14690
14691       DisplayGameControlValues();
14692     }
14693     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14694     {
14695       int shield_time = (element == EL_SHIELD_DEADLY ?
14696                          level.shield_deadly_time :
14697                          level.shield_normal_time);
14698
14699       player->shield_normal_time_left += shield_time;
14700       if (element == EL_SHIELD_DEADLY)
14701         player->shield_deadly_time_left += shield_time;
14702     }
14703     else if (element == EL_DYNAMITE ||
14704              element == EL_EM_DYNAMITE ||
14705              element == EL_SP_DISK_RED)
14706     {
14707       if (player->inventory_size < MAX_INVENTORY_SIZE)
14708         player->inventory_element[player->inventory_size++] = element;
14709
14710       DrawGameDoorValues();
14711     }
14712     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14713     {
14714       player->dynabomb_count++;
14715       player->dynabombs_left++;
14716     }
14717     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14718     {
14719       player->dynabomb_size++;
14720     }
14721     else if (element == EL_DYNABOMB_INCREASE_POWER)
14722     {
14723       player->dynabomb_xl = TRUE;
14724     }
14725     else if (IS_KEY(element))
14726     {
14727       player->key[KEY_NR(element)] = TRUE;
14728
14729       DrawGameDoorValues();
14730     }
14731     else if (element == EL_DC_KEY_WHITE)
14732     {
14733       player->num_white_keys++;
14734
14735       // display white keys?
14736       // DrawGameDoorValues();
14737     }
14738     else if (IS_ENVELOPE(element))
14739     {
14740       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14741
14742       if (!wait_for_snapping)
14743         player->show_envelope = element;
14744     }
14745     else if (element == EL_EMC_LENSES)
14746     {
14747       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14748
14749       RedrawAllInvisibleElementsForLenses();
14750     }
14751     else if (element == EL_EMC_MAGNIFIER)
14752     {
14753       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14754
14755       RedrawAllInvisibleElementsForMagnifier();
14756     }
14757     else if (IS_DROPPABLE(element) ||
14758              IS_THROWABLE(element))     // can be collected and dropped
14759     {
14760       int i;
14761
14762       if (collect_count == 0)
14763         player->inventory_infinite_element = element;
14764       else
14765         for (i = 0; i < collect_count; i++)
14766           if (player->inventory_size < MAX_INVENTORY_SIZE)
14767             player->inventory_element[player->inventory_size++] = element;
14768
14769       DrawGameDoorValues();
14770     }
14771     else if (collect_count > 0)
14772     {
14773       game.gems_still_needed -= collect_count;
14774       if (game.gems_still_needed < 0)
14775         game.gems_still_needed = 0;
14776
14777       game.snapshot.collected_item = TRUE;
14778
14779       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14780
14781       DisplayGameControlValues();
14782     }
14783
14784     RaiseScoreElement(element);
14785     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14786
14787     // use old behaviour for old levels (collecting)
14788     if (!level.finish_dig_collect && is_player)
14789     {
14790       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14791                                           player->index_bit, dig_side);
14792
14793       // if collecting triggered player relocation, finish collecting tile
14794       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14795         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14796     }
14797
14798     if (mode == DF_SNAP)
14799     {
14800       if (level.block_snap_field)
14801         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14802       else
14803         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14804
14805       // use old behaviour for old levels (snapping)
14806       if (!level.finish_dig_collect)
14807         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14808                                             player->index_bit, dig_side);
14809     }
14810   }
14811   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14812   {
14813     if (mode == DF_SNAP && element != EL_BD_ROCK)
14814       return MP_NO_ACTION;
14815
14816     if (CAN_FALL(element) && dy)
14817       return MP_NO_ACTION;
14818
14819     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14820         !(element == EL_SPRING && level.use_spring_bug))
14821       return MP_NO_ACTION;
14822
14823     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14824         ((move_direction & MV_VERTICAL &&
14825           ((element_info[element].move_pattern & MV_LEFT &&
14826             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14827            (element_info[element].move_pattern & MV_RIGHT &&
14828             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14829          (move_direction & MV_HORIZONTAL &&
14830           ((element_info[element].move_pattern & MV_UP &&
14831             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14832            (element_info[element].move_pattern & MV_DOWN &&
14833             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14834       return MP_NO_ACTION;
14835
14836     // do not push elements already moving away faster than player
14837     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14838         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14839       return MP_NO_ACTION;
14840
14841     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14842     {
14843       if (player->push_delay_value == -1 || !player_was_pushing)
14844         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14845     }
14846     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14847     {
14848       if (player->push_delay_value == -1)
14849         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14850     }
14851     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14852     {
14853       if (!player->is_pushing)
14854         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14855     }
14856
14857     player->is_pushing = TRUE;
14858     player->is_active = TRUE;
14859
14860     if (!(IN_LEV_FIELD(nextx, nexty) &&
14861           (IS_FREE(nextx, nexty) ||
14862            (IS_SB_ELEMENT(element) &&
14863             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14864            (IS_CUSTOM_ELEMENT(element) &&
14865             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14866       return MP_NO_ACTION;
14867
14868     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14869       return MP_NO_ACTION;
14870
14871     if (player->push_delay == -1)       // new pushing; restart delay
14872       player->push_delay = 0;
14873
14874     if (player->push_delay < player->push_delay_value &&
14875         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14876         element != EL_SPRING && element != EL_BALLOON)
14877     {
14878       // make sure that there is no move delay before next try to push
14879       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14880         player->move_delay = 0;
14881
14882       return MP_NO_ACTION;
14883     }
14884
14885     if (IS_CUSTOM_ELEMENT(element) &&
14886         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14887     {
14888       if (!DigFieldByCE(nextx, nexty, element))
14889         return MP_NO_ACTION;
14890     }
14891
14892     if (IS_SB_ELEMENT(element))
14893     {
14894       boolean sokoban_task_solved = FALSE;
14895
14896       if (element == EL_SOKOBAN_FIELD_FULL)
14897       {
14898         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14899
14900         IncrementSokobanFieldsNeeded();
14901         IncrementSokobanObjectsNeeded();
14902       }
14903
14904       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14905       {
14906         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14907
14908         DecrementSokobanFieldsNeeded();
14909         DecrementSokobanObjectsNeeded();
14910
14911         // sokoban object was pushed from empty field to sokoban field
14912         if (Back[x][y] == EL_EMPTY)
14913           sokoban_task_solved = TRUE;
14914       }
14915
14916       Tile[x][y] = EL_SOKOBAN_OBJECT;
14917
14918       if (Back[x][y] == Back[nextx][nexty])
14919         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14920       else if (Back[x][y] != 0)
14921         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14922                                     ACTION_EMPTYING);
14923       else
14924         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14925                                     ACTION_FILLING);
14926
14927       if (sokoban_task_solved &&
14928           game.sokoban_fields_still_needed == 0 &&
14929           game.sokoban_objects_still_needed == 0 &&
14930           level.auto_exit_sokoban)
14931       {
14932         game.players_still_needed = 0;
14933
14934         LevelSolved();
14935
14936         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14937       }
14938     }
14939     else
14940       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14941
14942     InitMovingField(x, y, move_direction);
14943     GfxAction[x][y] = ACTION_PUSHING;
14944
14945     if (mode == DF_SNAP)
14946       ContinueMoving(x, y);
14947     else
14948       MovPos[x][y] = (dx != 0 ? dx : dy);
14949
14950     Pushed[x][y] = TRUE;
14951     Pushed[nextx][nexty] = TRUE;
14952
14953     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14954       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14955     else
14956       player->push_delay_value = -1;    // get new value later
14957
14958     // check for element change _after_ element has been pushed
14959     if (game.use_change_when_pushing_bug)
14960     {
14961       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14962                                  player->index_bit, dig_side);
14963       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14964                                           player->index_bit, dig_side);
14965     }
14966   }
14967   else if (IS_SWITCHABLE(element))
14968   {
14969     if (PLAYER_SWITCHING(player, x, y))
14970     {
14971       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14972                                           player->index_bit, dig_side);
14973
14974       return MP_ACTION;
14975     }
14976
14977     player->is_switching = TRUE;
14978     player->switch_x = x;
14979     player->switch_y = y;
14980
14981     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14982
14983     if (element == EL_ROBOT_WHEEL)
14984     {
14985       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14986
14987       game.robot_wheel_x = x;
14988       game.robot_wheel_y = y;
14989       game.robot_wheel_active = TRUE;
14990
14991       TEST_DrawLevelField(x, y);
14992     }
14993     else if (element == EL_SP_TERMINAL)
14994     {
14995       int xx, yy;
14996
14997       SCAN_PLAYFIELD(xx, yy)
14998       {
14999         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
15000         {
15001           Bang(xx, yy);
15002         }
15003         else if (Tile[xx][yy] == EL_SP_TERMINAL)
15004         {
15005           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15006
15007           ResetGfxAnimation(xx, yy);
15008           TEST_DrawLevelField(xx, yy);
15009         }
15010       }
15011     }
15012     else if (IS_BELT_SWITCH(element))
15013     {
15014       ToggleBeltSwitch(x, y);
15015     }
15016     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15017              element == EL_SWITCHGATE_SWITCH_DOWN ||
15018              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15019              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15020     {
15021       ToggleSwitchgateSwitch();
15022     }
15023     else if (element == EL_LIGHT_SWITCH ||
15024              element == EL_LIGHT_SWITCH_ACTIVE)
15025     {
15026       ToggleLightSwitch(x, y);
15027     }
15028     else if (element == EL_TIMEGATE_SWITCH ||
15029              element == EL_DC_TIMEGATE_SWITCH)
15030     {
15031       ActivateTimegateSwitch(x, y);
15032     }
15033     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15034              element == EL_BALLOON_SWITCH_RIGHT ||
15035              element == EL_BALLOON_SWITCH_UP    ||
15036              element == EL_BALLOON_SWITCH_DOWN  ||
15037              element == EL_BALLOON_SWITCH_NONE  ||
15038              element == EL_BALLOON_SWITCH_ANY)
15039     {
15040       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15041                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15042                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15043                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15044                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15045                              move_direction);
15046     }
15047     else if (element == EL_LAMP)
15048     {
15049       Tile[x][y] = EL_LAMP_ACTIVE;
15050       game.lights_still_needed--;
15051
15052       ResetGfxAnimation(x, y);
15053       TEST_DrawLevelField(x, y);
15054     }
15055     else if (element == EL_TIME_ORB_FULL)
15056     {
15057       Tile[x][y] = EL_TIME_ORB_EMPTY;
15058
15059       if (level.time > 0 || level.use_time_orb_bug)
15060       {
15061         TimeLeft += level.time_orb_time;
15062         game.no_level_time_limit = FALSE;
15063
15064         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15065
15066         DisplayGameControlValues();
15067       }
15068
15069       ResetGfxAnimation(x, y);
15070       TEST_DrawLevelField(x, y);
15071     }
15072     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15073              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15074     {
15075       int xx, yy;
15076
15077       game.ball_active = !game.ball_active;
15078
15079       SCAN_PLAYFIELD(xx, yy)
15080       {
15081         int e = Tile[xx][yy];
15082
15083         if (game.ball_active)
15084         {
15085           if (e == EL_EMC_MAGIC_BALL)
15086             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15087           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15088             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15089         }
15090         else
15091         {
15092           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15093             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15094           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15095             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15096         }
15097       }
15098     }
15099
15100     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15101                                         player->index_bit, dig_side);
15102
15103     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15104                                         player->index_bit, dig_side);
15105
15106     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15107                                         player->index_bit, dig_side);
15108
15109     return MP_ACTION;
15110   }
15111   else
15112   {
15113     if (!PLAYER_SWITCHING(player, x, y))
15114     {
15115       player->is_switching = TRUE;
15116       player->switch_x = x;
15117       player->switch_y = y;
15118
15119       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15120                                  player->index_bit, dig_side);
15121       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15122                                           player->index_bit, dig_side);
15123
15124       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15125                                  player->index_bit, dig_side);
15126       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15127                                           player->index_bit, dig_side);
15128     }
15129
15130     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15131                                player->index_bit, dig_side);
15132     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15133                                         player->index_bit, dig_side);
15134
15135     return MP_NO_ACTION;
15136   }
15137
15138   player->push_delay = -1;
15139
15140   if (is_player)                // function can also be called by EL_PENGUIN
15141   {
15142     if (Tile[x][y] != element)          // really digged/collected something
15143     {
15144       player->is_collecting = !player->is_digging;
15145       player->is_active = TRUE;
15146
15147       player->last_removed_element = element;
15148     }
15149   }
15150
15151   return MP_MOVING;
15152 }
15153
15154 static boolean DigFieldByCE(int x, int y, int digging_element)
15155 {
15156   int element = Tile[x][y];
15157
15158   if (!IS_FREE(x, y))
15159   {
15160     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15161                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15162                   ACTION_BREAKING);
15163
15164     // no element can dig solid indestructible elements
15165     if (IS_INDESTRUCTIBLE(element) &&
15166         !IS_DIGGABLE(element) &&
15167         !IS_COLLECTIBLE(element))
15168       return FALSE;
15169
15170     if (AmoebaNr[x][y] &&
15171         (element == EL_AMOEBA_FULL ||
15172          element == EL_BD_AMOEBA ||
15173          element == EL_AMOEBA_GROWING))
15174     {
15175       AmoebaCnt[AmoebaNr[x][y]]--;
15176       AmoebaCnt2[AmoebaNr[x][y]]--;
15177     }
15178
15179     if (IS_MOVING(x, y))
15180       RemoveMovingField(x, y);
15181     else
15182     {
15183       RemoveField(x, y);
15184       TEST_DrawLevelField(x, y);
15185     }
15186
15187     // if digged element was about to explode, prevent the explosion
15188     ExplodeField[x][y] = EX_TYPE_NONE;
15189
15190     PlayLevelSoundAction(x, y, action);
15191   }
15192
15193   Store[x][y] = EL_EMPTY;
15194
15195   // this makes it possible to leave the removed element again
15196   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15197     Store[x][y] = element;
15198
15199   return TRUE;
15200 }
15201
15202 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15203 {
15204   int jx = player->jx, jy = player->jy;
15205   int x = jx + dx, y = jy + dy;
15206   int snap_direction = (dx == -1 ? MV_LEFT  :
15207                         dx == +1 ? MV_RIGHT :
15208                         dy == -1 ? MV_UP    :
15209                         dy == +1 ? MV_DOWN  : MV_NONE);
15210   boolean can_continue_snapping = (level.continuous_snapping &&
15211                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15212
15213   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15214     return FALSE;
15215
15216   if (!player->active || !IN_LEV_FIELD(x, y))
15217     return FALSE;
15218
15219   if (dx && dy)
15220     return FALSE;
15221
15222   if (!dx && !dy)
15223   {
15224     if (player->MovPos == 0)
15225       player->is_pushing = FALSE;
15226
15227     player->is_snapping = FALSE;
15228
15229     if (player->MovPos == 0)
15230     {
15231       player->is_moving = FALSE;
15232       player->is_digging = FALSE;
15233       player->is_collecting = FALSE;
15234     }
15235
15236     return FALSE;
15237   }
15238
15239   // prevent snapping with already pressed snap key when not allowed
15240   if (player->is_snapping && !can_continue_snapping)
15241     return FALSE;
15242
15243   player->MovDir = snap_direction;
15244
15245   if (player->MovPos == 0)
15246   {
15247     player->is_moving = FALSE;
15248     player->is_digging = FALSE;
15249     player->is_collecting = FALSE;
15250   }
15251
15252   player->is_dropping = FALSE;
15253   player->is_dropping_pressed = FALSE;
15254   player->drop_pressed_delay = 0;
15255
15256   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15257     return FALSE;
15258
15259   player->is_snapping = TRUE;
15260   player->is_active = TRUE;
15261
15262   if (player->MovPos == 0)
15263   {
15264     player->is_moving = FALSE;
15265     player->is_digging = FALSE;
15266     player->is_collecting = FALSE;
15267   }
15268
15269   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15270     TEST_DrawLevelField(player->last_jx, player->last_jy);
15271
15272   TEST_DrawLevelField(x, y);
15273
15274   return TRUE;
15275 }
15276
15277 static boolean DropElement(struct PlayerInfo *player)
15278 {
15279   int old_element, new_element;
15280   int dropx = player->jx, dropy = player->jy;
15281   int drop_direction = player->MovDir;
15282   int drop_side = drop_direction;
15283   int drop_element = get_next_dropped_element(player);
15284
15285   /* do not drop an element on top of another element; when holding drop key
15286      pressed without moving, dropped element must move away before the next
15287      element can be dropped (this is especially important if the next element
15288      is dynamite, which can be placed on background for historical reasons) */
15289   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15290     return MP_ACTION;
15291
15292   if (IS_THROWABLE(drop_element))
15293   {
15294     dropx += GET_DX_FROM_DIR(drop_direction);
15295     dropy += GET_DY_FROM_DIR(drop_direction);
15296
15297     if (!IN_LEV_FIELD(dropx, dropy))
15298       return FALSE;
15299   }
15300
15301   old_element = Tile[dropx][dropy];     // old element at dropping position
15302   new_element = drop_element;           // default: no change when dropping
15303
15304   // check if player is active, not moving and ready to drop
15305   if (!player->active || player->MovPos || player->drop_delay > 0)
15306     return FALSE;
15307
15308   // check if player has anything that can be dropped
15309   if (new_element == EL_UNDEFINED)
15310     return FALSE;
15311
15312   // only set if player has anything that can be dropped
15313   player->is_dropping_pressed = TRUE;
15314
15315   // check if drop key was pressed long enough for EM style dynamite
15316   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15317     return FALSE;
15318
15319   // check if anything can be dropped at the current position
15320   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15321     return FALSE;
15322
15323   // collected custom elements can only be dropped on empty fields
15324   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15325     return FALSE;
15326
15327   if (old_element != EL_EMPTY)
15328     Back[dropx][dropy] = old_element;   // store old element on this field
15329
15330   ResetGfxAnimation(dropx, dropy);
15331   ResetRandomAnimationValue(dropx, dropy);
15332
15333   if (player->inventory_size > 0 ||
15334       player->inventory_infinite_element != EL_UNDEFINED)
15335   {
15336     if (player->inventory_size > 0)
15337     {
15338       player->inventory_size--;
15339
15340       DrawGameDoorValues();
15341
15342       if (new_element == EL_DYNAMITE)
15343         new_element = EL_DYNAMITE_ACTIVE;
15344       else if (new_element == EL_EM_DYNAMITE)
15345         new_element = EL_EM_DYNAMITE_ACTIVE;
15346       else if (new_element == EL_SP_DISK_RED)
15347         new_element = EL_SP_DISK_RED_ACTIVE;
15348     }
15349
15350     Tile[dropx][dropy] = new_element;
15351
15352     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15353       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15354                           el2img(Tile[dropx][dropy]), 0);
15355
15356     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15357
15358     // needed if previous element just changed to "empty" in the last frame
15359     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15360
15361     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15362                                player->index_bit, drop_side);
15363     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15364                                         CE_PLAYER_DROPS_X,
15365                                         player->index_bit, drop_side);
15366
15367     TestIfElementTouchesCustomElement(dropx, dropy);
15368   }
15369   else          // player is dropping a dyna bomb
15370   {
15371     player->dynabombs_left--;
15372
15373     Tile[dropx][dropy] = new_element;
15374
15375     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15376       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15377                           el2img(Tile[dropx][dropy]), 0);
15378
15379     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15380   }
15381
15382   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15383     InitField_WithBug1(dropx, dropy, FALSE);
15384
15385   new_element = Tile[dropx][dropy];     // element might have changed
15386
15387   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15388       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15389   {
15390     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15391       MovDir[dropx][dropy] = drop_direction;
15392
15393     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15394
15395     // do not cause impact style collision by dropping elements that can fall
15396     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15397   }
15398
15399   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15400   player->is_dropping = TRUE;
15401
15402   player->drop_pressed_delay = 0;
15403   player->is_dropping_pressed = FALSE;
15404
15405   player->drop_x = dropx;
15406   player->drop_y = dropy;
15407
15408   return TRUE;
15409 }
15410
15411 // ----------------------------------------------------------------------------
15412 // game sound playing functions
15413 // ----------------------------------------------------------------------------
15414
15415 static int *loop_sound_frame = NULL;
15416 static int *loop_sound_volume = NULL;
15417
15418 void InitPlayLevelSound(void)
15419 {
15420   int num_sounds = getSoundListSize();
15421
15422   checked_free(loop_sound_frame);
15423   checked_free(loop_sound_volume);
15424
15425   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15426   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15427 }
15428
15429 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15430 {
15431   int sx = SCREENX(x), sy = SCREENY(y);
15432   int volume, stereo_position;
15433   int max_distance = 8;
15434   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15435
15436   if ((!setup.sound_simple && !is_loop_sound) ||
15437       (!setup.sound_loops && is_loop_sound))
15438     return;
15439
15440   if (!IN_LEV_FIELD(x, y) ||
15441       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15442       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15443     return;
15444
15445   volume = SOUND_MAX_VOLUME;
15446
15447   if (!IN_SCR_FIELD(sx, sy))
15448   {
15449     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15450     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15451
15452     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15453   }
15454
15455   stereo_position = (SOUND_MAX_LEFT +
15456                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15457                      (SCR_FIELDX + 2 * max_distance));
15458
15459   if (is_loop_sound)
15460   {
15461     /* This assures that quieter loop sounds do not overwrite louder ones,
15462        while restarting sound volume comparison with each new game frame. */
15463
15464     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15465       return;
15466
15467     loop_sound_volume[nr] = volume;
15468     loop_sound_frame[nr] = FrameCounter;
15469   }
15470
15471   PlaySoundExt(nr, volume, stereo_position, type);
15472 }
15473
15474 static void PlayLevelSound(int x, int y, int nr)
15475 {
15476   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15477 }
15478
15479 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15480 {
15481   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15482                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15483                  y < LEVELY(BY1) ? LEVELY(BY1) :
15484                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15485                  sound_action);
15486 }
15487
15488 static void PlayLevelSoundAction(int x, int y, int action)
15489 {
15490   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15491 }
15492
15493 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15494 {
15495   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15496
15497   if (sound_effect != SND_UNDEFINED)
15498     PlayLevelSound(x, y, sound_effect);
15499 }
15500
15501 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15502                                               int action)
15503 {
15504   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15505
15506   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15507     PlayLevelSound(x, y, sound_effect);
15508 }
15509
15510 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15511 {
15512   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15513
15514   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15515     PlayLevelSound(x, y, sound_effect);
15516 }
15517
15518 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15519 {
15520   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15521
15522   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15523     StopSound(sound_effect);
15524 }
15525
15526 static int getLevelMusicNr(void)
15527 {
15528   int level_pos = level_nr - leveldir_current->first_level;
15529
15530   if (levelset.music[level_nr] != MUS_UNDEFINED)
15531     return levelset.music[level_nr];            // from config file
15532   else
15533     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15534 }
15535
15536 static void FadeLevelSounds(void)
15537 {
15538   FadeSounds();
15539 }
15540
15541 static void FadeLevelMusic(void)
15542 {
15543   int music_nr = getLevelMusicNr();
15544   char *curr_music = getCurrentlyPlayingMusicFilename();
15545   char *next_music = getMusicInfoEntryFilename(music_nr);
15546
15547   if (!strEqual(curr_music, next_music))
15548     FadeMusic();
15549 }
15550
15551 void FadeLevelSoundsAndMusic(void)
15552 {
15553   FadeLevelSounds();
15554   FadeLevelMusic();
15555 }
15556
15557 static void PlayLevelMusic(void)
15558 {
15559   int music_nr = getLevelMusicNr();
15560   char *curr_music = getCurrentlyPlayingMusicFilename();
15561   char *next_music = getMusicInfoEntryFilename(music_nr);
15562
15563   if (!strEqual(curr_music, next_music))
15564     PlayMusicLoop(music_nr);
15565 }
15566
15567 static int getSoundAction_BD(int sample)
15568 {
15569   switch (sample)
15570   {
15571     case GD_S_STONE_PUSHING:
15572     case GD_S_MEGA_STONE_PUSHING:
15573     case GD_S_FLYING_STONE_PUSHING:
15574     case GD_S_WAITING_STONE_PUSHING:
15575     case GD_S_CHASING_STONE_PUSHING:
15576     case GD_S_NUT_PUSHING:
15577     case GD_S_NITRO_PACK_PUSHING:
15578     case GD_S_BLADDER_PUSHING:
15579     case GD_S_BOX_PUSHING:
15580       return ACTION_PUSHING;
15581
15582     case GD_S_STONE_FALLING:
15583     case GD_S_MEGA_STONE_FALLING:
15584     case GD_S_FLYING_STONE_FALLING:
15585     case GD_S_NUT_FALLING:
15586     case GD_S_DIRT_BALL_FALLING:
15587     case GD_S_DIRT_LOOSE_FALLING:
15588     case GD_S_NITRO_PACK_FALLING:
15589     case GD_S_FALLING_WALL_FALLING:
15590       return ACTION_FALLING;
15591
15592     case GD_S_STONE_IMPACT:
15593     case GD_S_MEGA_STONE_IMPACT:
15594     case GD_S_FLYING_STONE_IMPACT:
15595     case GD_S_NUT_IMPACT:
15596     case GD_S_DIRT_BALL_IMPACT:
15597     case GD_S_DIRT_LOOSE_IMPACT:
15598     case GD_S_NITRO_PACK_IMPACT:
15599     case GD_S_FALLING_WALL_IMPACT:
15600       return ACTION_IMPACT;
15601
15602     case GD_S_NUT_CRACKING:
15603       return ACTION_BREAKING;
15604
15605     case GD_S_EXPANDING_WALL:
15606     case GD_S_WALL_REAPPEARING:
15607     case GD_S_SLIME:
15608     case GD_S_LAVA:
15609     case GD_S_ACID_SPREADING:
15610       return ACTION_GROWING;
15611
15612     case GD_S_DIAMOND_COLLECTING:
15613     case GD_S_FLYING_DIAMOND_COLLECTING:
15614     case GD_S_SKELETON_COLLECTING:
15615     case GD_S_PNEUMATIC_COLLECTING:
15616     case GD_S_BOMB_COLLECTING:
15617     case GD_S_CLOCK_COLLECTING:
15618     case GD_S_SWEET_COLLECTING:
15619     case GD_S_KEY_COLLECTING:
15620     case GD_S_DIAMOND_KEY_COLLECTING:
15621       return ACTION_COLLECTING;
15622
15623     case GD_S_BOMB_PLACING:
15624     case GD_S_REPLICATOR:
15625       return ACTION_DROPPING;
15626
15627     case GD_S_BLADDER_MOVING:
15628       return ACTION_MOVING;
15629
15630     case GD_S_BLADDER_SPENDER:
15631     case GD_S_BLADDER_CONVERTING:
15632     case GD_S_GRAVITY_CHANGING:
15633       return ACTION_CHANGING;
15634
15635     case GD_S_BITER_EATING:
15636       return ACTION_EATING;
15637
15638     case GD_S_DOOR_OPENING:
15639     case GD_S_CRACKING:
15640       return ACTION_OPENING;
15641
15642     case GD_S_DIRT_WALKING:
15643       return ACTION_DIGGING;
15644
15645     case GD_S_EMPTY_WALKING:
15646       return ACTION_WALKING;
15647
15648     case GD_S_SWITCH_BITER:
15649     case GD_S_SWITCH_CREATURES:
15650     case GD_S_SWITCH_GRAVITY:
15651     case GD_S_SWITCH_EXPANDING:
15652     case GD_S_SWITCH_CONVEYOR:
15653     case GD_S_SWITCH_REPLICATOR:
15654     case GD_S_STIRRING:
15655       return ACTION_ACTIVATING;
15656
15657     case GD_S_TELEPORTER:
15658       return ACTION_PASSING;
15659
15660     case GD_S_EXPLODING:
15661     case GD_S_BOMB_EXPLODING:
15662     case GD_S_GHOST_EXPLODING:
15663     case GD_S_VOODOO_EXPLODING:
15664     case GD_S_NITRO_PACK_EXPLODING:
15665       return ACTION_EXPLODING;
15666
15667     case GD_S_COVERING:
15668     case GD_S_AMOEBA:
15669     case GD_S_MAGIC_WALL:
15670     case GD_S_PNEUMATIC_HAMMER:
15671     case GD_S_WATER:
15672       return ACTION_ACTIVE;
15673
15674     case GD_S_DIAMOND_FALLING_RANDOM:
15675     case GD_S_DIAMOND_FALLING_1:
15676     case GD_S_DIAMOND_FALLING_2:
15677     case GD_S_DIAMOND_FALLING_3:
15678     case GD_S_DIAMOND_FALLING_4:
15679     case GD_S_DIAMOND_FALLING_5:
15680     case GD_S_DIAMOND_FALLING_6:
15681     case GD_S_DIAMOND_FALLING_7:
15682     case GD_S_DIAMOND_FALLING_8:
15683     case GD_S_DIAMOND_IMPACT_RANDOM:
15684     case GD_S_DIAMOND_IMPACT_1:
15685     case GD_S_DIAMOND_IMPACT_2:
15686     case GD_S_DIAMOND_IMPACT_3:
15687     case GD_S_DIAMOND_IMPACT_4:
15688     case GD_S_DIAMOND_IMPACT_5:
15689     case GD_S_DIAMOND_IMPACT_6:
15690     case GD_S_DIAMOND_IMPACT_7:
15691     case GD_S_DIAMOND_IMPACT_8:
15692     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15693     case GD_S_FLYING_DIAMOND_FALLING_1:
15694     case GD_S_FLYING_DIAMOND_FALLING_2:
15695     case GD_S_FLYING_DIAMOND_FALLING_3:
15696     case GD_S_FLYING_DIAMOND_FALLING_4:
15697     case GD_S_FLYING_DIAMOND_FALLING_5:
15698     case GD_S_FLYING_DIAMOND_FALLING_6:
15699     case GD_S_FLYING_DIAMOND_FALLING_7:
15700     case GD_S_FLYING_DIAMOND_FALLING_8:
15701     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15702     case GD_S_FLYING_DIAMOND_IMPACT_1:
15703     case GD_S_FLYING_DIAMOND_IMPACT_2:
15704     case GD_S_FLYING_DIAMOND_IMPACT_3:
15705     case GD_S_FLYING_DIAMOND_IMPACT_4:
15706     case GD_S_FLYING_DIAMOND_IMPACT_5:
15707     case GD_S_FLYING_DIAMOND_IMPACT_6:
15708     case GD_S_FLYING_DIAMOND_IMPACT_7:
15709     case GD_S_FLYING_DIAMOND_IMPACT_8:
15710     case GD_S_TIMEOUT_0:
15711     case GD_S_TIMEOUT_1:
15712     case GD_S_TIMEOUT_2:
15713     case GD_S_TIMEOUT_3:
15714     case GD_S_TIMEOUT_4:
15715     case GD_S_TIMEOUT_5:
15716     case GD_S_TIMEOUT_6:
15717     case GD_S_TIMEOUT_7:
15718     case GD_S_TIMEOUT_8:
15719     case GD_S_TIMEOUT_9:
15720     case GD_S_TIMEOUT_10:
15721     case GD_S_BONUS_LIFE:
15722       // trigger special post-processing (and force sound to be non-looping)
15723       return ACTION_OTHER;
15724
15725     case GD_S_AMOEBA_MAGIC:
15726     case GD_S_FINISHED:
15727       // trigger special post-processing (and force sound to be looping)
15728       return ACTION_DEFAULT;
15729
15730     default:
15731       return ACTION_DEFAULT;
15732   }
15733 }
15734
15735 static int getSoundEffect_BD(int element_bd, int sample)
15736 {
15737   int sound_action = getSoundAction_BD(sample);
15738   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15739   int nr;
15740
15741   // standard sounds
15742   if (sound_action != ACTION_OTHER &&
15743       sound_action != ACTION_DEFAULT)
15744     return sound_effect;
15745
15746   // special post-processing for some sounds
15747   switch (sample)
15748   {
15749     case GD_S_DIAMOND_FALLING_RANDOM:
15750     case GD_S_DIAMOND_FALLING_1:
15751     case GD_S_DIAMOND_FALLING_2:
15752     case GD_S_DIAMOND_FALLING_3:
15753     case GD_S_DIAMOND_FALLING_4:
15754     case GD_S_DIAMOND_FALLING_5:
15755     case GD_S_DIAMOND_FALLING_6:
15756     case GD_S_DIAMOND_FALLING_7:
15757     case GD_S_DIAMOND_FALLING_8:
15758       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15759             sample - GD_S_DIAMOND_FALLING_1);
15760       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15761
15762       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15763         sound_effect = SND_BD_DIAMOND_FALLING;
15764       break;
15765
15766     case GD_S_DIAMOND_IMPACT_RANDOM:
15767     case GD_S_DIAMOND_IMPACT_1:
15768     case GD_S_DIAMOND_IMPACT_2:
15769     case GD_S_DIAMOND_IMPACT_3:
15770     case GD_S_DIAMOND_IMPACT_4:
15771     case GD_S_DIAMOND_IMPACT_5:
15772     case GD_S_DIAMOND_IMPACT_6:
15773     case GD_S_DIAMOND_IMPACT_7:
15774     case GD_S_DIAMOND_IMPACT_8:
15775       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15776             sample - GD_S_DIAMOND_IMPACT_1);
15777       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15778
15779       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15780         sound_effect = SND_BD_DIAMOND_IMPACT;
15781       break;
15782
15783     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15784     case GD_S_FLYING_DIAMOND_FALLING_1:
15785     case GD_S_FLYING_DIAMOND_FALLING_2:
15786     case GD_S_FLYING_DIAMOND_FALLING_3:
15787     case GD_S_FLYING_DIAMOND_FALLING_4:
15788     case GD_S_FLYING_DIAMOND_FALLING_5:
15789     case GD_S_FLYING_DIAMOND_FALLING_6:
15790     case GD_S_FLYING_DIAMOND_FALLING_7:
15791     case GD_S_FLYING_DIAMOND_FALLING_8:
15792       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15793             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15794       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15795
15796       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15797         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15798       break;
15799
15800     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15801     case GD_S_FLYING_DIAMOND_IMPACT_1:
15802     case GD_S_FLYING_DIAMOND_IMPACT_2:
15803     case GD_S_FLYING_DIAMOND_IMPACT_3:
15804     case GD_S_FLYING_DIAMOND_IMPACT_4:
15805     case GD_S_FLYING_DIAMOND_IMPACT_5:
15806     case GD_S_FLYING_DIAMOND_IMPACT_6:
15807     case GD_S_FLYING_DIAMOND_IMPACT_7:
15808     case GD_S_FLYING_DIAMOND_IMPACT_8:
15809       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15810             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15811       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15812
15813       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15814         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15815       break;
15816
15817     case GD_S_TIMEOUT_0:
15818     case GD_S_TIMEOUT_1:
15819     case GD_S_TIMEOUT_2:
15820     case GD_S_TIMEOUT_3:
15821     case GD_S_TIMEOUT_4:
15822     case GD_S_TIMEOUT_5:
15823     case GD_S_TIMEOUT_6:
15824     case GD_S_TIMEOUT_7:
15825     case GD_S_TIMEOUT_8:
15826     case GD_S_TIMEOUT_9:
15827     case GD_S_TIMEOUT_10:
15828       nr = sample - GD_S_TIMEOUT_0;
15829       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15830
15831       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15832         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15833       break;
15834
15835     case GD_S_BONUS_LIFE:
15836       sound_effect = SND_GAME_HEALTH_BONUS;
15837       break;
15838
15839     case GD_S_AMOEBA_MAGIC:
15840       sound_effect = SND_BD_AMOEBA_OTHER;
15841       break;
15842
15843     case GD_S_FINISHED:
15844       sound_effect = SND_GAME_LEVELTIME_BONUS;
15845       break;
15846
15847     default:
15848       sound_effect = SND_UNDEFINED;
15849       break;
15850   }
15851
15852   return sound_effect;
15853 }
15854
15855 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15856 {
15857   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15858   int sound_effect = getSoundEffect_BD(element, sample);
15859   int sound_action = getSoundAction_BD(sample);
15860   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15861   int offset = 0;
15862   int x = xx - offset;
15863   int y = yy - offset;
15864
15865   // some sound actions are always looping in native BD game engine
15866   if (sound_action == ACTION_DEFAULT)
15867     is_loop_sound = TRUE;
15868
15869   // some sound actions are always non-looping in native BD game engine
15870   if (sound_action == ACTION_FALLING ||
15871       sound_action == ACTION_MOVING ||
15872       sound_action == ACTION_OTHER)
15873     is_loop_sound = FALSE;
15874
15875   if (sound_effect != SND_UNDEFINED)
15876     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15877 }
15878
15879 void StopSound_BD(int element_bd, int sample)
15880 {
15881   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15882   int sound_effect = getSoundEffect_BD(element, sample);
15883
15884   if (sound_effect != SND_UNDEFINED)
15885     StopSound(sound_effect);
15886 }
15887
15888 boolean isSoundPlaying_BD(int element_bd, int sample)
15889 {
15890   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15891   int sound_effect = getSoundEffect_BD(element, sample);
15892
15893   if (sound_effect != SND_UNDEFINED)
15894     return isSoundPlaying(sound_effect);
15895
15896   return FALSE;
15897 }
15898
15899 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15900 {
15901   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15902   int offset = 0;
15903   int x = xx - offset;
15904   int y = yy - offset;
15905
15906   switch (sample)
15907   {
15908     case SOUND_blank:
15909       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15910       break;
15911
15912     case SOUND_roll:
15913       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15914       break;
15915
15916     case SOUND_stone:
15917       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15918       break;
15919
15920     case SOUND_nut:
15921       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15922       break;
15923
15924     case SOUND_crack:
15925       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15926       break;
15927
15928     case SOUND_bug:
15929       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15930       break;
15931
15932     case SOUND_tank:
15933       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15934       break;
15935
15936     case SOUND_android_clone:
15937       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15938       break;
15939
15940     case SOUND_android_move:
15941       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15942       break;
15943
15944     case SOUND_spring:
15945       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15946       break;
15947
15948     case SOUND_slurp:
15949       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15950       break;
15951
15952     case SOUND_eater:
15953       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15954       break;
15955
15956     case SOUND_eater_eat:
15957       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15958       break;
15959
15960     case SOUND_alien:
15961       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15962       break;
15963
15964     case SOUND_collect:
15965       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15966       break;
15967
15968     case SOUND_diamond:
15969       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15970       break;
15971
15972     case SOUND_squash:
15973       // !!! CHECK THIS !!!
15974 #if 1
15975       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15976 #else
15977       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15978 #endif
15979       break;
15980
15981     case SOUND_wonderfall:
15982       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15983       break;
15984
15985     case SOUND_drip:
15986       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15987       break;
15988
15989     case SOUND_push:
15990       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15991       break;
15992
15993     case SOUND_dirt:
15994       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15995       break;
15996
15997     case SOUND_acid:
15998       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15999       break;
16000
16001     case SOUND_ball:
16002       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16003       break;
16004
16005     case SOUND_slide:
16006       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16007       break;
16008
16009     case SOUND_wonder:
16010       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16011       break;
16012
16013     case SOUND_door:
16014       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16015       break;
16016
16017     case SOUND_exit_open:
16018       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16019       break;
16020
16021     case SOUND_exit_leave:
16022       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16023       break;
16024
16025     case SOUND_dynamite:
16026       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16027       break;
16028
16029     case SOUND_tick:
16030       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16031       break;
16032
16033     case SOUND_press:
16034       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16035       break;
16036
16037     case SOUND_wheel:
16038       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16039       break;
16040
16041     case SOUND_boom:
16042       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16043       break;
16044
16045     case SOUND_die:
16046       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16047       break;
16048
16049     case SOUND_time:
16050       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16051       break;
16052
16053     default:
16054       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16055       break;
16056   }
16057 }
16058
16059 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16060 {
16061   int element = map_element_SP_to_RND(element_sp);
16062   int action = map_action_SP_to_RND(action_sp);
16063   int offset = (setup.sp_show_border_elements ? 0 : 1);
16064   int x = xx - offset;
16065   int y = yy - offset;
16066
16067   PlayLevelSoundElementAction(x, y, element, action);
16068 }
16069
16070 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16071 {
16072   int element = map_element_MM_to_RND(element_mm);
16073   int action = map_action_MM_to_RND(action_mm);
16074   int offset = 0;
16075   int x = xx - offset;
16076   int y = yy - offset;
16077
16078   if (!IS_MM_ELEMENT(element))
16079     element = EL_MM_DEFAULT;
16080
16081   PlayLevelSoundElementAction(x, y, element, action);
16082 }
16083
16084 void PlaySound_MM(int sound_mm)
16085 {
16086   int sound = map_sound_MM_to_RND(sound_mm);
16087
16088   if (sound == SND_UNDEFINED)
16089     return;
16090
16091   PlaySound(sound);
16092 }
16093
16094 void PlaySoundLoop_MM(int sound_mm)
16095 {
16096   int sound = map_sound_MM_to_RND(sound_mm);
16097
16098   if (sound == SND_UNDEFINED)
16099     return;
16100
16101   PlaySoundLoop(sound);
16102 }
16103
16104 void StopSound_MM(int sound_mm)
16105 {
16106   int sound = map_sound_MM_to_RND(sound_mm);
16107
16108   if (sound == SND_UNDEFINED)
16109     return;
16110
16111   StopSound(sound);
16112 }
16113
16114 void RaiseScore(int value)
16115 {
16116   game.score += value;
16117
16118   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16119
16120   DisplayGameControlValues();
16121 }
16122
16123 void RaiseScoreElement(int element)
16124 {
16125   switch (element)
16126   {
16127     case EL_EMERALD:
16128     case EL_BD_DIAMOND:
16129     case EL_EMERALD_YELLOW:
16130     case EL_EMERALD_RED:
16131     case EL_EMERALD_PURPLE:
16132     case EL_SP_INFOTRON:
16133       RaiseScore(level.score[SC_EMERALD]);
16134       break;
16135     case EL_DIAMOND:
16136       RaiseScore(level.score[SC_DIAMOND]);
16137       break;
16138     case EL_CRYSTAL:
16139       RaiseScore(level.score[SC_CRYSTAL]);
16140       break;
16141     case EL_PEARL:
16142       RaiseScore(level.score[SC_PEARL]);
16143       break;
16144     case EL_BUG:
16145     case EL_BD_BUTTERFLY:
16146     case EL_SP_ELECTRON:
16147       RaiseScore(level.score[SC_BUG]);
16148       break;
16149     case EL_SPACESHIP:
16150     case EL_BD_FIREFLY:
16151     case EL_SP_SNIKSNAK:
16152       RaiseScore(level.score[SC_SPACESHIP]);
16153       break;
16154     case EL_YAMYAM:
16155     case EL_DARK_YAMYAM:
16156       RaiseScore(level.score[SC_YAMYAM]);
16157       break;
16158     case EL_ROBOT:
16159       RaiseScore(level.score[SC_ROBOT]);
16160       break;
16161     case EL_PACMAN:
16162       RaiseScore(level.score[SC_PACMAN]);
16163       break;
16164     case EL_NUT:
16165       RaiseScore(level.score[SC_NUT]);
16166       break;
16167     case EL_DYNAMITE:
16168     case EL_EM_DYNAMITE:
16169     case EL_SP_DISK_RED:
16170     case EL_DYNABOMB_INCREASE_NUMBER:
16171     case EL_DYNABOMB_INCREASE_SIZE:
16172     case EL_DYNABOMB_INCREASE_POWER:
16173       RaiseScore(level.score[SC_DYNAMITE]);
16174       break;
16175     case EL_SHIELD_NORMAL:
16176     case EL_SHIELD_DEADLY:
16177       RaiseScore(level.score[SC_SHIELD]);
16178       break;
16179     case EL_EXTRA_TIME:
16180       RaiseScore(level.extra_time_score);
16181       break;
16182     case EL_KEY_1:
16183     case EL_KEY_2:
16184     case EL_KEY_3:
16185     case EL_KEY_4:
16186     case EL_EM_KEY_1:
16187     case EL_EM_KEY_2:
16188     case EL_EM_KEY_3:
16189     case EL_EM_KEY_4:
16190     case EL_EMC_KEY_5:
16191     case EL_EMC_KEY_6:
16192     case EL_EMC_KEY_7:
16193     case EL_EMC_KEY_8:
16194     case EL_DC_KEY_WHITE:
16195       RaiseScore(level.score[SC_KEY]);
16196       break;
16197     default:
16198       RaiseScore(element_info[element].collect_score);
16199       break;
16200   }
16201 }
16202
16203 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16204 {
16205   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16206   {
16207     if (!quick_quit)
16208     {
16209       // prevent short reactivation of overlay buttons while closing door
16210       SetOverlayActive(FALSE);
16211       UnmapGameButtons();
16212
16213       // door may still be open due to skipped or envelope style request
16214       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16215     }
16216
16217     if (network.enabled)
16218     {
16219       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16220     }
16221     else
16222     {
16223       // when using BD game engine, cover screen before fading out
16224       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16225         game_bd.cover_screen = TRUE;
16226
16227       if (quick_quit)
16228         FadeSkipNextFadeIn();
16229
16230       SetGameStatus(GAME_MODE_MAIN);
16231
16232       DrawMainMenu();
16233     }
16234   }
16235   else          // continue playing the game
16236   {
16237     if (tape.playing && tape.deactivate_display)
16238       TapeDeactivateDisplayOff(TRUE);
16239
16240     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16241
16242     if (tape.playing && tape.deactivate_display)
16243       TapeDeactivateDisplayOn();
16244   }
16245 }
16246
16247 void RequestQuitGame(boolean escape_key_pressed)
16248 {
16249   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16250   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16251                         level_editor_test_game);
16252   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16253                           quick_quit || score_info_tape_play);
16254
16255   RequestQuitGameExt(skip_request, quick_quit,
16256                      "Do you really want to quit the game?");
16257 }
16258
16259 static char *getRestartGameMessage(void)
16260 {
16261   boolean play_again = hasStartedNetworkGame();
16262   static char message[MAX_OUTPUT_LINESIZE];
16263   char *game_over_text = "Game over!";
16264   char *play_again_text = " Play it again?";
16265
16266   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16267       game_mm.game_over_message != NULL)
16268     game_over_text = game_mm.game_over_message;
16269
16270   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16271            (play_again ? play_again_text : ""));
16272
16273   return message;
16274 }
16275
16276 static void RequestRestartGame(void)
16277 {
16278   char *message = getRestartGameMessage();
16279   boolean has_started_game = hasStartedNetworkGame();
16280   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16281   int door_state = DOOR_CLOSE_1;
16282
16283   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
16284   {
16285     CloseDoor(door_state);
16286
16287     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16288   }
16289   else
16290   {
16291     // if game was invoked from level editor, also close tape recorder door
16292     if (level_editor_test_game)
16293       door_state = DOOR_CLOSE_ALL;
16294
16295     CloseDoor(door_state);
16296
16297     SetGameStatus(GAME_MODE_MAIN);
16298
16299     DrawMainMenu();
16300   }
16301 }
16302
16303 boolean CheckRestartGame(void)
16304 {
16305   static int game_over_delay = 0;
16306   int game_over_delay_value = 50;
16307   boolean game_over = checkGameFailed();
16308
16309   if (!game_over)
16310   {
16311     game_over_delay = game_over_delay_value;
16312
16313     return FALSE;
16314   }
16315
16316   if (game_over_delay > 0)
16317   {
16318     if (game_over_delay == game_over_delay_value / 2)
16319       PlaySound(SND_GAME_LOSING);
16320
16321     game_over_delay--;
16322
16323     return FALSE;
16324   }
16325
16326   // do not ask to play again if request dialog is already active
16327   if (game.request_active)
16328     return FALSE;
16329
16330   // do not ask to play again if request dialog already handled
16331   if (game.RestartGameRequested)
16332     return FALSE;
16333
16334   // do not ask to play again if game was never actually played
16335   if (!game.GamePlayed)
16336     return FALSE;
16337
16338   // do not ask to play again if this was disabled in setup menu
16339   if (!setup.ask_on_game_over)
16340     return FALSE;
16341
16342   game.RestartGameRequested = TRUE;
16343
16344   RequestRestartGame();
16345
16346   return TRUE;
16347 }
16348
16349 boolean checkGameRunning(void)
16350 {
16351   if (game_status != GAME_MODE_PLAYING)
16352     return FALSE;
16353
16354   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16355     return FALSE;
16356
16357   return TRUE;
16358 }
16359
16360 boolean checkGamePlaying(void)
16361 {
16362   if (game_status != GAME_MODE_PLAYING)
16363     return FALSE;
16364
16365   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16366     return FALSE;
16367
16368   return TRUE;
16369 }
16370
16371 boolean checkGameSolved(void)
16372 {
16373   // set for all game engines if level was solved
16374   return game.LevelSolved_GameEnd;
16375 }
16376
16377 boolean checkGameFailed(void)
16378 {
16379   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16380     return (game_bd.game_over && !game_bd.level_solved);
16381   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16382     return (game_em.game_over && !game_em.level_solved);
16383   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16384     return (game_sp.game_over && !game_sp.level_solved);
16385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16386     return (game_mm.game_over && !game_mm.level_solved);
16387   else                          // GAME_ENGINE_TYPE_RND
16388     return (game.GameOver && !game.LevelSolved);
16389 }
16390
16391 boolean checkGameEnded(void)
16392 {
16393   return (checkGameSolved() || checkGameFailed());
16394 }
16395
16396
16397 // ----------------------------------------------------------------------------
16398 // random generator functions
16399 // ----------------------------------------------------------------------------
16400
16401 unsigned int InitEngineRandom_RND(int seed)
16402 {
16403   game.num_random_calls = 0;
16404
16405   return InitEngineRandom(seed);
16406 }
16407
16408 unsigned int RND(int max)
16409 {
16410   if (max > 0)
16411   {
16412     game.num_random_calls++;
16413
16414     return GetEngineRandom(max);
16415   }
16416
16417   return 0;
16418 }
16419
16420
16421 // ----------------------------------------------------------------------------
16422 // game engine snapshot handling functions
16423 // ----------------------------------------------------------------------------
16424
16425 struct EngineSnapshotInfo
16426 {
16427   // runtime values for custom element collect score
16428   int collect_score[NUM_CUSTOM_ELEMENTS];
16429
16430   // runtime values for group element choice position
16431   int choice_pos[NUM_GROUP_ELEMENTS];
16432
16433   // runtime values for belt position animations
16434   int belt_graphic[4][NUM_BELT_PARTS];
16435   int belt_anim_mode[4][NUM_BELT_PARTS];
16436 };
16437
16438 static struct EngineSnapshotInfo engine_snapshot_rnd;
16439 static char *snapshot_level_identifier = NULL;
16440 static int snapshot_level_nr = -1;
16441
16442 static void SaveEngineSnapshotValues_RND(void)
16443 {
16444   static int belt_base_active_element[4] =
16445   {
16446     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16447     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16448     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16449     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16450   };
16451   int i, j;
16452
16453   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16454   {
16455     int element = EL_CUSTOM_START + i;
16456
16457     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16458   }
16459
16460   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16461   {
16462     int element = EL_GROUP_START + i;
16463
16464     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16465   }
16466
16467   for (i = 0; i < 4; i++)
16468   {
16469     for (j = 0; j < NUM_BELT_PARTS; j++)
16470     {
16471       int element = belt_base_active_element[i] + j;
16472       int graphic = el2img(element);
16473       int anim_mode = graphic_info[graphic].anim_mode;
16474
16475       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16476       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16477     }
16478   }
16479 }
16480
16481 static void LoadEngineSnapshotValues_RND(void)
16482 {
16483   unsigned int num_random_calls = game.num_random_calls;
16484   int i, j;
16485
16486   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16487   {
16488     int element = EL_CUSTOM_START + i;
16489
16490     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16491   }
16492
16493   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16494   {
16495     int element = EL_GROUP_START + i;
16496
16497     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16498   }
16499
16500   for (i = 0; i < 4; i++)
16501   {
16502     for (j = 0; j < NUM_BELT_PARTS; j++)
16503     {
16504       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16505       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16506
16507       graphic_info[graphic].anim_mode = anim_mode;
16508     }
16509   }
16510
16511   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16512   {
16513     InitRND(tape.random_seed);
16514     for (i = 0; i < num_random_calls; i++)
16515       RND(1);
16516   }
16517
16518   if (game.num_random_calls != num_random_calls)
16519   {
16520     Error("number of random calls out of sync");
16521     Error("number of random calls should be %d", num_random_calls);
16522     Error("number of random calls is %d", game.num_random_calls);
16523
16524     Fail("this should not happen -- please debug");
16525   }
16526 }
16527
16528 void FreeEngineSnapshotSingle(void)
16529 {
16530   FreeSnapshotSingle();
16531
16532   setString(&snapshot_level_identifier, NULL);
16533   snapshot_level_nr = -1;
16534 }
16535
16536 void FreeEngineSnapshotList(void)
16537 {
16538   FreeSnapshotList();
16539 }
16540
16541 static ListNode *SaveEngineSnapshotBuffers(void)
16542 {
16543   ListNode *buffers = NULL;
16544
16545   // copy some special values to a structure better suited for the snapshot
16546
16547   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16548     SaveEngineSnapshotValues_RND();
16549   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16550     SaveEngineSnapshotValues_EM();
16551   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16552     SaveEngineSnapshotValues_SP(&buffers);
16553   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16554     SaveEngineSnapshotValues_MM();
16555
16556   // save values stored in special snapshot structure
16557
16558   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16559     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16560   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16561     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16562   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16563     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16564   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16565     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16566
16567   // save further RND engine values
16568
16569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16572
16573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16576   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16578   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16579
16580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16583
16584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16585
16586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16588
16589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16600   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16607
16608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16610
16611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16614
16615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16617
16618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16621   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16624
16625   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16626   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16627
16628 #if 0
16629   ListNode *node = engine_snapshot_list_rnd;
16630   int num_bytes = 0;
16631
16632   while (node != NULL)
16633   {
16634     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16635
16636     node = node->next;
16637   }
16638
16639   Debug("game:playing:SaveEngineSnapshotBuffers",
16640         "size of engine snapshot: %d bytes", num_bytes);
16641 #endif
16642
16643   return buffers;
16644 }
16645
16646 void SaveEngineSnapshotSingle(void)
16647 {
16648   ListNode *buffers = SaveEngineSnapshotBuffers();
16649
16650   // finally save all snapshot buffers to single snapshot
16651   SaveSnapshotSingle(buffers);
16652
16653   // save level identification information
16654   setString(&snapshot_level_identifier, leveldir_current->identifier);
16655   snapshot_level_nr = level_nr;
16656 }
16657
16658 boolean CheckSaveEngineSnapshotToList(void)
16659 {
16660   boolean save_snapshot =
16661     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16662      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16663       game.snapshot.changed_action) ||
16664      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16665       game.snapshot.collected_item));
16666
16667   game.snapshot.changed_action = FALSE;
16668   game.snapshot.collected_item = FALSE;
16669   game.snapshot.save_snapshot = save_snapshot;
16670
16671   return save_snapshot;
16672 }
16673
16674 void SaveEngineSnapshotToList(void)
16675 {
16676   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16677       tape.quick_resume)
16678     return;
16679
16680   ListNode *buffers = SaveEngineSnapshotBuffers();
16681
16682   // finally save all snapshot buffers to snapshot list
16683   SaveSnapshotToList(buffers);
16684 }
16685
16686 void SaveEngineSnapshotToListInitial(void)
16687 {
16688   FreeEngineSnapshotList();
16689
16690   SaveEngineSnapshotToList();
16691 }
16692
16693 static void LoadEngineSnapshotValues(void)
16694 {
16695   // restore special values from snapshot structure
16696
16697   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16698     LoadEngineSnapshotValues_RND();
16699   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16700     LoadEngineSnapshotValues_EM();
16701   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16702     LoadEngineSnapshotValues_SP();
16703   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16704     LoadEngineSnapshotValues_MM();
16705 }
16706
16707 void LoadEngineSnapshotSingle(void)
16708 {
16709   LoadSnapshotSingle();
16710
16711   LoadEngineSnapshotValues();
16712 }
16713
16714 static void LoadEngineSnapshot_Undo(int steps)
16715 {
16716   LoadSnapshotFromList_Older(steps);
16717
16718   LoadEngineSnapshotValues();
16719 }
16720
16721 static void LoadEngineSnapshot_Redo(int steps)
16722 {
16723   LoadSnapshotFromList_Newer(steps);
16724
16725   LoadEngineSnapshotValues();
16726 }
16727
16728 boolean CheckEngineSnapshotSingle(void)
16729 {
16730   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16731           snapshot_level_nr == level_nr);
16732 }
16733
16734 boolean CheckEngineSnapshotList(void)
16735 {
16736   return CheckSnapshotList();
16737 }
16738
16739
16740 // ---------- new game button stuff -------------------------------------------
16741
16742 static struct
16743 {
16744   int graphic;
16745   struct XY *pos;
16746   int gadget_id;
16747   boolean *setup_value;
16748   boolean allowed_on_tape;
16749   boolean is_touch_button;
16750   char *infotext;
16751 } gamebutton_info[NUM_GAME_BUTTONS] =
16752 {
16753   {
16754     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16755     GAME_CTRL_ID_STOP,                          NULL,
16756     TRUE, FALSE,                                "stop game"
16757   },
16758   {
16759     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16760     GAME_CTRL_ID_PAUSE,                         NULL,
16761     TRUE, FALSE,                                "pause game"
16762   },
16763   {
16764     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16765     GAME_CTRL_ID_PLAY,                          NULL,
16766     TRUE, FALSE,                                "play game"
16767   },
16768   {
16769     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16770     GAME_CTRL_ID_UNDO,                          NULL,
16771     TRUE, FALSE,                                "undo step"
16772   },
16773   {
16774     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16775     GAME_CTRL_ID_REDO,                          NULL,
16776     TRUE, FALSE,                                "redo step"
16777   },
16778   {
16779     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16780     GAME_CTRL_ID_SAVE,                          NULL,
16781     TRUE, FALSE,                                "save game"
16782   },
16783   {
16784     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16785     GAME_CTRL_ID_PAUSE2,                        NULL,
16786     TRUE, FALSE,                                "pause game"
16787   },
16788   {
16789     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16790     GAME_CTRL_ID_LOAD,                          NULL,
16791     TRUE, FALSE,                                "load game"
16792   },
16793   {
16794     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16795     GAME_CTRL_ID_RESTART,                       NULL,
16796     TRUE, FALSE,                                "restart game"
16797   },
16798   {
16799     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16800     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16801     FALSE, FALSE,                               "stop game"
16802   },
16803   {
16804     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16805     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16806     FALSE, FALSE,                               "pause game"
16807   },
16808   {
16809     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16810     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16811     FALSE, FALSE,                               "play game"
16812   },
16813   {
16814     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16815     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16816     FALSE, FALSE,                               "restart game"
16817   },
16818   {
16819     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16820     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16821     FALSE, TRUE,                                "stop game"
16822   },
16823   {
16824     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16825     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16826     FALSE, TRUE,                                "pause game"
16827   },
16828   {
16829     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16830     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16831     FALSE, TRUE,                                "restart game"
16832   },
16833   {
16834     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16835     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16836     TRUE, FALSE,                                "background music on/off"
16837   },
16838   {
16839     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16840     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16841     TRUE, FALSE,                                "sound loops on/off"
16842   },
16843   {
16844     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16845     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16846     TRUE, FALSE,                                "normal sounds on/off"
16847   },
16848   {
16849     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16850     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16851     FALSE, FALSE,                               "background music on/off"
16852   },
16853   {
16854     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16855     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16856     FALSE, FALSE,                               "sound loops on/off"
16857   },
16858   {
16859     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16860     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16861     FALSE, FALSE,                               "normal sounds on/off"
16862   }
16863 };
16864
16865 void CreateGameButtons(void)
16866 {
16867   int i;
16868
16869   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870   {
16871     int graphic = gamebutton_info[i].graphic;
16872     struct GraphicInfo *gfx = &graphic_info[graphic];
16873     struct XY *pos = gamebutton_info[i].pos;
16874     struct GadgetInfo *gi;
16875     int button_type;
16876     boolean checked;
16877     unsigned int event_mask;
16878     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16879     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16880     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16881     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16882     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16883     int gd_x   = gfx->src_x;
16884     int gd_y   = gfx->src_y;
16885     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16886     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16887     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16888     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16889     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16890     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16891     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16892     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16893     int id = i;
16894
16895     // do not use touch buttons if overlay touch buttons are disabled
16896     if (is_touch_button && !setup.touch.overlay_buttons)
16897       continue;
16898
16899     if (gfx->bitmap == NULL)
16900     {
16901       game_gadget[id] = NULL;
16902
16903       continue;
16904     }
16905
16906     if (id == GAME_CTRL_ID_STOP ||
16907         id == GAME_CTRL_ID_PANEL_STOP ||
16908         id == GAME_CTRL_ID_TOUCH_STOP ||
16909         id == GAME_CTRL_ID_PLAY ||
16910         id == GAME_CTRL_ID_PANEL_PLAY ||
16911         id == GAME_CTRL_ID_SAVE ||
16912         id == GAME_CTRL_ID_LOAD ||
16913         id == GAME_CTRL_ID_RESTART ||
16914         id == GAME_CTRL_ID_PANEL_RESTART ||
16915         id == GAME_CTRL_ID_TOUCH_RESTART)
16916     {
16917       button_type = GD_TYPE_NORMAL_BUTTON;
16918       checked = FALSE;
16919       event_mask = GD_EVENT_RELEASED;
16920     }
16921     else if (id == GAME_CTRL_ID_UNDO ||
16922              id == GAME_CTRL_ID_REDO)
16923     {
16924       button_type = GD_TYPE_NORMAL_BUTTON;
16925       checked = FALSE;
16926       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16927     }
16928     else
16929     {
16930       button_type = GD_TYPE_CHECK_BUTTON;
16931       checked = (gamebutton_info[i].setup_value != NULL ?
16932                  *gamebutton_info[i].setup_value : FALSE);
16933       event_mask = GD_EVENT_PRESSED;
16934     }
16935
16936     gi = CreateGadget(GDI_CUSTOM_ID, id,
16937                       GDI_IMAGE_ID, graphic,
16938                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16939                       GDI_X, base_x + x,
16940                       GDI_Y, base_y + y,
16941                       GDI_WIDTH, gfx->width,
16942                       GDI_HEIGHT, gfx->height,
16943                       GDI_TYPE, button_type,
16944                       GDI_STATE, GD_BUTTON_UNPRESSED,
16945                       GDI_CHECKED, checked,
16946                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16947                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16948                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16949                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16950                       GDI_DIRECT_DRAW, FALSE,
16951                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16952                       GDI_EVENT_MASK, event_mask,
16953                       GDI_CALLBACK_ACTION, HandleGameButtons,
16954                       GDI_END);
16955
16956     if (gi == NULL)
16957       Fail("cannot create gadget");
16958
16959     game_gadget[id] = gi;
16960   }
16961 }
16962
16963 void FreeGameButtons(void)
16964 {
16965   int i;
16966
16967   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16968     FreeGadget(game_gadget[i]);
16969 }
16970
16971 static void UnmapGameButtonsAtSamePosition(int id)
16972 {
16973   int i;
16974
16975   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16976     if (i != id &&
16977         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16978         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16979       UnmapGadget(game_gadget[i]);
16980 }
16981
16982 static void UnmapGameButtonsAtSamePosition_All(void)
16983 {
16984   if (setup.show_load_save_buttons)
16985   {
16986     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16987     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16988     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16989   }
16990   else if (setup.show_undo_redo_buttons)
16991   {
16992     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16993     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16994     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16995   }
16996   else
16997   {
16998     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16999     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
17000     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
17001
17002     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
17003     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
17004     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
17005   }
17006 }
17007
17008 void MapLoadSaveButtons(void)
17009 {
17010   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17011   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17012
17013   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17014   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17015 }
17016
17017 void MapUndoRedoButtons(void)
17018 {
17019   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17020   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17021
17022   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17023   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17024 }
17025
17026 void ModifyPauseButtons(void)
17027 {
17028   static int ids[] =
17029   {
17030     GAME_CTRL_ID_PAUSE,
17031     GAME_CTRL_ID_PAUSE2,
17032     GAME_CTRL_ID_PANEL_PAUSE,
17033     GAME_CTRL_ID_TOUCH_PAUSE,
17034     -1
17035   };
17036   int i;
17037
17038   // do not redraw pause button on closed door (may happen when restarting game)
17039   if (!(GetDoorState() & DOOR_OPEN_1))
17040     return;
17041
17042   for (i = 0; ids[i] > -1; i++)
17043     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17044 }
17045
17046 static void MapGameButtonsExt(boolean on_tape)
17047 {
17048   int i;
17049
17050   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17051   {
17052     if ((i == GAME_CTRL_ID_UNDO ||
17053          i == GAME_CTRL_ID_REDO) &&
17054         game_status != GAME_MODE_PLAYING)
17055       continue;
17056
17057     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17058       MapGadget(game_gadget[i]);
17059   }
17060
17061   UnmapGameButtonsAtSamePosition_All();
17062
17063   RedrawGameButtons();
17064 }
17065
17066 static void UnmapGameButtonsExt(boolean on_tape)
17067 {
17068   int i;
17069
17070   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17071     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17072       UnmapGadget(game_gadget[i]);
17073 }
17074
17075 static void RedrawGameButtonsExt(boolean on_tape)
17076 {
17077   int i;
17078
17079   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17080     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17081       RedrawGadget(game_gadget[i]);
17082 }
17083
17084 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17085 {
17086   if (gi == NULL)
17087     return;
17088
17089   gi->checked = state;
17090 }
17091
17092 static void RedrawSoundButtonGadget(int id)
17093 {
17094   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17095              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17096              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17097              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17098              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17099              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17100              id);
17101
17102   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17103   RedrawGadget(game_gadget[id2]);
17104 }
17105
17106 void MapGameButtons(void)
17107 {
17108   MapGameButtonsExt(FALSE);
17109 }
17110
17111 void UnmapGameButtons(void)
17112 {
17113   UnmapGameButtonsExt(FALSE);
17114 }
17115
17116 void RedrawGameButtons(void)
17117 {
17118   RedrawGameButtonsExt(FALSE);
17119 }
17120
17121 void MapGameButtonsOnTape(void)
17122 {
17123   MapGameButtonsExt(TRUE);
17124 }
17125
17126 void UnmapGameButtonsOnTape(void)
17127 {
17128   UnmapGameButtonsExt(TRUE);
17129 }
17130
17131 void RedrawGameButtonsOnTape(void)
17132 {
17133   RedrawGameButtonsExt(TRUE);
17134 }
17135
17136 static void GameUndoRedoExt(void)
17137 {
17138   ClearPlayerAction();
17139
17140   tape.pausing = TRUE;
17141
17142   RedrawPlayfield();
17143   UpdateAndDisplayGameControlValues();
17144
17145   DrawCompleteVideoDisplay();
17146   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17147   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17148   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17149
17150   ModifyPauseButtons();
17151
17152   BackToFront();
17153 }
17154
17155 static void GameUndo(int steps)
17156 {
17157   if (!CheckEngineSnapshotList())
17158     return;
17159
17160   int tape_property_bits = tape.property_bits;
17161
17162   LoadEngineSnapshot_Undo(steps);
17163
17164   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17165
17166   GameUndoRedoExt();
17167 }
17168
17169 static void GameRedo(int steps)
17170 {
17171   if (!CheckEngineSnapshotList())
17172     return;
17173
17174   int tape_property_bits = tape.property_bits;
17175
17176   LoadEngineSnapshot_Redo(steps);
17177
17178   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17179
17180   GameUndoRedoExt();
17181 }
17182
17183 static void HandleGameButtonsExt(int id, int button)
17184 {
17185   static boolean game_undo_executed = FALSE;
17186   int steps = BUTTON_STEPSIZE(button);
17187   boolean handle_game_buttons =
17188     (game_status == GAME_MODE_PLAYING ||
17189      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17190
17191   if (!handle_game_buttons)
17192     return;
17193
17194   switch (id)
17195   {
17196     case GAME_CTRL_ID_STOP:
17197     case GAME_CTRL_ID_PANEL_STOP:
17198     case GAME_CTRL_ID_TOUCH_STOP:
17199       TapeStopGame();
17200
17201       break;
17202
17203     case GAME_CTRL_ID_PAUSE:
17204     case GAME_CTRL_ID_PAUSE2:
17205     case GAME_CTRL_ID_PANEL_PAUSE:
17206     case GAME_CTRL_ID_TOUCH_PAUSE:
17207       if (network.enabled && game_status == GAME_MODE_PLAYING)
17208       {
17209         if (tape.pausing)
17210           SendToServer_ContinuePlaying();
17211         else
17212           SendToServer_PausePlaying();
17213       }
17214       else
17215         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17216
17217       game_undo_executed = FALSE;
17218
17219       break;
17220
17221     case GAME_CTRL_ID_PLAY:
17222     case GAME_CTRL_ID_PANEL_PLAY:
17223       if (game_status == GAME_MODE_MAIN)
17224       {
17225         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17226       }
17227       else if (tape.pausing)
17228       {
17229         if (network.enabled)
17230           SendToServer_ContinuePlaying();
17231         else
17232           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17233       }
17234       break;
17235
17236     case GAME_CTRL_ID_UNDO:
17237       // Important: When using "save snapshot when collecting an item" mode,
17238       // load last (current) snapshot for first "undo" after pressing "pause"
17239       // (else the last-but-one snapshot would be loaded, because the snapshot
17240       // pointer already points to the last snapshot when pressing "pause",
17241       // which is fine for "every step/move" mode, but not for "every collect")
17242       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17243           !game_undo_executed)
17244         steps--;
17245
17246       game_undo_executed = TRUE;
17247
17248       GameUndo(steps);
17249       break;
17250
17251     case GAME_CTRL_ID_REDO:
17252       GameRedo(steps);
17253       break;
17254
17255     case GAME_CTRL_ID_SAVE:
17256       TapeQuickSave();
17257       break;
17258
17259     case GAME_CTRL_ID_LOAD:
17260       TapeQuickLoad();
17261       break;
17262
17263     case GAME_CTRL_ID_RESTART:
17264     case GAME_CTRL_ID_PANEL_RESTART:
17265     case GAME_CTRL_ID_TOUCH_RESTART:
17266       TapeRestartGame();
17267
17268       break;
17269
17270     case SOUND_CTRL_ID_MUSIC:
17271     case SOUND_CTRL_ID_PANEL_MUSIC:
17272       if (setup.sound_music)
17273       { 
17274         setup.sound_music = FALSE;
17275
17276         FadeMusic();
17277       }
17278       else if (audio.music_available)
17279       { 
17280         setup.sound = setup.sound_music = TRUE;
17281
17282         SetAudioMode(setup.sound);
17283
17284         if (game_status == GAME_MODE_PLAYING)
17285           PlayLevelMusic();
17286       }
17287
17288       RedrawSoundButtonGadget(id);
17289
17290       break;
17291
17292     case SOUND_CTRL_ID_LOOPS:
17293     case SOUND_CTRL_ID_PANEL_LOOPS:
17294       if (setup.sound_loops)
17295         setup.sound_loops = FALSE;
17296       else if (audio.loops_available)
17297       {
17298         setup.sound = setup.sound_loops = TRUE;
17299
17300         SetAudioMode(setup.sound);
17301       }
17302
17303       RedrawSoundButtonGadget(id);
17304
17305       break;
17306
17307     case SOUND_CTRL_ID_SIMPLE:
17308     case SOUND_CTRL_ID_PANEL_SIMPLE:
17309       if (setup.sound_simple)
17310         setup.sound_simple = FALSE;
17311       else if (audio.sound_available)
17312       {
17313         setup.sound = setup.sound_simple = TRUE;
17314
17315         SetAudioMode(setup.sound);
17316       }
17317
17318       RedrawSoundButtonGadget(id);
17319
17320       break;
17321
17322     default:
17323       break;
17324   }
17325 }
17326
17327 static void HandleGameButtons(struct GadgetInfo *gi)
17328 {
17329   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17330 }
17331
17332 void HandleSoundButtonKeys(Key key)
17333 {
17334   if (key == setup.shortcut.sound_simple)
17335     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17336   else if (key == setup.shortcut.sound_loops)
17337     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17338   else if (key == setup.shortcut.sound_music)
17339     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17340 }