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