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