fixed replicator animation definition for native BD engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_GEMS_NEEDED                  2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_NEEDED,
250     &game.panel.gems_needed,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void InitFieldForEngine_RND(int x, int y)
1839 {
1840   int element = Tile[x][y];
1841
1842   // convert BD engine elements to corresponding R'n'D engine elements
1843   element = (element == EL_BD_EMPTY             ? EL_EMPTY :
1844              element == EL_BD_PLAYER            ? EL_PLAYER_1 :
1845              element == EL_BD_INBOX             ? EL_PLAYER_1 :
1846              element == EL_BD_SAND              ? EL_SAND :
1847              element == EL_BD_STEELWALL         ? EL_STEELWALL :
1848              element == EL_BD_EXIT_CLOSED       ? EL_EXIT_CLOSED :
1849              element == EL_BD_EXIT_OPEN         ? EL_EXIT_OPEN :
1850              element);
1851
1852   Tile[x][y] = element;
1853 }
1854
1855 static void InitFieldForEngine(int x, int y)
1856 {
1857   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1858     InitFieldForEngine_RND(x, y);
1859 }
1860
1861 static void InitField(int x, int y, boolean init_game)
1862 {
1863   int element = Tile[x][y];
1864
1865   switch (element)
1866   {
1867     case EL_SP_MURPHY:
1868     case EL_PLAYER_1:
1869     case EL_PLAYER_2:
1870     case EL_PLAYER_3:
1871     case EL_PLAYER_4:
1872       InitPlayerField(x, y, element, init_game);
1873       break;
1874
1875     case EL_SOKOBAN_FIELD_PLAYER:
1876       element = Tile[x][y] = EL_PLAYER_1;
1877       InitField(x, y, init_game);
1878
1879       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1880       InitField(x, y, init_game);
1881       break;
1882
1883     case EL_SOKOBAN_FIELD_EMPTY:
1884       IncrementSokobanFieldsNeeded();
1885       break;
1886
1887     case EL_SOKOBAN_OBJECT:
1888       IncrementSokobanObjectsNeeded();
1889       break;
1890
1891     case EL_STONEBLOCK:
1892       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1893         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1894       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1895         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1896       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1897         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1898       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1899         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1900       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1901         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1902       break;
1903
1904     case EL_BUG:
1905     case EL_BUG_RIGHT:
1906     case EL_BUG_UP:
1907     case EL_BUG_LEFT:
1908     case EL_BUG_DOWN:
1909     case EL_SPACESHIP:
1910     case EL_SPACESHIP_RIGHT:
1911     case EL_SPACESHIP_UP:
1912     case EL_SPACESHIP_LEFT:
1913     case EL_SPACESHIP_DOWN:
1914     case EL_BD_BUTTERFLY:
1915     case EL_BD_BUTTERFLY_RIGHT:
1916     case EL_BD_BUTTERFLY_UP:
1917     case EL_BD_BUTTERFLY_LEFT:
1918     case EL_BD_BUTTERFLY_DOWN:
1919     case EL_BD_FIREFLY:
1920     case EL_BD_FIREFLY_RIGHT:
1921     case EL_BD_FIREFLY_UP:
1922     case EL_BD_FIREFLY_LEFT:
1923     case EL_BD_FIREFLY_DOWN:
1924     case EL_PACMAN_RIGHT:
1925     case EL_PACMAN_UP:
1926     case EL_PACMAN_LEFT:
1927     case EL_PACMAN_DOWN:
1928     case EL_YAMYAM:
1929     case EL_YAMYAM_LEFT:
1930     case EL_YAMYAM_RIGHT:
1931     case EL_YAMYAM_UP:
1932     case EL_YAMYAM_DOWN:
1933     case EL_DARK_YAMYAM:
1934     case EL_ROBOT:
1935     case EL_PACMAN:
1936     case EL_SP_SNIKSNAK:
1937     case EL_SP_ELECTRON:
1938     case EL_MOLE:
1939     case EL_MOLE_LEFT:
1940     case EL_MOLE_RIGHT:
1941     case EL_MOLE_UP:
1942     case EL_MOLE_DOWN:
1943     case EL_SPRING_LEFT:
1944     case EL_SPRING_RIGHT:
1945       InitMovDir(x, y);
1946       break;
1947
1948     case EL_AMOEBA_FULL:
1949     case EL_BD_AMOEBA:
1950       InitAmoebaNr(x, y);
1951       break;
1952
1953     case EL_AMOEBA_DROP:
1954       if (y == lev_fieldy - 1)
1955       {
1956         Tile[x][y] = EL_AMOEBA_GROWING;
1957         Store[x][y] = EL_AMOEBA_WET;
1958       }
1959       break;
1960
1961     case EL_DYNAMITE_ACTIVE:
1962     case EL_SP_DISK_RED_ACTIVE:
1963     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1964     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1965     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1966     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1967       MovDelay[x][y] = 96;
1968       break;
1969
1970     case EL_EM_DYNAMITE_ACTIVE:
1971       MovDelay[x][y] = 32;
1972       break;
1973
1974     case EL_LAMP:
1975       game.lights_still_needed++;
1976       break;
1977
1978     case EL_PENGUIN:
1979       game.friends_still_needed++;
1980       break;
1981
1982     case EL_PIG:
1983     case EL_DRAGON:
1984       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1985       break;
1986
1987     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1988     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1989     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1990     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1991     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1992     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1993     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1994     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1995     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1996     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1997     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1998     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1999       if (init_game)
2000       {
2001         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2002         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2003         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2004
2005         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2006         {
2007           game.belt_dir[belt_nr] = belt_dir;
2008           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2009         }
2010         else    // more than one switch -- set it like the first switch
2011         {
2012           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2013         }
2014       }
2015       break;
2016
2017     case EL_LIGHT_SWITCH_ACTIVE:
2018       if (init_game)
2019         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2020       break;
2021
2022     case EL_INVISIBLE_STEELWALL:
2023     case EL_INVISIBLE_WALL:
2024     case EL_INVISIBLE_SAND:
2025       if (game.light_time_left > 0 ||
2026           game.lenses_time_left > 0)
2027         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2028       break;
2029
2030     case EL_EMC_MAGIC_BALL:
2031       if (game.ball_active)
2032         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2033       break;
2034
2035     case EL_EMC_MAGIC_BALL_SWITCH:
2036       if (game.ball_active)
2037         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2038       break;
2039
2040     case EL_TRIGGER_PLAYER:
2041     case EL_TRIGGER_ELEMENT:
2042     case EL_TRIGGER_CE_VALUE:
2043     case EL_TRIGGER_CE_SCORE:
2044     case EL_SELF:
2045     case EL_ANY_ELEMENT:
2046     case EL_CURRENT_CE_VALUE:
2047     case EL_CURRENT_CE_SCORE:
2048     case EL_PREV_CE_1:
2049     case EL_PREV_CE_2:
2050     case EL_PREV_CE_3:
2051     case EL_PREV_CE_4:
2052     case EL_PREV_CE_5:
2053     case EL_PREV_CE_6:
2054     case EL_PREV_CE_7:
2055     case EL_PREV_CE_8:
2056     case EL_NEXT_CE_1:
2057     case EL_NEXT_CE_2:
2058     case EL_NEXT_CE_3:
2059     case EL_NEXT_CE_4:
2060     case EL_NEXT_CE_5:
2061     case EL_NEXT_CE_6:
2062     case EL_NEXT_CE_7:
2063     case EL_NEXT_CE_8:
2064       // reference elements should not be used on the playfield
2065       Tile[x][y] = EL_EMPTY;
2066       break;
2067
2068     default:
2069       if (IS_CUSTOM_ELEMENT(element))
2070       {
2071         if (CAN_MOVE(element))
2072           InitMovDir(x, y);
2073
2074         if (!element_info[element].use_last_ce_value || init_game)
2075           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2076       }
2077       else if (IS_GROUP_ELEMENT(element))
2078       {
2079         Tile[x][y] = GetElementFromGroupElement(element);
2080
2081         InitField(x, y, init_game);
2082       }
2083       else if (IS_EMPTY_ELEMENT(element))
2084       {
2085         GfxElementEmpty[x][y] = element;
2086         Tile[x][y] = EL_EMPTY;
2087
2088         if (element_info[element].use_gfx_element)
2089           game.use_masked_elements = TRUE;
2090       }
2091
2092       break;
2093   }
2094
2095   if (!init_game)
2096     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2097 }
2098
2099 static void InitField_WithBug1(int x, int y, boolean init_game)
2100 {
2101   InitField(x, y, init_game);
2102
2103   // not needed to call InitMovDir() -- already done by InitField()!
2104   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2105       CAN_MOVE(Tile[x][y]))
2106     InitMovDir(x, y);
2107 }
2108
2109 static void InitField_WithBug2(int x, int y, boolean init_game)
2110 {
2111   int old_element = Tile[x][y];
2112
2113   InitField(x, y, init_game);
2114
2115   // not needed to call InitMovDir() -- already done by InitField()!
2116   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2117       CAN_MOVE(old_element) &&
2118       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2119     InitMovDir(x, y);
2120
2121   /* this case is in fact a combination of not less than three bugs:
2122      first, it calls InitMovDir() for elements that can move, although this is
2123      already done by InitField(); then, it checks the element that was at this
2124      field _before_ the call to InitField() (which can change it); lastly, it
2125      was not called for "mole with direction" elements, which were treated as
2126      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2127   */
2128 }
2129
2130 static int get_key_element_from_nr(int key_nr)
2131 {
2132   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2133                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                           EL_EM_KEY_1 : EL_KEY_1);
2135
2136   return key_base_element + key_nr;
2137 }
2138
2139 static int get_next_dropped_element(struct PlayerInfo *player)
2140 {
2141   return (player->inventory_size > 0 ?
2142           player->inventory_element[player->inventory_size - 1] :
2143           player->inventory_infinite_element != EL_UNDEFINED ?
2144           player->inventory_infinite_element :
2145           player->dynabombs_left > 0 ?
2146           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2147           EL_UNDEFINED);
2148 }
2149
2150 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2151 {
2152   // pos >= 0: get element from bottom of the stack;
2153   // pos <  0: get element from top of the stack
2154
2155   if (pos < 0)
2156   {
2157     int min_inventory_size = -pos;
2158     int inventory_pos = player->inventory_size - min_inventory_size;
2159     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2160
2161     return (player->inventory_size >= min_inventory_size ?
2162             player->inventory_element[inventory_pos] :
2163             player->inventory_infinite_element != EL_UNDEFINED ?
2164             player->inventory_infinite_element :
2165             player->dynabombs_left >= min_dynabombs_left ?
2166             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2167             EL_UNDEFINED);
2168   }
2169   else
2170   {
2171     int min_dynabombs_left = pos + 1;
2172     int min_inventory_size = pos + 1 - player->dynabombs_left;
2173     int inventory_pos = pos - player->dynabombs_left;
2174
2175     return (player->inventory_infinite_element != EL_UNDEFINED ?
2176             player->inventory_infinite_element :
2177             player->dynabombs_left >= min_dynabombs_left ?
2178             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2179             player->inventory_size >= min_inventory_size ?
2180             player->inventory_element[inventory_pos] :
2181             EL_UNDEFINED);
2182   }
2183 }
2184
2185 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2186 {
2187   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2188   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2189   int compare_result;
2190
2191   if (gpo1->sort_priority != gpo2->sort_priority)
2192     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2193   else
2194     compare_result = gpo1->nr - gpo2->nr;
2195
2196   return compare_result;
2197 }
2198
2199 int getPlayerInventorySize(int player_nr)
2200 {
2201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202     return game_em.ply[player_nr]->dynamite;
2203   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2204     return game_sp.red_disk_count;
2205   else
2206     return stored_player[player_nr].inventory_size;
2207 }
2208
2209 static void InitGameControlValues(void)
2210 {
2211   int i;
2212
2213   for (i = 0; game_panel_controls[i].nr != -1; i++)
2214   {
2215     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2216     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2217     struct TextPosInfo *pos = gpc->pos;
2218     int nr = gpc->nr;
2219     int type = gpc->type;
2220
2221     if (nr != i)
2222     {
2223       Error("'game_panel_controls' structure corrupted at %d", i);
2224
2225       Fail("this should not happen -- please debug");
2226     }
2227
2228     // force update of game controls after initialization
2229     gpc->value = gpc->last_value = -1;
2230     gpc->frame = gpc->last_frame = -1;
2231     gpc->gfx_frame = -1;
2232
2233     // determine panel value width for later calculation of alignment
2234     if (type == TYPE_INTEGER || type == TYPE_STRING)
2235     {
2236       pos->width = pos->size * getFontWidth(pos->font);
2237       pos->height = getFontHeight(pos->font);
2238     }
2239     else if (type == TYPE_ELEMENT)
2240     {
2241       pos->width = pos->size;
2242       pos->height = pos->size;
2243     }
2244
2245     // fill structure for game panel draw order
2246     gpo->nr = gpc->nr;
2247     gpo->sort_priority = pos->sort_priority;
2248   }
2249
2250   // sort game panel controls according to sort_priority and control number
2251   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2252         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2253 }
2254
2255 static void UpdatePlayfieldElementCount(void)
2256 {
2257   boolean use_element_count = FALSE;
2258   int i, j, x, y;
2259
2260   // first check if it is needed at all to calculate playfield element count
2261   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2262     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2263       use_element_count = TRUE;
2264
2265   if (!use_element_count)
2266     return;
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269     element_info[i].element_count = 0;
2270
2271   SCAN_PLAYFIELD(x, y)
2272   {
2273     element_info[Tile[x][y]].element_count++;
2274   }
2275
2276   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2277     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2278       if (IS_IN_GROUP(j, i))
2279         element_info[EL_GROUP_START + i].element_count +=
2280           element_info[j].element_count;
2281 }
2282
2283 static void UpdateGameControlValues(void)
2284 {
2285   int i, k;
2286   int time = (game.LevelSolved ?
2287               game.LevelSolved_CountingTime :
2288               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2289               game_bd.time_played :
2290               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2291               game_em.lev->time :
2292               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2293               game_sp.time_played :
2294               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2295               game_mm.energy_left :
2296               game.no_level_time_limit ? TimePlayed : TimeLeft);
2297   int score = (game.LevelSolved ?
2298                game.LevelSolved_CountingScore :
2299                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2300                game_bd.score :
2301                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2302                game_em.lev->score :
2303                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2304                game_sp.score :
2305                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2306                game_mm.score :
2307                game.score);
2308   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2309               game_bd.gems_still_needed :
2310               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2311               game_em.lev->gems_needed :
2312               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2313               game_sp.infotrons_still_needed :
2314               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2315               game_mm.kettles_still_needed :
2316               game.gems_still_needed);
2317   int gems_needed = level.gems_needed;
2318   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2319                         game_bd.game->cave->diamonds_collected :
2320                         gems_needed - gems);
2321   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2322                     game_bd.game->cave->diamond_value :
2323                     level.score[SC_EMERALD]);
2324   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2325                      game_bd.gems_still_needed > 0 :
2326                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2327                      game_em.lev->gems_needed > 0 :
2328                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2329                      game_sp.infotrons_still_needed > 0 :
2330                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2331                      game_mm.kettles_still_needed > 0 ||
2332                      game_mm.lights_still_needed > 0 :
2333                      game.gems_still_needed > 0 ||
2334                      game.sokoban_fields_still_needed > 0 ||
2335                      game.sokoban_objects_still_needed > 0 ||
2336                      game.lights_still_needed > 0);
2337   int health = (game.LevelSolved ?
2338                 game.LevelSolved_CountingHealth :
2339                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2340                 MM_HEALTH(game_mm.laser_overload_value) :
2341                 game.health);
2342   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2343
2344   UpdatePlayfieldElementCount();
2345
2346   // update game panel control values
2347
2348   // used instead of "level_nr" (for network games)
2349   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2350   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2351   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2352   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2353   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2354
2355   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2356   for (i = 0; i < MAX_NUM_KEYS; i++)
2357     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2358   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2359   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2360
2361   if (game.centered_player_nr == -1)
2362   {
2363     for (i = 0; i < MAX_PLAYERS; i++)
2364     {
2365       // only one player in Supaplex game engine
2366       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2367         break;
2368
2369       for (k = 0; k < MAX_NUM_KEYS; k++)
2370       {
2371         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2372         {
2373           if (game_em.ply[i]->keys & (1 << k))
2374             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2375               get_key_element_from_nr(k);
2376         }
2377         else if (stored_player[i].key[k])
2378           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2379             get_key_element_from_nr(k);
2380       }
2381
2382       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2383         getPlayerInventorySize(i);
2384
2385       if (stored_player[i].num_white_keys > 0)
2386         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2387           EL_DC_KEY_WHITE;
2388
2389       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2390         stored_player[i].num_white_keys;
2391     }
2392   }
2393   else
2394   {
2395     int player_nr = game.centered_player_nr;
2396
2397     for (k = 0; k < MAX_NUM_KEYS; k++)
2398     {
2399       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2400       {
2401         if (game_em.ply[player_nr]->keys & (1 << k))
2402           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2403             get_key_element_from_nr(k);
2404       }
2405       else if (stored_player[player_nr].key[k])
2406         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2407           get_key_element_from_nr(k);
2408     }
2409
2410     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2411       getPlayerInventorySize(player_nr);
2412
2413     if (stored_player[player_nr].num_white_keys > 0)
2414       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2415
2416     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2417       stored_player[player_nr].num_white_keys;
2418   }
2419
2420   // re-arrange keys on game panel, if needed or if defined by style settings
2421   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2422   {
2423     int nr = GAME_PANEL_KEY_1 + i;
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2425     struct TextPosInfo *pos = gpc->pos;
2426
2427     // skip check if key is not in the player's inventory
2428     if (gpc->value == EL_EMPTY)
2429       continue;
2430
2431     // check if keys should be arranged on panel from left to right
2432     if (pos->style == STYLE_LEFTMOST_POSITION)
2433     {
2434       // check previous key positions (left from current key)
2435       for (k = 0; k < i; k++)
2436       {
2437         int nr_new = GAME_PANEL_KEY_1 + k;
2438
2439         if (game_panel_controls[nr_new].value == EL_EMPTY)
2440         {
2441           game_panel_controls[nr_new].value = gpc->value;
2442           gpc->value = EL_EMPTY;
2443
2444           break;
2445         }
2446       }
2447     }
2448
2449     // check if "undefined" keys can be placed at some other position
2450     if (pos->x == -1 && pos->y == -1)
2451     {
2452       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2453
2454       // 1st try: display key at the same position as normal or EM keys
2455       if (game_panel_controls[nr_new].value == EL_EMPTY)
2456       {
2457         game_panel_controls[nr_new].value = gpc->value;
2458       }
2459       else
2460       {
2461         // 2nd try: display key at the next free position in the key panel
2462         for (k = 0; k < STD_NUM_KEYS; k++)
2463         {
2464           nr_new = GAME_PANEL_KEY_1 + k;
2465
2466           if (game_panel_controls[nr_new].value == EL_EMPTY)
2467           {
2468             game_panel_controls[nr_new].value = gpc->value;
2469
2470             break;
2471           }
2472         }
2473       }
2474     }
2475   }
2476
2477   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2478   {
2479     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2480       get_inventory_element_from_pos(local_player, i);
2481     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2482       get_inventory_element_from_pos(local_player, -i - 1);
2483   }
2484
2485   game_panel_controls[GAME_PANEL_SCORE].value = score;
2486   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2487
2488   game_panel_controls[GAME_PANEL_TIME].value = time;
2489
2490   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2491   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2492   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2493
2494   if (level.time == 0)
2495     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2496   else
2497     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2498
2499   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2500   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2501
2502   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2503
2504   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2505     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2506      EL_EMPTY);
2507   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2508     local_player->shield_normal_time_left;
2509   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2510     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2511      EL_EMPTY);
2512   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2513     local_player->shield_deadly_time_left;
2514
2515   game_panel_controls[GAME_PANEL_EXIT].value =
2516     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2517
2518   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2519     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2520   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2521     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2522      EL_EMC_MAGIC_BALL_SWITCH);
2523
2524   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2525     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2526   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2527     game.light_time_left;
2528
2529   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2530     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2531   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2532     game.timegate_time_left;
2533
2534   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2535     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2536
2537   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2538     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2539   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2540     game.lenses_time_left;
2541
2542   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2543     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2544   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2545     game.magnify_time_left;
2546
2547   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2548     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2549      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2550      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2551      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2552      EL_BALLOON_SWITCH_NONE);
2553
2554   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2555     local_player->dynabomb_count;
2556   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2557     local_player->dynabomb_size;
2558   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2559     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2560
2561   game_panel_controls[GAME_PANEL_PENGUINS].value =
2562     game.friends_still_needed;
2563
2564   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2565     game.sokoban_objects_still_needed;
2566   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2567     game.sokoban_fields_still_needed;
2568
2569   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2570     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2571
2572   for (i = 0; i < NUM_BELTS; i++)
2573   {
2574     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2575       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2576        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2577     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2578       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2579   }
2580
2581   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2582     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2583   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2584     game.magic_wall_time_left;
2585
2586   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2587     local_player->gravity;
2588
2589   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2590     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2591
2592   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2593     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2594       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2595        game.panel.element[i].id : EL_UNDEFINED);
2596
2597   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2598     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2599       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2600        element_info[game.panel.element_count[i].id].element_count : 0);
2601
2602   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2603     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2604       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2605        element_info[game.panel.ce_score[i].id].collect_score : 0);
2606
2607   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2608     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2609       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2610        element_info[game.panel.ce_score_element[i].id].collect_score :
2611        EL_UNDEFINED);
2612
2613   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2614   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2615   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2616
2617   // update game panel control frames
2618
2619   for (i = 0; game_panel_controls[i].nr != -1; i++)
2620   {
2621     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2622
2623     if (gpc->type == TYPE_ELEMENT)
2624     {
2625       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2626       {
2627         int last_anim_random_frame = gfx.anim_random_frame;
2628         int element = gpc->value;
2629         int graphic = el2panelimg(element);
2630         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2631                                sync_random_frame :
2632                                graphic_info[graphic].anim_global_anim_sync ?
2633                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2634
2635         if (gpc->value != gpc->last_value)
2636         {
2637           gpc->gfx_frame = 0;
2638           gpc->gfx_random = init_gfx_random;
2639         }
2640         else
2641         {
2642           gpc->gfx_frame++;
2643
2644           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2645               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2646             gpc->gfx_random = init_gfx_random;
2647         }
2648
2649         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2650           gfx.anim_random_frame = gpc->gfx_random;
2651
2652         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2653           gpc->gfx_frame = element_info[element].collect_score;
2654
2655         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2656
2657         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2658           gfx.anim_random_frame = last_anim_random_frame;
2659       }
2660     }
2661     else if (gpc->type == TYPE_GRAPHIC)
2662     {
2663       if (gpc->graphic != IMG_UNDEFINED)
2664       {
2665         int last_anim_random_frame = gfx.anim_random_frame;
2666         int graphic = gpc->graphic;
2667         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2668                                sync_random_frame :
2669                                graphic_info[graphic].anim_global_anim_sync ?
2670                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2671
2672         if (gpc->value != gpc->last_value)
2673         {
2674           gpc->gfx_frame = 0;
2675           gpc->gfx_random = init_gfx_random;
2676         }
2677         else
2678         {
2679           gpc->gfx_frame++;
2680
2681           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2682               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2683             gpc->gfx_random = init_gfx_random;
2684         }
2685
2686         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2687           gfx.anim_random_frame = gpc->gfx_random;
2688
2689         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2690
2691         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2692           gfx.anim_random_frame = last_anim_random_frame;
2693       }
2694     }
2695   }
2696 }
2697
2698 static void DisplayGameControlValues(void)
2699 {
2700   boolean redraw_panel = FALSE;
2701   int i;
2702
2703   for (i = 0; game_panel_controls[i].nr != -1; i++)
2704   {
2705     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2706
2707     if (PANEL_DEACTIVATED(gpc->pos))
2708       continue;
2709
2710     if (gpc->value == gpc->last_value &&
2711         gpc->frame == gpc->last_frame)
2712       continue;
2713
2714     redraw_panel = TRUE;
2715   }
2716
2717   if (!redraw_panel)
2718     return;
2719
2720   // copy default game door content to main double buffer
2721
2722   // !!! CHECK AGAIN !!!
2723   SetPanelBackground();
2724   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2725   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2726
2727   // redraw game control buttons
2728   RedrawGameButtons();
2729
2730   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2731
2732   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2733   {
2734     int nr = game_panel_order[i].nr;
2735     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2736     struct TextPosInfo *pos = gpc->pos;
2737     int type = gpc->type;
2738     int value = gpc->value;
2739     int frame = gpc->frame;
2740     int size = pos->size;
2741     int font = pos->font;
2742     boolean draw_masked = pos->draw_masked;
2743     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2744
2745     if (PANEL_DEACTIVATED(pos))
2746       continue;
2747
2748     if (pos->class == get_hash_from_string("extra_panel_items") &&
2749         !setup.prefer_extra_panel_items)
2750       continue;
2751
2752     gpc->last_value = value;
2753     gpc->last_frame = frame;
2754
2755     if (type == TYPE_INTEGER)
2756     {
2757       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2758           nr == GAME_PANEL_INVENTORY_COUNT ||
2759           nr == GAME_PANEL_SCORE ||
2760           nr == GAME_PANEL_HIGHSCORE ||
2761           nr == GAME_PANEL_TIME)
2762       {
2763         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2764
2765         if (use_dynamic_size)           // use dynamic number of digits
2766         {
2767           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2768                               nr == GAME_PANEL_INVENTORY_COUNT ||
2769                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2770           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2771                           nr == GAME_PANEL_INVENTORY_COUNT ||
2772                           nr == GAME_PANEL_TIME ? 1 : 2);
2773           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2774                        nr == GAME_PANEL_INVENTORY_COUNT ||
2775                        nr == GAME_PANEL_TIME ? 3 : 5);
2776           int size2 = size1 + size_add;
2777           int font1 = pos->font;
2778           int font2 = pos->font_alt;
2779
2780           size = (value < value_change ? size1 : size2);
2781           font = (value < value_change ? font1 : font2);
2782         }
2783       }
2784
2785       // correct text size if "digits" is zero or less
2786       if (size <= 0)
2787         size = strlen(int2str(value, size));
2788
2789       // dynamically correct text alignment
2790       pos->width = size * getFontWidth(font);
2791
2792       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2793                   int2str(value, size), font, mask_mode);
2794     }
2795     else if (type == TYPE_ELEMENT)
2796     {
2797       int element, graphic;
2798       Bitmap *src_bitmap;
2799       int src_x, src_y;
2800       int width, height;
2801       int dst_x = PANEL_XPOS(pos);
2802       int dst_y = PANEL_YPOS(pos);
2803
2804       if (value != EL_UNDEFINED && value != EL_EMPTY)
2805       {
2806         element = value;
2807         graphic = el2panelimg(value);
2808
2809 #if 0
2810         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2811               element, EL_NAME(element), size);
2812 #endif
2813
2814         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2815           size = TILESIZE;
2816
2817         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2818                               &src_x, &src_y);
2819
2820         width  = graphic_info[graphic].width  * size / TILESIZE;
2821         height = graphic_info[graphic].height * size / TILESIZE;
2822
2823         if (draw_masked)
2824           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2825                            dst_x, dst_y);
2826         else
2827           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2828                      dst_x, dst_y);
2829       }
2830     }
2831     else if (type == TYPE_GRAPHIC)
2832     {
2833       int graphic        = gpc->graphic;
2834       int graphic_active = gpc->graphic_active;
2835       Bitmap *src_bitmap;
2836       int src_x, src_y;
2837       int width, height;
2838       int dst_x = PANEL_XPOS(pos);
2839       int dst_y = PANEL_YPOS(pos);
2840       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2841                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2842
2843       if (graphic != IMG_UNDEFINED && !skip)
2844       {
2845         if (pos->style == STYLE_REVERSE)
2846           value = 100 - value;
2847
2848         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2849
2850         if (pos->direction & MV_HORIZONTAL)
2851         {
2852           width  = graphic_info[graphic_active].width * value / 100;
2853           height = graphic_info[graphic_active].height;
2854
2855           if (pos->direction == MV_LEFT)
2856           {
2857             src_x += graphic_info[graphic_active].width - width;
2858             dst_x += graphic_info[graphic_active].width - width;
2859           }
2860         }
2861         else
2862         {
2863           width  = graphic_info[graphic_active].width;
2864           height = graphic_info[graphic_active].height * value / 100;
2865
2866           if (pos->direction == MV_UP)
2867           {
2868             src_y += graphic_info[graphic_active].height - height;
2869             dst_y += graphic_info[graphic_active].height - height;
2870           }
2871         }
2872
2873         if (draw_masked)
2874           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2875                            dst_x, dst_y);
2876         else
2877           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2878                      dst_x, dst_y);
2879
2880         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2881
2882         if (pos->direction & MV_HORIZONTAL)
2883         {
2884           if (pos->direction == MV_RIGHT)
2885           {
2886             src_x += width;
2887             dst_x += width;
2888           }
2889           else
2890           {
2891             dst_x = PANEL_XPOS(pos);
2892           }
2893
2894           width = graphic_info[graphic].width - width;
2895         }
2896         else
2897         {
2898           if (pos->direction == MV_DOWN)
2899           {
2900             src_y += height;
2901             dst_y += height;
2902           }
2903           else
2904           {
2905             dst_y = PANEL_YPOS(pos);
2906           }
2907
2908           height = graphic_info[graphic].height - height;
2909         }
2910
2911         if (draw_masked)
2912           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2913                            dst_x, dst_y);
2914         else
2915           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2916                      dst_x, dst_y);
2917       }
2918     }
2919     else if (type == TYPE_STRING)
2920     {
2921       boolean active = (value != 0);
2922       char *state_normal = "off";
2923       char *state_active = "on";
2924       char *state = (active ? state_active : state_normal);
2925       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2926                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2927                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2928                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2929
2930       if (nr == GAME_PANEL_GRAVITY_STATE)
2931       {
2932         int font1 = pos->font;          // (used for normal state)
2933         int font2 = pos->font_alt;      // (used for active state)
2934
2935         font = (active ? font2 : font1);
2936       }
2937
2938       if (s != NULL)
2939       {
2940         char *s_cut;
2941
2942         if (size <= 0)
2943         {
2944           // don't truncate output if "chars" is zero or less
2945           size = strlen(s);
2946
2947           // dynamically correct text alignment
2948           pos->width = size * getFontWidth(font);
2949         }
2950
2951         s_cut = getStringCopyN(s, size);
2952
2953         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2954                     s_cut, font, mask_mode);
2955
2956         free(s_cut);
2957       }
2958     }
2959
2960     redraw_mask |= REDRAW_DOOR_1;
2961   }
2962
2963   SetGameStatus(GAME_MODE_PLAYING);
2964 }
2965
2966 void UpdateAndDisplayGameControlValues(void)
2967 {
2968   if (tape.deactivate_display)
2969     return;
2970
2971   UpdateGameControlValues();
2972   DisplayGameControlValues();
2973 }
2974
2975 void UpdateGameDoorValues(void)
2976 {
2977   UpdateGameControlValues();
2978 }
2979
2980 void DrawGameDoorValues(void)
2981 {
2982   DisplayGameControlValues();
2983 }
2984
2985
2986 // ============================================================================
2987 // InitGameEngine()
2988 // ----------------------------------------------------------------------------
2989 // initialize game engine due to level / tape version number
2990 // ============================================================================
2991
2992 static void InitGameEngine(void)
2993 {
2994   int i, j, k, l, x, y;
2995
2996   // set game engine from tape file when re-playing, else from level file
2997   game.engine_version = (tape.playing ? tape.engine_version :
2998                          level.game_version);
2999
3000   // set single or multi-player game mode (needed for re-playing tapes)
3001   game.team_mode = setup.team_mode;
3002
3003   if (tape.playing)
3004   {
3005     int num_players = 0;
3006
3007     for (i = 0; i < MAX_PLAYERS; i++)
3008       if (tape.player_participates[i])
3009         num_players++;
3010
3011     // multi-player tapes contain input data for more than one player
3012     game.team_mode = (num_players > 1);
3013   }
3014
3015 #if 0
3016   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3017         level.game_version);
3018   Debug("game:init:level", "          tape.file_version   == %06d",
3019         tape.file_version);
3020   Debug("game:init:level", "          tape.game_version   == %06d",
3021         tape.game_version);
3022   Debug("game:init:level", "          tape.engine_version == %06d",
3023         tape.engine_version);
3024   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3025         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3026 #endif
3027
3028   // --------------------------------------------------------------------------
3029   // set flags for bugs and changes according to active game engine version
3030   // --------------------------------------------------------------------------
3031
3032   /*
3033     Summary of bugfix:
3034     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3035
3036     Bug was introduced in version:
3037     2.0.1
3038
3039     Bug was fixed in version:
3040     4.2.0.0
3041
3042     Description:
3043     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3044     but the property "can fall" was missing, which caused some levels to be
3045     unsolvable. This was fixed in version 4.2.0.0.
3046
3047     Affected levels/tapes:
3048     An example for a tape that was fixed by this bugfix is tape 029 from the
3049     level set "rnd_sam_bateman".
3050     The wrong behaviour will still be used for all levels or tapes that were
3051     created/recorded with it. An example for this is tape 023 from the level
3052     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3053   */
3054
3055   boolean use_amoeba_dropping_cannot_fall_bug =
3056     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3057       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3058      (tape.playing &&
3059       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3060       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3061
3062   /*
3063     Summary of bugfix/change:
3064     Fixed move speed of elements entering or leaving magic wall.
3065
3066     Fixed/changed in version:
3067     2.0.1
3068
3069     Description:
3070     Before 2.0.1, move speed of elements entering or leaving magic wall was
3071     twice as fast as it is now.
3072     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3073
3074     Affected levels/tapes:
3075     The first condition is generally needed for all levels/tapes before version
3076     2.0.1, which might use the old behaviour before it was changed; known tapes
3077     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3078     The second condition is an exception from the above case and is needed for
3079     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3080     above, but before it was known that this change would break tapes like the
3081     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3082     although the engine version while recording maybe was before 2.0.1. There
3083     are a lot of tapes that are affected by this exception, like tape 006 from
3084     the level set "rnd_conor_mancone".
3085   */
3086
3087   boolean use_old_move_stepsize_for_magic_wall =
3088     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3089      !(tape.playing &&
3090        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3091        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3092
3093   /*
3094     Summary of bugfix/change:
3095     Fixed handling for custom elements that change when pushed by the player.
3096
3097     Fixed/changed in version:
3098     3.1.0
3099
3100     Description:
3101     Before 3.1.0, custom elements that "change when pushing" changed directly
3102     after the player started pushing them (until then handled in "DigField()").
3103     Since 3.1.0, these custom elements are not changed until the "pushing"
3104     move of the element is finished (now handled in "ContinueMoving()").
3105
3106     Affected levels/tapes:
3107     The first condition is generally needed for all levels/tapes before version
3108     3.1.0, which might use the old behaviour before it was changed; known tapes
3109     that are affected are some tapes from the level set "Walpurgis Gardens" by
3110     Jamie Cullen.
3111     The second condition is an exception from the above case and is needed for
3112     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3113     above (including some development versions of 3.1.0), but before it was
3114     known that this change would break tapes like the above and was fixed in
3115     3.1.1, so that the changed behaviour was active although the engine version
3116     while recording maybe was before 3.1.0. There is at least one tape that is
3117     affected by this exception, which is the tape for the one-level set "Bug
3118     Machine" by Juergen Bonhagen.
3119   */
3120
3121   game.use_change_when_pushing_bug =
3122     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3123      !(tape.playing &&
3124        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3125        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3126
3127   /*
3128     Summary of bugfix/change:
3129     Fixed handling for blocking the field the player leaves when moving.
3130
3131     Fixed/changed in version:
3132     3.1.1
3133
3134     Description:
3135     Before 3.1.1, when "block last field when moving" was enabled, the field
3136     the player is leaving when moving was blocked for the time of the move,
3137     and was directly unblocked afterwards. This resulted in the last field
3138     being blocked for exactly one less than the number of frames of one player
3139     move. Additionally, even when blocking was disabled, the last field was
3140     blocked for exactly one frame.
3141     Since 3.1.1, due to changes in player movement handling, the last field
3142     is not blocked at all when blocking is disabled. When blocking is enabled,
3143     the last field is blocked for exactly the number of frames of one player
3144     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3145     last field is blocked for exactly one more than the number of frames of
3146     one player move.
3147
3148     Affected levels/tapes:
3149     (!!! yet to be determined -- probably many !!!)
3150   */
3151
3152   game.use_block_last_field_bug =
3153     (game.engine_version < VERSION_IDENT(3,1,1,0));
3154
3155   /* various special flags and settings for native Emerald Mine game engine */
3156
3157   game_em.use_single_button =
3158     (game.engine_version > VERSION_IDENT(4,0,0,2));
3159
3160   game_em.use_push_delay =
3161     (game.engine_version > VERSION_IDENT(4,3,7,1));
3162
3163   game_em.use_snap_key_bug =
3164     (game.engine_version < VERSION_IDENT(4,0,1,0));
3165
3166   game_em.use_random_bug =
3167     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3168
3169   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3170
3171   game_em.use_old_explosions            = use_old_em_engine;
3172   game_em.use_old_android               = use_old_em_engine;
3173   game_em.use_old_push_elements         = use_old_em_engine;
3174   game_em.use_old_push_into_acid        = use_old_em_engine;
3175
3176   game_em.use_wrap_around               = !use_old_em_engine;
3177
3178   // --------------------------------------------------------------------------
3179
3180   // set maximal allowed number of custom element changes per game frame
3181   game.max_num_changes_per_frame = 1;
3182
3183   // default scan direction: scan playfield from top/left to bottom/right
3184   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3185
3186   // dynamically adjust element properties according to game engine version
3187   InitElementPropertiesEngine(game.engine_version);
3188
3189   // ---------- initialize special element properties -------------------------
3190
3191   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3192   if (use_amoeba_dropping_cannot_fall_bug)
3193     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3194
3195   // ---------- initialize player's initial move delay ------------------------
3196
3197   // dynamically adjust player properties according to level information
3198   for (i = 0; i < MAX_PLAYERS; i++)
3199     game.initial_move_delay_value[i] =
3200       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3201
3202   // dynamically adjust player properties according to game engine version
3203   for (i = 0; i < MAX_PLAYERS; i++)
3204     game.initial_move_delay[i] =
3205       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3206        game.initial_move_delay_value[i] : 0);
3207
3208   // ---------- initialize player's initial push delay ------------------------
3209
3210   // dynamically adjust player properties according to game engine version
3211   game.initial_push_delay_value =
3212     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3213
3214   // ---------- initialize changing elements ----------------------------------
3215
3216   // initialize changing elements information
3217   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[i];
3220
3221     // this pointer might have been changed in the level editor
3222     ei->change = &ei->change_page[0];
3223
3224     if (!IS_CUSTOM_ELEMENT(i))
3225     {
3226       ei->change->target_element = EL_EMPTY_SPACE;
3227       ei->change->delay_fixed = 0;
3228       ei->change->delay_random = 0;
3229       ei->change->delay_frames = 1;
3230     }
3231
3232     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3233     {
3234       ei->has_change_event[j] = FALSE;
3235
3236       ei->event_page_nr[j] = 0;
3237       ei->event_page[j] = &ei->change_page[0];
3238     }
3239   }
3240
3241   // add changing elements from pre-defined list
3242   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3243   {
3244     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3245     struct ElementInfo *ei = &element_info[ch_delay->element];
3246
3247     ei->change->target_element       = ch_delay->target_element;
3248     ei->change->delay_fixed          = ch_delay->change_delay;
3249
3250     ei->change->pre_change_function  = ch_delay->pre_change_function;
3251     ei->change->change_function      = ch_delay->change_function;
3252     ei->change->post_change_function = ch_delay->post_change_function;
3253
3254     ei->change->can_change = TRUE;
3255     ei->change->can_change_or_has_action = TRUE;
3256
3257     ei->has_change_event[CE_DELAY] = TRUE;
3258
3259     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3260     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3261   }
3262
3263   // ---------- initialize if element can trigger global animations -----------
3264
3265   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3266   {
3267     struct ElementInfo *ei = &element_info[i];
3268
3269     ei->has_anim_event = FALSE;
3270   }
3271
3272   InitGlobalAnimEventsForCustomElements();
3273
3274   // ---------- initialize internal run-time variables ------------------------
3275
3276   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       ei->change_page[j].can_change_or_has_action =
3283         (ei->change_page[j].can_change |
3284          ei->change_page[j].has_action);
3285     }
3286   }
3287
3288   // add change events from custom element configuration
3289   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3290   {
3291     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3292
3293     for (j = 0; j < ei->num_change_pages; j++)
3294     {
3295       if (!ei->change_page[j].can_change_or_has_action)
3296         continue;
3297
3298       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3299       {
3300         // only add event page for the first page found with this event
3301         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3302         {
3303           ei->has_change_event[k] = TRUE;
3304
3305           ei->event_page_nr[k] = j;
3306           ei->event_page[k] = &ei->change_page[j];
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize reference elements in change conditions ------------
3313
3314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3315   {
3316     int element = EL_CUSTOM_START + i;
3317     struct ElementInfo *ei = &element_info[element];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       int trigger_element = ei->change_page[j].initial_trigger_element;
3322
3323       if (trigger_element >= EL_PREV_CE_8 &&
3324           trigger_element <= EL_NEXT_CE_8)
3325         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3326
3327       ei->change_page[j].trigger_element = trigger_element;
3328     }
3329   }
3330
3331   // ---------- initialize run-time trigger player and element ----------------
3332
3333   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334   {
3335     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336
3337     for (j = 0; j < ei->num_change_pages; j++)
3338     {
3339       struct ElementChangeInfo *change = &ei->change_page[j];
3340
3341       change->actual_trigger_element = EL_EMPTY;
3342       change->actual_trigger_player = EL_EMPTY;
3343       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3344       change->actual_trigger_side = CH_SIDE_NONE;
3345       change->actual_trigger_ce_value = 0;
3346       change->actual_trigger_ce_score = 0;
3347       change->actual_trigger_x = -1;
3348       change->actual_trigger_y = -1;
3349     }
3350   }
3351
3352   // ---------- initialize trigger events -------------------------------------
3353
3354   // initialize trigger events information
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3357       trigger_events[i][j] = FALSE;
3358
3359   // add trigger events from element change event properties
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361   {
3362     struct ElementInfo *ei = &element_info[i];
3363
3364     for (j = 0; j < ei->num_change_pages; j++)
3365     {
3366       struct ElementChangeInfo *change = &ei->change_page[j];
3367
3368       if (!change->can_change_or_has_action)
3369         continue;
3370
3371       if (change->has_event[CE_BY_OTHER_ACTION])
3372       {
3373         int trigger_element = change->trigger_element;
3374
3375         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3376         {
3377           if (change->has_event[k])
3378           {
3379             if (IS_GROUP_ELEMENT(trigger_element))
3380             {
3381               struct ElementGroupInfo *group =
3382                 element_info[trigger_element].group;
3383
3384               for (l = 0; l < group->num_elements_resolved; l++)
3385                 trigger_events[group->element_resolved[l]][k] = TRUE;
3386             }
3387             else if (trigger_element == EL_ANY_ELEMENT)
3388               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3389                 trigger_events[l][k] = TRUE;
3390             else
3391               trigger_events[trigger_element][k] = TRUE;
3392           }
3393         }
3394       }
3395     }
3396   }
3397
3398   // ---------- initialize push delay -----------------------------------------
3399
3400   // initialize push delay values to default
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402   {
3403     if (!IS_CUSTOM_ELEMENT(i))
3404     {
3405       // set default push delay values (corrected since version 3.0.7-1)
3406       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3407       {
3408         element_info[i].push_delay_fixed = 2;
3409         element_info[i].push_delay_random = 8;
3410       }
3411       else
3412       {
3413         element_info[i].push_delay_fixed = 8;
3414         element_info[i].push_delay_random = 8;
3415       }
3416     }
3417   }
3418
3419   // set push delay value for certain elements from pre-defined list
3420   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3421   {
3422     int e = push_delay_list[i].element;
3423
3424     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3425     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3426   }
3427
3428   // set push delay value for Supaplex elements for newer engine versions
3429   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3430   {
3431     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432     {
3433       if (IS_SP_ELEMENT(i))
3434       {
3435         // set SP push delay to just enough to push under a falling zonk
3436         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3437
3438         element_info[i].push_delay_fixed  = delay;
3439         element_info[i].push_delay_random = 0;
3440       }
3441     }
3442   }
3443
3444   // ---------- initialize move stepsize --------------------------------------
3445
3446   // initialize move stepsize values to default
3447   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3448     if (!IS_CUSTOM_ELEMENT(i))
3449       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3450
3451   // set move stepsize value for certain elements from pre-defined list
3452   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3453   {
3454     int e = move_stepsize_list[i].element;
3455
3456     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3457
3458     // set move stepsize value for certain elements for older engine versions
3459     if (use_old_move_stepsize_for_magic_wall)
3460     {
3461       if (e == EL_MAGIC_WALL_FILLING ||
3462           e == EL_MAGIC_WALL_EMPTYING ||
3463           e == EL_BD_MAGIC_WALL_FILLING ||
3464           e == EL_BD_MAGIC_WALL_EMPTYING)
3465         element_info[e].move_stepsize *= 2;
3466     }
3467   }
3468
3469   // ---------- initialize collect score --------------------------------------
3470
3471   // initialize collect score values for custom elements from initial value
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473     if (IS_CUSTOM_ELEMENT(i))
3474       element_info[i].collect_score = element_info[i].collect_score_initial;
3475
3476   // ---------- initialize collect count --------------------------------------
3477
3478   // initialize collect count values for non-custom elements
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (!IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_count_initial = 0;
3482
3483   // add collect count values for all elements from pre-defined list
3484   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485     element_info[collect_count_list[i].element].collect_count_initial =
3486       collect_count_list[i].count;
3487
3488   // ---------- initialize access direction -----------------------------------
3489
3490   // initialize access direction values to default (access from every side)
3491   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492     if (!IS_CUSTOM_ELEMENT(i))
3493       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3494
3495   // set access direction value for certain elements from pre-defined list
3496   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497     element_info[access_direction_list[i].element].access_direction =
3498       access_direction_list[i].direction;
3499
3500   // ---------- initialize explosion content ----------------------------------
3501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502   {
3503     if (IS_CUSTOM_ELEMENT(i))
3504       continue;
3505
3506     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3507     {
3508       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3509
3510       element_info[i].content.e[x][y] =
3511         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513          i == EL_PLAYER_3 ? EL_EMERALD :
3514          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515          i == EL_MOLE ? EL_EMERALD_RED :
3516          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521          i == EL_WALL_EMERALD ? EL_EMERALD :
3522          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527          i == EL_WALL_PEARL ? EL_PEARL :
3528          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3529          EL_EMPTY);
3530     }
3531   }
3532
3533   // ---------- initialize recursion detection --------------------------------
3534   recursion_loop_depth = 0;
3535   recursion_loop_detected = FALSE;
3536   recursion_loop_element = EL_UNDEFINED;
3537
3538   // ---------- initialize graphics engine ------------------------------------
3539   game.scroll_delay_value =
3540     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3542      !setup.forced_scroll_delay           ? 0 :
3543      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3544   if (game.forced_scroll_delay_value == -1)
3545     game.scroll_delay_value =
3546       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3547
3548   // ---------- initialize game engine snapshots ------------------------------
3549   for (i = 0; i < MAX_PLAYERS; i++)
3550     game.snapshot.last_action[i] = 0;
3551   game.snapshot.changed_action = FALSE;
3552   game.snapshot.collected_item = FALSE;
3553   game.snapshot.mode =
3554     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3555      SNAPSHOT_MODE_EVERY_STEP :
3556      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3557      SNAPSHOT_MODE_EVERY_MOVE :
3558      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3559      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3560   game.snapshot.save_snapshot = FALSE;
3561
3562   // ---------- initialize level time for Supaplex engine ---------------------
3563   // Supaplex levels with time limit currently unsupported -- should be added
3564   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3565     level.time = 0;
3566
3567   // ---------- initialize flags for handling game actions --------------------
3568
3569   // set flags for game actions to default values
3570   game.use_key_actions = TRUE;
3571   game.use_mouse_actions = FALSE;
3572
3573   // when using Mirror Magic game engine, handle mouse events only
3574   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3575   {
3576     game.use_key_actions = FALSE;
3577     game.use_mouse_actions = TRUE;
3578   }
3579
3580   // check for custom elements with mouse click events
3581   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3582   {
3583     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3584     {
3585       int element = EL_CUSTOM_START + i;
3586
3587       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3588           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3589           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3590           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3591         game.use_mouse_actions = TRUE;
3592     }
3593   }
3594 }
3595
3596 static int get_num_special_action(int element, int action_first,
3597                                   int action_last)
3598 {
3599   int num_special_action = 0;
3600   int i, j;
3601
3602   for (i = action_first; i <= action_last; i++)
3603   {
3604     boolean found = FALSE;
3605
3606     for (j = 0; j < NUM_DIRECTIONS; j++)
3607       if (el_act_dir2img(element, i, j) !=
3608           el_act_dir2img(element, ACTION_DEFAULT, j))
3609         found = TRUE;
3610
3611     if (found)
3612       num_special_action++;
3613     else
3614       break;
3615   }
3616
3617   return num_special_action;
3618 }
3619
3620
3621 // ============================================================================
3622 // InitGame()
3623 // ----------------------------------------------------------------------------
3624 // initialize and start new game
3625 // ============================================================================
3626
3627 #if DEBUG_INIT_PLAYER
3628 static void DebugPrintPlayerStatus(char *message)
3629 {
3630   int i;
3631
3632   if (!options.debug)
3633     return;
3634
3635   Debug("game:init:player", "%s:", message);
3636
3637   for (i = 0; i < MAX_PLAYERS; i++)
3638   {
3639     struct PlayerInfo *player = &stored_player[i];
3640
3641     Debug("game:init:player",
3642           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3643           i + 1,
3644           player->present,
3645           player->connected,
3646           player->connected_locally,
3647           player->connected_network,
3648           player->active,
3649           (local_player == player ? " (local player)" : ""));
3650   }
3651 }
3652 #endif
3653
3654 void InitGame(void)
3655 {
3656   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3657   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3658   int fade_mask = REDRAW_FIELD;
3659   boolean restarting = (game_status == GAME_MODE_PLAYING);
3660   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3661   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3662   int initial_move_dir = MV_DOWN;
3663   int i, j, x, y;
3664
3665   // required here to update video display before fading (FIX THIS)
3666   DrawMaskedBorder(REDRAW_DOOR_2);
3667
3668   if (!game.restart_level)
3669     CloseDoor(DOOR_CLOSE_1);
3670
3671   if (restarting)
3672   {
3673     // force fading out global animations displayed during game play
3674     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3675   }
3676   else
3677   {
3678     SetGameStatus(GAME_MODE_PLAYING);
3679   }
3680
3681   if (level_editor_test_game)
3682     FadeSkipNextFadeOut();
3683   else
3684     FadeSetEnterScreen();
3685
3686   if (CheckFadeAll())
3687     fade_mask = REDRAW_ALL;
3688
3689   FadeLevelSoundsAndMusic();
3690
3691   ExpireSoundLoops(TRUE);
3692
3693   FadeOut(fade_mask);
3694
3695   if (restarting)
3696   {
3697     // force restarting global animations displayed during game play
3698     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3699
3700     // this is required for "transforming" fade modes like cross-fading
3701     // (else global animations will be stopped, but not restarted here)
3702     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3703
3704     SetGameStatus(GAME_MODE_PLAYING);
3705   }
3706
3707   if (level_editor_test_game)
3708     FadeSkipNextFadeIn();
3709
3710   // needed if different viewport properties defined for playing
3711   ChangeViewportPropertiesIfNeeded();
3712
3713   ClearField();
3714
3715   DrawCompleteVideoDisplay();
3716
3717   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3718
3719   InitGameEngine();
3720   InitGameControlValues();
3721
3722   if (tape.recording)
3723   {
3724     // initialize tape actions from game when recording tape
3725     tape.use_key_actions   = game.use_key_actions;
3726     tape.use_mouse_actions = game.use_mouse_actions;
3727
3728     // initialize visible playfield size when recording tape (for team mode)
3729     tape.scr_fieldx = SCR_FIELDX;
3730     tape.scr_fieldy = SCR_FIELDY;
3731   }
3732
3733   // don't play tapes over network
3734   network_playing = (network.enabled && !tape.playing);
3735
3736   for (i = 0; i < MAX_PLAYERS; i++)
3737   {
3738     struct PlayerInfo *player = &stored_player[i];
3739
3740     player->index_nr = i;
3741     player->index_bit = (1 << i);
3742     player->element_nr = EL_PLAYER_1 + i;
3743
3744     player->present = FALSE;
3745     player->active = FALSE;
3746     player->mapped = FALSE;
3747
3748     player->killed = FALSE;
3749     player->reanimated = FALSE;
3750     player->buried = FALSE;
3751
3752     player->action = 0;
3753     player->effective_action = 0;
3754     player->programmed_action = 0;
3755     player->snap_action = 0;
3756
3757     player->mouse_action.lx = 0;
3758     player->mouse_action.ly = 0;
3759     player->mouse_action.button = 0;
3760     player->mouse_action.button_hint = 0;
3761
3762     player->effective_mouse_action.lx = 0;
3763     player->effective_mouse_action.ly = 0;
3764     player->effective_mouse_action.button = 0;
3765     player->effective_mouse_action.button_hint = 0;
3766
3767     for (j = 0; j < MAX_NUM_KEYS; j++)
3768       player->key[j] = FALSE;
3769
3770     player->num_white_keys = 0;
3771
3772     player->dynabomb_count = 0;
3773     player->dynabomb_size = 1;
3774     player->dynabombs_left = 0;
3775     player->dynabomb_xl = FALSE;
3776
3777     player->MovDir = initial_move_dir;
3778     player->MovPos = 0;
3779     player->GfxPos = 0;
3780     player->GfxDir = initial_move_dir;
3781     player->GfxAction = ACTION_DEFAULT;
3782     player->Frame = 0;
3783     player->StepFrame = 0;
3784
3785     player->initial_element = player->element_nr;
3786     player->artwork_element =
3787       (level.use_artwork_element[i] ? level.artwork_element[i] :
3788        player->element_nr);
3789     player->use_murphy = FALSE;
3790
3791     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3792     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3793
3794     player->gravity = level.initial_player_gravity[i];
3795
3796     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3797
3798     player->actual_frame_counter.count = 0;
3799     player->actual_frame_counter.value = 1;
3800
3801     player->step_counter = 0;
3802
3803     player->last_move_dir = initial_move_dir;
3804
3805     player->is_active = FALSE;
3806
3807     player->is_waiting = FALSE;
3808     player->is_moving = FALSE;
3809     player->is_auto_moving = FALSE;
3810     player->is_digging = FALSE;
3811     player->is_snapping = FALSE;
3812     player->is_collecting = FALSE;
3813     player->is_pushing = FALSE;
3814     player->is_switching = FALSE;
3815     player->is_dropping = FALSE;
3816     player->is_dropping_pressed = FALSE;
3817
3818     player->is_bored = FALSE;
3819     player->is_sleeping = FALSE;
3820
3821     player->was_waiting = TRUE;
3822     player->was_moving = FALSE;
3823     player->was_snapping = FALSE;
3824     player->was_dropping = FALSE;
3825
3826     player->force_dropping = FALSE;
3827
3828     player->frame_counter_bored = -1;
3829     player->frame_counter_sleeping = -1;
3830
3831     player->anim_delay_counter = 0;
3832     player->post_delay_counter = 0;
3833
3834     player->dir_waiting = initial_move_dir;
3835     player->action_waiting = ACTION_DEFAULT;
3836     player->last_action_waiting = ACTION_DEFAULT;
3837     player->special_action_bored = ACTION_DEFAULT;
3838     player->special_action_sleeping = ACTION_DEFAULT;
3839
3840     player->switch_x = -1;
3841     player->switch_y = -1;
3842
3843     player->drop_x = -1;
3844     player->drop_y = -1;
3845
3846     player->show_envelope = 0;
3847
3848     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3849
3850     player->push_delay       = -1;      // initialized when pushing starts
3851     player->push_delay_value = game.initial_push_delay_value;
3852
3853     player->drop_delay = 0;
3854     player->drop_pressed_delay = 0;
3855
3856     player->last_jx = -1;
3857     player->last_jy = -1;
3858     player->jx = -1;
3859     player->jy = -1;
3860
3861     player->shield_normal_time_left = 0;
3862     player->shield_deadly_time_left = 0;
3863
3864     player->last_removed_element = EL_UNDEFINED;
3865
3866     player->inventory_infinite_element = EL_UNDEFINED;
3867     player->inventory_size = 0;
3868
3869     if (level.use_initial_inventory[i])
3870     {
3871       for (j = 0; j < level.initial_inventory_size[i]; j++)
3872       {
3873         int element = level.initial_inventory_content[i][j];
3874         int collect_count = element_info[element].collect_count_initial;
3875         int k;
3876
3877         if (!IS_CUSTOM_ELEMENT(element))
3878           collect_count = 1;
3879
3880         if (collect_count == 0)
3881           player->inventory_infinite_element = element;
3882         else
3883           for (k = 0; k < collect_count; k++)
3884             if (player->inventory_size < MAX_INVENTORY_SIZE)
3885               player->inventory_element[player->inventory_size++] = element;
3886       }
3887     }
3888
3889     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3890     SnapField(player, 0, 0);
3891
3892     map_player_action[i] = i;
3893   }
3894
3895   network_player_action_received = FALSE;
3896
3897   // initial null action
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900
3901   FrameCounter = 0;
3902   TimeFrames = 0;
3903   TimePlayed = 0;
3904   TimeLeft = level.time;
3905
3906   TapeTimeFrames = 0;
3907   TapeTime = 0;
3908
3909   ScreenMovDir = MV_NONE;
3910   ScreenMovPos = 0;
3911   ScreenGfxPos = 0;
3912
3913   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3914
3915   game.robot_wheel_x = -1;
3916   game.robot_wheel_y = -1;
3917
3918   game.exit_x = -1;
3919   game.exit_y = -1;
3920
3921   game.all_players_gone = FALSE;
3922
3923   game.LevelSolved = FALSE;
3924   game.GameOver = FALSE;
3925
3926   game.GamePlayed = !tape.playing;
3927
3928   game.LevelSolved_GameWon = FALSE;
3929   game.LevelSolved_GameEnd = FALSE;
3930   game.LevelSolved_SaveTape = FALSE;
3931   game.LevelSolved_SaveScore = FALSE;
3932
3933   game.LevelSolved_CountingTime = 0;
3934   game.LevelSolved_CountingScore = 0;
3935   game.LevelSolved_CountingHealth = 0;
3936
3937   game.RestartGameRequested = FALSE;
3938
3939   game.panel.active = TRUE;
3940
3941   game.no_level_time_limit = (level.time == 0);
3942   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3943
3944   game.yamyam_content_nr = 0;
3945   game.robot_wheel_active = FALSE;
3946   game.magic_wall_active = FALSE;
3947   game.magic_wall_time_left = 0;
3948   game.light_time_left = 0;
3949   game.timegate_time_left = 0;
3950   game.switchgate_pos = 0;
3951   game.wind_direction = level.wind_direction_initial;
3952
3953   game.time_final = 0;
3954   game.score_time_final = 0;
3955
3956   game.score = 0;
3957   game.score_final = 0;
3958
3959   game.health = MAX_HEALTH;
3960   game.health_final = MAX_HEALTH;
3961
3962   game.gems_still_needed = level.gems_needed;
3963   game.sokoban_fields_still_needed = 0;
3964   game.sokoban_objects_still_needed = 0;
3965   game.lights_still_needed = 0;
3966   game.players_still_needed = 0;
3967   game.friends_still_needed = 0;
3968
3969   game.lenses_time_left = 0;
3970   game.magnify_time_left = 0;
3971
3972   game.ball_active = level.ball_active_initial;
3973   game.ball_content_nr = 0;
3974
3975   game.explosions_delayed = TRUE;
3976
3977   // special case: set custom artwork setting to initial value
3978   game.use_masked_elements = game.use_masked_elements_initial;
3979
3980   for (i = 0; i < NUM_BELTS; i++)
3981   {
3982     game.belt_dir[i] = MV_NONE;
3983     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3984   }
3985
3986   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3987     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3988
3989 #if DEBUG_INIT_PLAYER
3990   DebugPrintPlayerStatus("Player status at level initialization");
3991 #endif
3992
3993   SCAN_PLAYFIELD(x, y)
3994   {
3995     Tile[x][y] = Last[x][y] = level.field[x][y];
3996     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3997     ChangeDelay[x][y] = 0;
3998     ChangePage[x][y] = -1;
3999     CustomValue[x][y] = 0;              // initialized in InitField()
4000     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4001     AmoebaNr[x][y] = 0;
4002     WasJustMoving[x][y] = 0;
4003     WasJustFalling[x][y] = 0;
4004     CheckCollision[x][y] = 0;
4005     CheckImpact[x][y] = 0;
4006     Stop[x][y] = FALSE;
4007     Pushed[x][y] = FALSE;
4008
4009     ChangeCount[x][y] = 0;
4010     ChangeEvent[x][y] = -1;
4011
4012     ExplodePhase[x][y] = 0;
4013     ExplodeDelay[x][y] = 0;
4014     ExplodeField[x][y] = EX_TYPE_NONE;
4015
4016     RunnerVisit[x][y] = 0;
4017     PlayerVisit[x][y] = 0;
4018
4019     GfxFrame[x][y] = 0;
4020     GfxRandom[x][y] = INIT_GFX_RANDOM();
4021     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4022     GfxElement[x][y] = EL_UNDEFINED;
4023     GfxElementEmpty[x][y] = EL_EMPTY;
4024     GfxAction[x][y] = ACTION_DEFAULT;
4025     GfxDir[x][y] = MV_NONE;
4026     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4027   }
4028
4029   SCAN_PLAYFIELD(x, y)
4030   {
4031     InitFieldForEngine(x, y);
4032
4033     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4034       emulate_bd = FALSE;
4035     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4036       emulate_sp = FALSE;
4037
4038     InitField(x, y, TRUE);
4039
4040     ResetGfxAnimation(x, y);
4041   }
4042
4043   InitBeltMovement();
4044
4045   // required if level does not contain any "empty space" element
4046   if (element_info[EL_EMPTY].use_gfx_element)
4047     game.use_masked_elements = TRUE;
4048
4049   for (i = 0; i < MAX_PLAYERS; i++)
4050   {
4051     struct PlayerInfo *player = &stored_player[i];
4052
4053     // set number of special actions for bored and sleeping animation
4054     player->num_special_action_bored =
4055       get_num_special_action(player->artwork_element,
4056                              ACTION_BORING_1, ACTION_BORING_LAST);
4057     player->num_special_action_sleeping =
4058       get_num_special_action(player->artwork_element,
4059                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4060   }
4061
4062   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4063                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4064
4065   // initialize type of slippery elements
4066   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4067   {
4068     if (!IS_CUSTOM_ELEMENT(i))
4069     {
4070       // default: elements slip down either to the left or right randomly
4071       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4072
4073       // SP style elements prefer to slip down on the left side
4074       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4075         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4076
4077       // BD style elements prefer to slip down on the left side
4078       if (game.emulation == EMU_BOULDERDASH)
4079         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4080     }
4081   }
4082
4083   // initialize explosion and ignition delay
4084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4085   {
4086     if (!IS_CUSTOM_ELEMENT(i))
4087     {
4088       int num_phase = 8;
4089       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4090                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4091                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4092       int last_phase = (num_phase + 1) * delay;
4093       int half_phase = (num_phase / 2) * delay;
4094
4095       element_info[i].explosion_delay = last_phase - 1;
4096       element_info[i].ignition_delay = half_phase;
4097
4098       if (i == EL_BLACK_ORB)
4099         element_info[i].ignition_delay = 1;
4100     }
4101   }
4102
4103   // correct non-moving belts to start moving left
4104   for (i = 0; i < NUM_BELTS; i++)
4105     if (game.belt_dir[i] == MV_NONE)
4106       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4107
4108 #if USE_NEW_PLAYER_ASSIGNMENTS
4109   // use preferred player also in local single-player mode
4110   if (!network.enabled && !game.team_mode)
4111   {
4112     int new_index_nr = setup.network_player_nr;
4113
4114     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4115     {
4116       for (i = 0; i < MAX_PLAYERS; i++)
4117         stored_player[i].connected_locally = FALSE;
4118
4119       stored_player[new_index_nr].connected_locally = TRUE;
4120     }
4121   }
4122
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124   {
4125     stored_player[i].connected = FALSE;
4126
4127     // in network game mode, the local player might not be the first player
4128     if (stored_player[i].connected_locally)
4129       local_player = &stored_player[i];
4130   }
4131
4132   if (!network.enabled)
4133     local_player->connected = TRUE;
4134
4135   if (tape.playing)
4136   {
4137     for (i = 0; i < MAX_PLAYERS; i++)
4138       stored_player[i].connected = tape.player_participates[i];
4139   }
4140   else if (network.enabled)
4141   {
4142     // add team mode players connected over the network (needed for correct
4143     // assignment of player figures from level to locally playing players)
4144
4145     for (i = 0; i < MAX_PLAYERS; i++)
4146       if (stored_player[i].connected_network)
4147         stored_player[i].connected = TRUE;
4148   }
4149   else if (game.team_mode)
4150   {
4151     // try to guess locally connected team mode players (needed for correct
4152     // assignment of player figures from level to locally playing players)
4153
4154     for (i = 0; i < MAX_PLAYERS; i++)
4155       if (setup.input[i].use_joystick ||
4156           setup.input[i].key.left != KSYM_UNDEFINED)
4157         stored_player[i].connected = TRUE;
4158   }
4159
4160 #if DEBUG_INIT_PLAYER
4161   DebugPrintPlayerStatus("Player status after level initialization");
4162 #endif
4163
4164 #if DEBUG_INIT_PLAYER
4165   Debug("game:init:player", "Reassigning players ...");
4166 #endif
4167
4168   // check if any connected player was not found in playfield
4169   for (i = 0; i < MAX_PLAYERS; i++)
4170   {
4171     struct PlayerInfo *player = &stored_player[i];
4172
4173     if (player->connected && !player->present)
4174     {
4175       struct PlayerInfo *field_player = NULL;
4176
4177 #if DEBUG_INIT_PLAYER
4178       Debug("game:init:player",
4179             "- looking for field player for player %d ...", i + 1);
4180 #endif
4181
4182       // assign first free player found that is present in the playfield
4183
4184       // first try: look for unmapped playfield player that is not connected
4185       for (j = 0; j < MAX_PLAYERS; j++)
4186         if (field_player == NULL &&
4187             stored_player[j].present &&
4188             !stored_player[j].mapped &&
4189             !stored_player[j].connected)
4190           field_player = &stored_player[j];
4191
4192       // second try: look for *any* unmapped playfield player
4193       for (j = 0; j < MAX_PLAYERS; j++)
4194         if (field_player == NULL &&
4195             stored_player[j].present &&
4196             !stored_player[j].mapped)
4197           field_player = &stored_player[j];
4198
4199       if (field_player != NULL)
4200       {
4201         int jx = field_player->jx, jy = field_player->jy;
4202
4203 #if DEBUG_INIT_PLAYER
4204         Debug("game:init:player", "- found player %d",
4205               field_player->index_nr + 1);
4206 #endif
4207
4208         player->present = FALSE;
4209         player->active = FALSE;
4210
4211         field_player->present = TRUE;
4212         field_player->active = TRUE;
4213
4214         /*
4215         player->initial_element = field_player->initial_element;
4216         player->artwork_element = field_player->artwork_element;
4217
4218         player->block_last_field       = field_player->block_last_field;
4219         player->block_delay_adjustment = field_player->block_delay_adjustment;
4220         */
4221
4222         StorePlayer[jx][jy] = field_player->element_nr;
4223
4224         field_player->jx = field_player->last_jx = jx;
4225         field_player->jy = field_player->last_jy = jy;
4226
4227         if (local_player == player)
4228           local_player = field_player;
4229
4230         map_player_action[field_player->index_nr] = i;
4231
4232         field_player->mapped = TRUE;
4233
4234 #if DEBUG_INIT_PLAYER
4235         Debug("game:init:player", "- map_player_action[%d] == %d",
4236               field_player->index_nr + 1, i + 1);
4237 #endif
4238       }
4239     }
4240
4241     if (player->connected && player->present)
4242       player->mapped = TRUE;
4243   }
4244
4245 #if DEBUG_INIT_PLAYER
4246   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4247 #endif
4248
4249 #else
4250
4251   // check if any connected player was not found in playfield
4252   for (i = 0; i < MAX_PLAYERS; i++)
4253   {
4254     struct PlayerInfo *player = &stored_player[i];
4255
4256     if (player->connected && !player->present)
4257     {
4258       for (j = 0; j < MAX_PLAYERS; j++)
4259       {
4260         struct PlayerInfo *field_player = &stored_player[j];
4261         int jx = field_player->jx, jy = field_player->jy;
4262
4263         // assign first free player found that is present in the playfield
4264         if (field_player->present && !field_player->connected)
4265         {
4266           player->present = TRUE;
4267           player->active = TRUE;
4268
4269           field_player->present = FALSE;
4270           field_player->active = FALSE;
4271
4272           player->initial_element = field_player->initial_element;
4273           player->artwork_element = field_player->artwork_element;
4274
4275           player->block_last_field       = field_player->block_last_field;
4276           player->block_delay_adjustment = field_player->block_delay_adjustment;
4277
4278           StorePlayer[jx][jy] = player->element_nr;
4279
4280           player->jx = player->last_jx = jx;
4281           player->jy = player->last_jy = jy;
4282
4283           break;
4284         }
4285       }
4286     }
4287   }
4288 #endif
4289
4290 #if 0
4291   Debug("game:init:player", "local_player->present == %d",
4292         local_player->present);
4293 #endif
4294
4295   // set focus to local player for network games, else to all players
4296   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4297   game.centered_player_nr_next = game.centered_player_nr;
4298   game.set_centered_player = FALSE;
4299   game.set_centered_player_wrap = FALSE;
4300
4301   if (network_playing && tape.recording)
4302   {
4303     // store client dependent player focus when recording network games
4304     tape.centered_player_nr_next = game.centered_player_nr_next;
4305     tape.set_centered_player = TRUE;
4306   }
4307
4308   if (tape.playing)
4309   {
4310     // when playing a tape, eliminate all players who do not participate
4311
4312 #if USE_NEW_PLAYER_ASSIGNMENTS
4313
4314     if (!game.team_mode)
4315     {
4316       for (i = 0; i < MAX_PLAYERS; i++)
4317       {
4318         if (stored_player[i].active &&
4319             !tape.player_participates[map_player_action[i]])
4320         {
4321           struct PlayerInfo *player = &stored_player[i];
4322           int jx = player->jx, jy = player->jy;
4323
4324 #if DEBUG_INIT_PLAYER
4325           Debug("game:init:player", "Removing player %d at (%d, %d)",
4326                 i + 1, jx, jy);
4327 #endif
4328
4329           player->active = FALSE;
4330           StorePlayer[jx][jy] = 0;
4331           Tile[jx][jy] = EL_EMPTY;
4332         }
4333       }
4334     }
4335
4336 #else
4337
4338     for (i = 0; i < MAX_PLAYERS; i++)
4339     {
4340       if (stored_player[i].active &&
4341           !tape.player_participates[i])
4342       {
4343         struct PlayerInfo *player = &stored_player[i];
4344         int jx = player->jx, jy = player->jy;
4345
4346         player->active = FALSE;
4347         StorePlayer[jx][jy] = 0;
4348         Tile[jx][jy] = EL_EMPTY;
4349       }
4350     }
4351 #endif
4352   }
4353   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4354   {
4355     // when in single player mode, eliminate all but the local player
4356
4357     for (i = 0; i < MAX_PLAYERS; i++)
4358     {
4359       struct PlayerInfo *player = &stored_player[i];
4360
4361       if (player->active && player != local_player)
4362       {
4363         int jx = player->jx, jy = player->jy;
4364
4365         player->active = FALSE;
4366         player->present = FALSE;
4367
4368         StorePlayer[jx][jy] = 0;
4369         Tile[jx][jy] = EL_EMPTY;
4370       }
4371     }
4372   }
4373
4374   for (i = 0; i < MAX_PLAYERS; i++)
4375     if (stored_player[i].active)
4376       game.players_still_needed++;
4377
4378   if (level.solved_by_one_player)
4379     game.players_still_needed = 1;
4380
4381   // when recording the game, store which players take part in the game
4382   if (tape.recording)
4383   {
4384 #if USE_NEW_PLAYER_ASSIGNMENTS
4385     for (i = 0; i < MAX_PLAYERS; i++)
4386       if (stored_player[i].connected)
4387         tape.player_participates[i] = TRUE;
4388 #else
4389     for (i = 0; i < MAX_PLAYERS; i++)
4390       if (stored_player[i].active)
4391         tape.player_participates[i] = TRUE;
4392 #endif
4393   }
4394
4395 #if DEBUG_INIT_PLAYER
4396   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4397 #endif
4398
4399   if (BorderElement == EL_EMPTY)
4400   {
4401     SBX_Left = 0;
4402     SBX_Right = lev_fieldx - SCR_FIELDX;
4403     SBY_Upper = 0;
4404     SBY_Lower = lev_fieldy - SCR_FIELDY;
4405   }
4406   else
4407   {
4408     SBX_Left = -1;
4409     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4410     SBY_Upper = -1;
4411     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4412   }
4413
4414   if (full_lev_fieldx <= SCR_FIELDX)
4415     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4416   if (full_lev_fieldy <= SCR_FIELDY)
4417     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4418
4419   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4420     SBX_Left--;
4421   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4422     SBY_Upper--;
4423
4424   // if local player not found, look for custom element that might create
4425   // the player (make some assumptions about the right custom element)
4426   if (!local_player->present)
4427   {
4428     int start_x = 0, start_y = 0;
4429     int found_rating = 0;
4430     int found_element = EL_UNDEFINED;
4431     int player_nr = local_player->index_nr;
4432
4433     SCAN_PLAYFIELD(x, y)
4434     {
4435       int element = Tile[x][y];
4436       int content;
4437       int xx, yy;
4438       boolean is_player;
4439
4440       if (level.use_start_element[player_nr] &&
4441           level.start_element[player_nr] == element &&
4442           found_rating < 4)
4443       {
4444         start_x = x;
4445         start_y = y;
4446
4447         found_rating = 4;
4448         found_element = element;
4449       }
4450
4451       if (!IS_CUSTOM_ELEMENT(element))
4452         continue;
4453
4454       if (CAN_CHANGE(element))
4455       {
4456         for (i = 0; i < element_info[element].num_change_pages; i++)
4457         {
4458           // check for player created from custom element as single target
4459           content = element_info[element].change_page[i].target_element;
4460           is_player = IS_PLAYER_ELEMENT(content);
4461
4462           if (is_player && (found_rating < 3 ||
4463                             (found_rating == 3 && element < found_element)))
4464           {
4465             start_x = x;
4466             start_y = y;
4467
4468             found_rating = 3;
4469             found_element = element;
4470           }
4471         }
4472       }
4473
4474       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4475       {
4476         // check for player created from custom element as explosion content
4477         content = element_info[element].content.e[xx][yy];
4478         is_player = IS_PLAYER_ELEMENT(content);
4479
4480         if (is_player && (found_rating < 2 ||
4481                           (found_rating == 2 && element < found_element)))
4482         {
4483           start_x = x + xx - 1;
4484           start_y = y + yy - 1;
4485
4486           found_rating = 2;
4487           found_element = element;
4488         }
4489
4490         if (!CAN_CHANGE(element))
4491           continue;
4492
4493         for (i = 0; i < element_info[element].num_change_pages; i++)
4494         {
4495           // check for player created from custom element as extended target
4496           content =
4497             element_info[element].change_page[i].target_content.e[xx][yy];
4498
4499           is_player = IS_PLAYER_ELEMENT(content);
4500
4501           if (is_player && (found_rating < 1 ||
4502                             (found_rating == 1 && element < found_element)))
4503           {
4504             start_x = x + xx - 1;
4505             start_y = y + yy - 1;
4506
4507             found_rating = 1;
4508             found_element = element;
4509           }
4510         }
4511       }
4512     }
4513
4514     scroll_x = SCROLL_POSITION_X(start_x);
4515     scroll_y = SCROLL_POSITION_Y(start_y);
4516   }
4517   else
4518   {
4519     scroll_x = SCROLL_POSITION_X(local_player->jx);
4520     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4521   }
4522
4523   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4524     scroll_x = game.forced_scroll_x;
4525   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4526     scroll_y = game.forced_scroll_y;
4527
4528   // !!! FIX THIS (START) !!!
4529   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4530   {
4531     InitGameEngine_BD();
4532   }
4533   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4534   {
4535     InitGameEngine_EM();
4536   }
4537   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4538   {
4539     InitGameEngine_SP();
4540   }
4541   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542   {
4543     InitGameEngine_MM();
4544   }
4545   else
4546   {
4547     DrawLevel(REDRAW_FIELD);
4548     DrawAllPlayers();
4549
4550     // after drawing the level, correct some elements
4551     if (game.timegate_time_left == 0)
4552       CloseAllOpenTimegates();
4553   }
4554
4555   // blit playfield from scroll buffer to normal back buffer for fading in
4556   BlitScreenToBitmap(backbuffer);
4557   // !!! FIX THIS (END) !!!
4558
4559   DrawMaskedBorder(fade_mask);
4560
4561   FadeIn(fade_mask);
4562
4563 #if 1
4564   // full screen redraw is required at this point in the following cases:
4565   // - special editor door undrawn when game was started from level editor
4566   // - drawing area (playfield) was changed and has to be removed completely
4567   redraw_mask = REDRAW_ALL;
4568   BackToFront();
4569 #endif
4570
4571   if (!game.restart_level)
4572   {
4573     // copy default game door content to main double buffer
4574
4575     // !!! CHECK AGAIN !!!
4576     SetPanelBackground();
4577     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4578     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4579   }
4580
4581   SetPanelBackground();
4582   SetDrawBackgroundMask(REDRAW_DOOR_1);
4583
4584   UpdateAndDisplayGameControlValues();
4585
4586   if (!game.restart_level)
4587   {
4588     UnmapGameButtons();
4589     UnmapTapeButtons();
4590
4591     FreeGameButtons();
4592     CreateGameButtons();
4593
4594     MapGameButtons();
4595     MapTapeButtons();
4596
4597     // copy actual game door content to door double buffer for OpenDoor()
4598     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4599
4600     OpenDoor(DOOR_OPEN_ALL);
4601
4602     KeyboardAutoRepeatOffUnlessAutoplay();
4603
4604 #if DEBUG_INIT_PLAYER
4605     DebugPrintPlayerStatus("Player status (final)");
4606 #endif
4607   }
4608
4609   UnmapAllGadgets();
4610
4611   MapGameButtons();
4612   MapTapeButtons();
4613
4614   if (!game.restart_level && !tape.playing)
4615   {
4616     LevelStats_incPlayed(level_nr);
4617
4618     SaveLevelSetup_SeriesInfo();
4619   }
4620
4621   game.restart_level = FALSE;
4622   game.request_active = FALSE;
4623   game.envelope_active = FALSE;
4624   game.any_door_active = FALSE;
4625
4626   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4627     InitGameActions_MM();
4628
4629   SaveEngineSnapshotToListInitial();
4630
4631   if (!game.restart_level)
4632   {
4633     PlaySound(SND_GAME_STARTING);
4634
4635     if (setup.sound_music)
4636       PlayLevelMusic();
4637   }
4638
4639   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4640 }
4641
4642 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4643                         int actual_player_x, int actual_player_y)
4644 {
4645   // this is used for non-R'n'D game engines to update certain engine values
4646
4647   // needed to determine if sounds are played within the visible screen area
4648   scroll_x = actual_scroll_x;
4649   scroll_y = actual_scroll_y;
4650
4651   // needed to get player position for "follow finger" playing input method
4652   local_player->jx = actual_player_x;
4653   local_player->jy = actual_player_y;
4654 }
4655
4656 void InitMovDir(int x, int y)
4657 {
4658   int i, element = Tile[x][y];
4659   static int xy[4][2] =
4660   {
4661     {  0, +1 },
4662     { +1,  0 },
4663     {  0, -1 },
4664     { -1,  0 }
4665   };
4666   static int direction[3][4] =
4667   {
4668     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4669     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4670     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4671   };
4672
4673   switch (element)
4674   {
4675     case EL_BUG_RIGHT:
4676     case EL_BUG_UP:
4677     case EL_BUG_LEFT:
4678     case EL_BUG_DOWN:
4679       Tile[x][y] = EL_BUG;
4680       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4681       break;
4682
4683     case EL_SPACESHIP_RIGHT:
4684     case EL_SPACESHIP_UP:
4685     case EL_SPACESHIP_LEFT:
4686     case EL_SPACESHIP_DOWN:
4687       Tile[x][y] = EL_SPACESHIP;
4688       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4689       break;
4690
4691     case EL_BD_BUTTERFLY_RIGHT:
4692     case EL_BD_BUTTERFLY_UP:
4693     case EL_BD_BUTTERFLY_LEFT:
4694     case EL_BD_BUTTERFLY_DOWN:
4695       Tile[x][y] = EL_BD_BUTTERFLY;
4696       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4697       break;
4698
4699     case EL_BD_FIREFLY_RIGHT:
4700     case EL_BD_FIREFLY_UP:
4701     case EL_BD_FIREFLY_LEFT:
4702     case EL_BD_FIREFLY_DOWN:
4703       Tile[x][y] = EL_BD_FIREFLY;
4704       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4705       break;
4706
4707     case EL_PACMAN_RIGHT:
4708     case EL_PACMAN_UP:
4709     case EL_PACMAN_LEFT:
4710     case EL_PACMAN_DOWN:
4711       Tile[x][y] = EL_PACMAN;
4712       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4713       break;
4714
4715     case EL_YAMYAM_LEFT:
4716     case EL_YAMYAM_RIGHT:
4717     case EL_YAMYAM_UP:
4718     case EL_YAMYAM_DOWN:
4719       Tile[x][y] = EL_YAMYAM;
4720       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4721       break;
4722
4723     case EL_SP_SNIKSNAK:
4724       MovDir[x][y] = MV_UP;
4725       break;
4726
4727     case EL_SP_ELECTRON:
4728       MovDir[x][y] = MV_LEFT;
4729       break;
4730
4731     case EL_MOLE_LEFT:
4732     case EL_MOLE_RIGHT:
4733     case EL_MOLE_UP:
4734     case EL_MOLE_DOWN:
4735       Tile[x][y] = EL_MOLE;
4736       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4737       break;
4738
4739     case EL_SPRING_LEFT:
4740     case EL_SPRING_RIGHT:
4741       Tile[x][y] = EL_SPRING;
4742       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4743       break;
4744
4745     default:
4746       if (IS_CUSTOM_ELEMENT(element))
4747       {
4748         struct ElementInfo *ei = &element_info[element];
4749         int move_direction_initial = ei->move_direction_initial;
4750         int move_pattern = ei->move_pattern;
4751
4752         if (move_direction_initial == MV_START_PREVIOUS)
4753         {
4754           if (MovDir[x][y] != MV_NONE)
4755             return;
4756
4757           move_direction_initial = MV_START_AUTOMATIC;
4758         }
4759
4760         if (move_direction_initial == MV_START_RANDOM)
4761           MovDir[x][y] = 1 << RND(4);
4762         else if (move_direction_initial & MV_ANY_DIRECTION)
4763           MovDir[x][y] = move_direction_initial;
4764         else if (move_pattern == MV_ALL_DIRECTIONS ||
4765                  move_pattern == MV_TURNING_LEFT ||
4766                  move_pattern == MV_TURNING_RIGHT ||
4767                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4768                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4769                  move_pattern == MV_TURNING_RANDOM)
4770           MovDir[x][y] = 1 << RND(4);
4771         else if (move_pattern == MV_HORIZONTAL)
4772           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4773         else if (move_pattern == MV_VERTICAL)
4774           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4775         else if (move_pattern & MV_ANY_DIRECTION)
4776           MovDir[x][y] = element_info[element].move_pattern;
4777         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4778                  move_pattern == MV_ALONG_RIGHT_SIDE)
4779         {
4780           // use random direction as default start direction
4781           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4782             MovDir[x][y] = 1 << RND(4);
4783
4784           for (i = 0; i < NUM_DIRECTIONS; i++)
4785           {
4786             int x1 = x + xy[i][0];
4787             int y1 = y + xy[i][1];
4788
4789             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4790             {
4791               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4792                 MovDir[x][y] = direction[0][i];
4793               else
4794                 MovDir[x][y] = direction[1][i];
4795
4796               break;
4797             }
4798           }
4799         }                
4800       }
4801       else
4802       {
4803         MovDir[x][y] = 1 << RND(4);
4804
4805         if (element != EL_BUG &&
4806             element != EL_SPACESHIP &&
4807             element != EL_BD_BUTTERFLY &&
4808             element != EL_BD_FIREFLY)
4809           break;
4810
4811         for (i = 0; i < NUM_DIRECTIONS; i++)
4812         {
4813           int x1 = x + xy[i][0];
4814           int y1 = y + xy[i][1];
4815
4816           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4817           {
4818             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4819             {
4820               MovDir[x][y] = direction[0][i];
4821               break;
4822             }
4823             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4824                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4825             {
4826               MovDir[x][y] = direction[1][i];
4827               break;
4828             }
4829           }
4830         }
4831       }
4832       break;
4833   }
4834
4835   GfxDir[x][y] = MovDir[x][y];
4836 }
4837
4838 void InitAmoebaNr(int x, int y)
4839 {
4840   int i;
4841   int group_nr = AmoebaNeighbourNr(x, y);
4842
4843   if (group_nr == 0)
4844   {
4845     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4846     {
4847       if (AmoebaCnt[i] == 0)
4848       {
4849         group_nr = i;
4850         break;
4851       }
4852     }
4853   }
4854
4855   AmoebaNr[x][y] = group_nr;
4856   AmoebaCnt[group_nr]++;
4857   AmoebaCnt2[group_nr]++;
4858 }
4859
4860 static void LevelSolved_SetFinalGameValues(void)
4861 {
4862   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4863                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4864   game.score_time_final = (level.use_step_counter ? TimePlayed :
4865                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4866
4867   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4868                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4869                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4870                       game.score);
4871
4872   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4873                        MM_HEALTH(game_mm.laser_overload_value) :
4874                        game.health);
4875
4876   game.LevelSolved_CountingTime = game.time_final;
4877   game.LevelSolved_CountingScore = game.score_final;
4878   game.LevelSolved_CountingHealth = game.health_final;
4879 }
4880
4881 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4882 {
4883   game.LevelSolved_CountingTime = time;
4884   game.LevelSolved_CountingScore = score;
4885   game.LevelSolved_CountingHealth = health;
4886
4887   game_panel_controls[GAME_PANEL_TIME].value = time;
4888   game_panel_controls[GAME_PANEL_SCORE].value = score;
4889   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4890
4891   DisplayGameControlValues();
4892 }
4893
4894 static void LevelSolved(void)
4895 {
4896   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4897       game.players_still_needed > 0)
4898     return;
4899
4900   game.LevelSolved = TRUE;
4901   game.GameOver = TRUE;
4902
4903   tape.solved = TRUE;
4904
4905   // needed here to display correct panel values while player walks into exit
4906   LevelSolved_SetFinalGameValues();
4907 }
4908
4909 static boolean AdvanceToNextLevel(void)
4910 {
4911   if (setup.increment_levels &&
4912       level_nr < leveldir_current->last_level &&
4913       !network_playing)
4914   {
4915     level_nr++;         // advance to next level
4916     TapeErase();        // start with empty tape
4917
4918     if (setup.auto_play_next_level)
4919     {
4920       scores.continue_playing = TRUE;
4921       scores.next_level_nr = level_nr;
4922
4923       LoadLevel(level_nr);
4924
4925       SaveLevelSetup_SeriesInfo();
4926     }
4927
4928     return TRUE;
4929   }
4930
4931   return FALSE;
4932 }
4933
4934 void GameWon(void)
4935 {
4936   static int time_count_steps;
4937   static int time, time_final;
4938   static float score, score_final; // needed for time score < 10 for 10 seconds
4939   static int health, health_final;
4940   static int game_over_delay_1 = 0;
4941   static int game_over_delay_2 = 0;
4942   static int game_over_delay_3 = 0;
4943   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4944   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4945
4946   if (!game.LevelSolved_GameWon)
4947   {
4948     int i;
4949
4950     // do not start end game actions before the player stops moving (to exit)
4951     if (local_player->active && local_player->MovPos)
4952       return;
4953
4954     // calculate final game values after player finished walking into exit
4955     LevelSolved_SetFinalGameValues();
4956
4957     game.LevelSolved_GameWon = TRUE;
4958     game.LevelSolved_SaveTape = tape.recording;
4959     game.LevelSolved_SaveScore = !tape.playing;
4960
4961     if (!tape.playing)
4962     {
4963       LevelStats_incSolved(level_nr);
4964
4965       SaveLevelSetup_SeriesInfo();
4966     }
4967
4968     if (tape.auto_play)         // tape might already be stopped here
4969       tape.auto_play_level_solved = TRUE;
4970
4971     TapeStop();
4972
4973     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4974     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4975     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4976
4977     time = time_final = game.time_final;
4978     score = score_final = game.score_final;
4979     health = health_final = game.health_final;
4980
4981     // update game panel values before (delayed) counting of score (if any)
4982     LevelSolved_DisplayFinalGameValues(time, score, health);
4983
4984     // if level has time score defined, calculate new final game values
4985     if (time_score > 0)
4986     {
4987       int time_final_max = 999;
4988       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4989       int time_frames = 0;
4990       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4991       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4992
4993       if (TimeLeft > 0)
4994       {
4995         time_final = 0;
4996         time_frames = time_frames_left;
4997       }
4998       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4999       {
5000         time_final = time_final_max;
5001         time_frames = time_frames_final_max - time_frames_played;
5002       }
5003
5004       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
5005
5006       time_count_steps = MAX(1, ABS(time_final - time) / 100);
5007
5008       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
5009       {
5010         // keep previous values (final values already processed here)
5011         time_final = time;
5012         score_final = score;
5013       }
5014       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
5015       {
5016         health_final = 0;
5017         score_final += health * time_score;
5018       }
5019
5020       game.score_final = score_final;
5021       game.health_final = health_final;
5022     }
5023
5024     // if not counting score after game, immediately update game panel values
5025     if (level_editor_test_game || !setup.count_score_after_game ||
5026         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5027     {
5028       time = time_final;
5029       score = score_final;
5030
5031       LevelSolved_DisplayFinalGameValues(time, score, health);
5032     }
5033
5034     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5035     {
5036       // check if last player has left the level
5037       if (game.exit_x >= 0 &&
5038           game.exit_y >= 0)
5039       {
5040         int x = game.exit_x;
5041         int y = game.exit_y;
5042         int element = Tile[x][y];
5043
5044         // close exit door after last player
5045         if ((game.all_players_gone &&
5046              (element == EL_EXIT_OPEN ||
5047               element == EL_SP_EXIT_OPEN ||
5048               element == EL_STEEL_EXIT_OPEN)) ||
5049             element == EL_EM_EXIT_OPEN ||
5050             element == EL_EM_STEEL_EXIT_OPEN)
5051         {
5052
5053           Tile[x][y] =
5054             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5055              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5056              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5057              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5058              EL_EM_STEEL_EXIT_CLOSING);
5059
5060           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5061         }
5062
5063         // player disappears
5064         DrawLevelField(x, y);
5065       }
5066
5067       for (i = 0; i < MAX_PLAYERS; i++)
5068       {
5069         struct PlayerInfo *player = &stored_player[i];
5070
5071         if (player->present)
5072         {
5073           RemovePlayer(player);
5074
5075           // player disappears
5076           DrawLevelField(player->jx, player->jy);
5077         }
5078       }
5079     }
5080
5081     PlaySound(SND_GAME_WINNING);
5082   }
5083
5084   if (setup.count_score_after_game)
5085   {
5086     if (time != time_final)
5087     {
5088       if (game_over_delay_1 > 0)
5089       {
5090         game_over_delay_1--;
5091
5092         return;
5093       }
5094
5095       int time_to_go = ABS(time_final - time);
5096       int time_count_dir = (time < time_final ? +1 : -1);
5097
5098       if (time_to_go < time_count_steps)
5099         time_count_steps = 1;
5100
5101       time  += time_count_steps * time_count_dir;
5102       score += time_count_steps * time_score;
5103
5104       // set final score to correct rounding differences after counting score
5105       if (time == time_final)
5106         score = score_final;
5107
5108       LevelSolved_DisplayFinalGameValues(time, score, health);
5109
5110       if (time == time_final)
5111         StopSound(SND_GAME_LEVELTIME_BONUS);
5112       else if (setup.sound_loops)
5113         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5114       else
5115         PlaySound(SND_GAME_LEVELTIME_BONUS);
5116
5117       return;
5118     }
5119
5120     if (health != health_final)
5121     {
5122       if (game_over_delay_2 > 0)
5123       {
5124         game_over_delay_2--;
5125
5126         return;
5127       }
5128
5129       int health_count_dir = (health < health_final ? +1 : -1);
5130
5131       health += health_count_dir;
5132       score  += time_score;
5133
5134       LevelSolved_DisplayFinalGameValues(time, score, health);
5135
5136       if (health == health_final)
5137         StopSound(SND_GAME_LEVELTIME_BONUS);
5138       else if (setup.sound_loops)
5139         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5140       else
5141         PlaySound(SND_GAME_LEVELTIME_BONUS);
5142
5143       return;
5144     }
5145   }
5146
5147   game.panel.active = FALSE;
5148
5149   if (game_over_delay_3 > 0)
5150   {
5151     game_over_delay_3--;
5152
5153     return;
5154   }
5155
5156   GameEnd();
5157 }
5158
5159 void GameEnd(void)
5160 {
5161   // used instead of "level_nr" (needed for network games)
5162   int last_level_nr = levelset.level_nr;
5163   boolean tape_saved = FALSE;
5164   boolean game_over = checkGameFailed();
5165
5166   // Important note: This function is not only called after "GameWon()", but also after
5167   // "game over" (if automatically asking for restarting the game is disabled in setup)
5168
5169   // do not handle game end if game over and automatically asking for game restart
5170   if (game_over && setup.ask_on_game_over)
5171     return;
5172
5173   // do not handle game end if request dialog is already active
5174   if (checkRequestActive())
5175     return;
5176
5177   if (game.LevelSolved)
5178     game.LevelSolved_GameEnd = TRUE;
5179
5180   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5181   {
5182     // make sure that request dialog to save tape does not open door again
5183     if (!global.use_envelope_request)
5184       CloseDoor(DOOR_CLOSE_1);
5185
5186     // ask to save tape
5187     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5188
5189     // set unique basename for score tape (also saved in high score table)
5190     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5191   }
5192
5193   // if no tape is to be saved, close both doors simultaneously
5194   CloseDoor(DOOR_CLOSE_ALL);
5195
5196   if (level_editor_test_game || score_info_tape_play)
5197   {
5198     SetGameStatus(GAME_MODE_MAIN);
5199
5200     DrawMainMenu();
5201
5202     return;
5203   }
5204
5205   if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission))
5206   {
5207     SetGameStatus(GAME_MODE_MAIN);
5208
5209     DrawMainMenu();
5210
5211     return;
5212   }
5213
5214   if (level_nr == leveldir_current->handicap_level)
5215   {
5216     leveldir_current->handicap_level++;
5217
5218     SaveLevelSetup_SeriesInfo();
5219   }
5220
5221   // save score and score tape before potentially erasing tape below
5222   if (game.LevelSolved_SaveScore)
5223     NewHighScore(last_level_nr, tape_saved);
5224
5225   // increment and load next level (if possible and not configured otherwise)
5226   AdvanceToNextLevel();
5227
5228   if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game)
5229   {
5230     SetGameStatus(GAME_MODE_SCORES);
5231
5232     DrawHallOfFame(last_level_nr);
5233   }
5234   else if (scores.continue_playing)
5235   {
5236     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5237   }
5238   else
5239   {
5240     SetGameStatus(GAME_MODE_MAIN);
5241
5242     DrawMainMenu();
5243   }
5244 }
5245
5246 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5247                          boolean one_score_entry_per_name)
5248 {
5249   int i;
5250
5251   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5252     return -1;
5253
5254   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5255   {
5256     struct ScoreEntry *entry = &list->entry[i];
5257     boolean score_is_better = (new_entry->score >  entry->score);
5258     boolean score_is_equal  = (new_entry->score == entry->score);
5259     boolean time_is_better  = (new_entry->time  <  entry->time);
5260     boolean time_is_equal   = (new_entry->time  == entry->time);
5261     boolean better_by_score = (score_is_better ||
5262                                (score_is_equal && time_is_better));
5263     boolean better_by_time  = (time_is_better ||
5264                                (time_is_equal && score_is_better));
5265     boolean is_better = (level.rate_time_over_score ? better_by_time :
5266                          better_by_score);
5267     boolean entry_is_empty = (entry->score == 0 &&
5268                               entry->time == 0);
5269
5270     // prevent adding server score entries if also existing in local score file
5271     // (special case: historic score entries have an empty tape basename entry)
5272     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5273         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5274     {
5275       // add fields from server score entry not stored in local score entry
5276       // (currently, this means setting platform, version and country fields;
5277       // in rare cases, this may also correct an invalid score value, as
5278       // historic scores might have been truncated to 16-bit values locally)
5279       *entry = *new_entry;
5280
5281       return -1;
5282     }
5283
5284     if (is_better || entry_is_empty)
5285     {
5286       // player has made it to the hall of fame
5287
5288       if (i < MAX_SCORE_ENTRIES - 1)
5289       {
5290         int m = MAX_SCORE_ENTRIES - 1;
5291         int l;
5292
5293         if (one_score_entry_per_name)
5294         {
5295           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5296             if (strEqual(list->entry[l].name, new_entry->name))
5297               m = l;
5298
5299           if (m == i)   // player's new highscore overwrites his old one
5300             goto put_into_list;
5301         }
5302
5303         for (l = m; l > i; l--)
5304           list->entry[l] = list->entry[l - 1];
5305       }
5306
5307       put_into_list:
5308
5309       *entry = *new_entry;
5310
5311       return i;
5312     }
5313     else if (one_score_entry_per_name &&
5314              strEqual(entry->name, new_entry->name))
5315     {
5316       // player already in high score list with better score or time
5317
5318       return -1;
5319     }
5320   }
5321
5322   // special case: new score is beyond the last high score list position
5323   return MAX_SCORE_ENTRIES;
5324 }
5325
5326 void NewHighScore(int level_nr, boolean tape_saved)
5327 {
5328   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5329   boolean one_per_name = FALSE;
5330
5331   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5332   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5333
5334   new_entry.score = game.score_final;
5335   new_entry.time = game.score_time_final;
5336
5337   LoadScore(level_nr);
5338
5339   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5340
5341   if (scores.last_added >= MAX_SCORE_ENTRIES)
5342   {
5343     scores.last_added = MAX_SCORE_ENTRIES - 1;
5344     scores.force_last_added = TRUE;
5345
5346     scores.entry[scores.last_added] = new_entry;
5347
5348     // store last added local score entry (before merging server scores)
5349     scores.last_added_local = scores.last_added;
5350
5351     return;
5352   }
5353
5354   if (scores.last_added < 0)
5355     return;
5356
5357   SaveScore(level_nr);
5358
5359   // store last added local score entry (before merging server scores)
5360   scores.last_added_local = scores.last_added;
5361
5362   if (!game.LevelSolved_SaveTape)
5363     return;
5364
5365   SaveScoreTape(level_nr);
5366
5367   if (setup.ask_for_using_api_server)
5368   {
5369     setup.use_api_server =
5370       Request("Upload your score and tape to the high score server?", REQ_ASK);
5371
5372     if (!setup.use_api_server)
5373       Request("Not using high score server! Use setup menu to enable again!",
5374               REQ_CONFIRM);
5375
5376     runtime.use_api_server = setup.use_api_server;
5377
5378     // after asking for using API server once, do not ask again
5379     setup.ask_for_using_api_server = FALSE;
5380
5381     SaveSetup_ServerSetup();
5382   }
5383
5384   SaveServerScore(level_nr, tape_saved);
5385 }
5386
5387 void MergeServerScore(void)
5388 {
5389   struct ScoreEntry last_added_entry;
5390   boolean one_per_name = FALSE;
5391   int i;
5392
5393   if (scores.last_added >= 0)
5394     last_added_entry = scores.entry[scores.last_added];
5395
5396   for (i = 0; i < server_scores.num_entries; i++)
5397   {
5398     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5399
5400     if (pos >= 0 && pos <= scores.last_added)
5401       scores.last_added++;
5402   }
5403
5404   if (scores.last_added >= MAX_SCORE_ENTRIES)
5405   {
5406     scores.last_added = MAX_SCORE_ENTRIES - 1;
5407     scores.force_last_added = TRUE;
5408
5409     scores.entry[scores.last_added] = last_added_entry;
5410   }
5411 }
5412
5413 static int getElementMoveStepsizeExt(int x, int y, int direction)
5414 {
5415   int element = Tile[x][y];
5416   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5417   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5418   int horiz_move = (dx != 0);
5419   int sign = (horiz_move ? dx : dy);
5420   int step = sign * element_info[element].move_stepsize;
5421
5422   // special values for move stepsize for spring and things on conveyor belt
5423   if (horiz_move)
5424   {
5425     if (CAN_FALL(element) &&
5426         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5427       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5428     else if (element == EL_SPRING)
5429       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5430   }
5431
5432   return step;
5433 }
5434
5435 static int getElementMoveStepsize(int x, int y)
5436 {
5437   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5438 }
5439
5440 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5441 {
5442   if (player->GfxAction != action || player->GfxDir != dir)
5443   {
5444     player->GfxAction = action;
5445     player->GfxDir = dir;
5446     player->Frame = 0;
5447     player->StepFrame = 0;
5448   }
5449 }
5450
5451 static void ResetGfxFrame(int x, int y)
5452 {
5453   // profiling showed that "autotest" spends 10~20% of its time in this function
5454   if (DrawingDeactivatedField())
5455     return;
5456
5457   int element = Tile[x][y];
5458   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5459
5460   if (graphic_info[graphic].anim_global_sync)
5461     GfxFrame[x][y] = FrameCounter;
5462   else if (graphic_info[graphic].anim_global_anim_sync)
5463     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5464   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5465     GfxFrame[x][y] = CustomValue[x][y];
5466   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5467     GfxFrame[x][y] = element_info[element].collect_score;
5468   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5469     GfxFrame[x][y] = ChangeDelay[x][y];
5470 }
5471
5472 static void ResetGfxAnimation(int x, int y)
5473 {
5474   GfxAction[x][y] = ACTION_DEFAULT;
5475   GfxDir[x][y] = MovDir[x][y];
5476   GfxFrame[x][y] = 0;
5477
5478   ResetGfxFrame(x, y);
5479 }
5480
5481 static void ResetRandomAnimationValue(int x, int y)
5482 {
5483   GfxRandom[x][y] = INIT_GFX_RANDOM();
5484 }
5485
5486 static void InitMovingField(int x, int y, int direction)
5487 {
5488   int element = Tile[x][y];
5489   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5490   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5491   int newx = x + dx;
5492   int newy = y + dy;
5493   boolean is_moving_before, is_moving_after;
5494
5495   // check if element was/is moving or being moved before/after mode change
5496   is_moving_before = (WasJustMoving[x][y] != 0);
5497   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5498
5499   // reset animation only for moving elements which change direction of moving
5500   // or which just started or stopped moving
5501   // (else CEs with property "can move" / "not moving" are reset each frame)
5502   if (is_moving_before != is_moving_after ||
5503       direction != MovDir[x][y])
5504     ResetGfxAnimation(x, y);
5505
5506   MovDir[x][y] = direction;
5507   GfxDir[x][y] = direction;
5508
5509   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5510                      direction == MV_DOWN && CAN_FALL(element) ?
5511                      ACTION_FALLING : ACTION_MOVING);
5512
5513   // this is needed for CEs with property "can move" / "not moving"
5514
5515   if (is_moving_after)
5516   {
5517     if (Tile[newx][newy] == EL_EMPTY)
5518       Tile[newx][newy] = EL_BLOCKED;
5519
5520     MovDir[newx][newy] = MovDir[x][y];
5521
5522     CustomValue[newx][newy] = CustomValue[x][y];
5523
5524     GfxFrame[newx][newy] = GfxFrame[x][y];
5525     GfxRandom[newx][newy] = GfxRandom[x][y];
5526     GfxAction[newx][newy] = GfxAction[x][y];
5527     GfxDir[newx][newy] = GfxDir[x][y];
5528   }
5529 }
5530
5531 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5532 {
5533   int direction = MovDir[x][y];
5534   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5535   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5536
5537   *goes_to_x = newx;
5538   *goes_to_y = newy;
5539 }
5540
5541 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5542 {
5543   int direction = MovDir[x][y];
5544   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5545   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5546
5547   *comes_from_x = oldx;
5548   *comes_from_y = oldy;
5549 }
5550
5551 static int MovingOrBlocked2Element(int x, int y)
5552 {
5553   int element = Tile[x][y];
5554
5555   if (element == EL_BLOCKED)
5556   {
5557     int oldx, oldy;
5558
5559     Blocked2Moving(x, y, &oldx, &oldy);
5560
5561     return Tile[oldx][oldy];
5562   }
5563
5564   return element;
5565 }
5566
5567 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5568 {
5569   // like MovingOrBlocked2Element(), but if element is moving
5570   // and (x, y) is the field the moving element is just leaving,
5571   // return EL_BLOCKED instead of the element value
5572   int element = Tile[x][y];
5573
5574   if (IS_MOVING(x, y))
5575   {
5576     if (element == EL_BLOCKED)
5577     {
5578       int oldx, oldy;
5579
5580       Blocked2Moving(x, y, &oldx, &oldy);
5581       return Tile[oldx][oldy];
5582     }
5583     else
5584       return EL_BLOCKED;
5585   }
5586   else
5587     return element;
5588 }
5589
5590 static void RemoveField(int x, int y)
5591 {
5592   Tile[x][y] = EL_EMPTY;
5593
5594   MovPos[x][y] = 0;
5595   MovDir[x][y] = 0;
5596   MovDelay[x][y] = 0;
5597
5598   CustomValue[x][y] = 0;
5599
5600   AmoebaNr[x][y] = 0;
5601   ChangeDelay[x][y] = 0;
5602   ChangePage[x][y] = -1;
5603   Pushed[x][y] = FALSE;
5604
5605   GfxElement[x][y] = EL_UNDEFINED;
5606   GfxAction[x][y] = ACTION_DEFAULT;
5607   GfxDir[x][y] = MV_NONE;
5608 }
5609
5610 static void RemoveMovingField(int x, int y)
5611 {
5612   int oldx = x, oldy = y, newx = x, newy = y;
5613   int element = Tile[x][y];
5614   int next_element = EL_UNDEFINED;
5615
5616   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5617     return;
5618
5619   if (IS_MOVING(x, y))
5620   {
5621     Moving2Blocked(x, y, &newx, &newy);
5622
5623     if (Tile[newx][newy] != EL_BLOCKED)
5624     {
5625       // element is moving, but target field is not free (blocked), but
5626       // already occupied by something different (example: acid pool);
5627       // in this case, only remove the moving field, but not the target
5628
5629       RemoveField(oldx, oldy);
5630
5631       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5632
5633       TEST_DrawLevelField(oldx, oldy);
5634
5635       return;
5636     }
5637   }
5638   else if (element == EL_BLOCKED)
5639   {
5640     Blocked2Moving(x, y, &oldx, &oldy);
5641     if (!IS_MOVING(oldx, oldy))
5642       return;
5643   }
5644
5645   if (element == EL_BLOCKED &&
5646       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5647        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5648        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5649        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5650        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5651        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5652     next_element = get_next_element(Tile[oldx][oldy]);
5653
5654   RemoveField(oldx, oldy);
5655   RemoveField(newx, newy);
5656
5657   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5658
5659   if (next_element != EL_UNDEFINED)
5660     Tile[oldx][oldy] = next_element;
5661
5662   TEST_DrawLevelField(oldx, oldy);
5663   TEST_DrawLevelField(newx, newy);
5664 }
5665
5666 void DrawDynamite(int x, int y)
5667 {
5668   int sx = SCREENX(x), sy = SCREENY(y);
5669   int graphic = el2img(Tile[x][y]);
5670   int frame;
5671
5672   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5673     return;
5674
5675   if (IS_WALKABLE_INSIDE(Back[x][y]))
5676     return;
5677
5678   if (Back[x][y])
5679     DrawLevelElement(x, y, Back[x][y]);
5680   else if (Store[x][y])
5681     DrawLevelElement(x, y, Store[x][y]);
5682   else if (game.use_masked_elements)
5683     DrawLevelElement(x, y, EL_EMPTY);
5684
5685   frame = getGraphicAnimationFrameXY(graphic, x, y);
5686
5687   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5688     DrawGraphicThruMask(sx, sy, graphic, frame);
5689   else
5690     DrawGraphic(sx, sy, graphic, frame);
5691 }
5692
5693 static void CheckDynamite(int x, int y)
5694 {
5695   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5696   {
5697     MovDelay[x][y]--;
5698
5699     if (MovDelay[x][y] != 0)
5700     {
5701       DrawDynamite(x, y);
5702       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5703
5704       return;
5705     }
5706   }
5707
5708   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5709
5710   Bang(x, y);
5711 }
5712
5713 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5714 {
5715   boolean num_checked_players = 0;
5716   int i;
5717
5718   for (i = 0; i < MAX_PLAYERS; i++)
5719   {
5720     if (stored_player[i].active)
5721     {
5722       int sx = stored_player[i].jx;
5723       int sy = stored_player[i].jy;
5724
5725       if (num_checked_players == 0)
5726       {
5727         *sx1 = *sx2 = sx;
5728         *sy1 = *sy2 = sy;
5729       }
5730       else
5731       {
5732         *sx1 = MIN(*sx1, sx);
5733         *sy1 = MIN(*sy1, sy);
5734         *sx2 = MAX(*sx2, sx);
5735         *sy2 = MAX(*sy2, sy);
5736       }
5737
5738       num_checked_players++;
5739     }
5740   }
5741 }
5742
5743 static boolean checkIfAllPlayersFitToScreen_RND(void)
5744 {
5745   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5746
5747   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5748
5749   return (sx2 - sx1 < SCR_FIELDX &&
5750           sy2 - sy1 < SCR_FIELDY);
5751 }
5752
5753 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5754 {
5755   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5756
5757   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5758
5759   *sx = (sx1 + sx2) / 2;
5760   *sy = (sy1 + sy2) / 2;
5761 }
5762
5763 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5764                                boolean center_screen, boolean quick_relocation)
5765 {
5766   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5767   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5768   boolean no_delay = (tape.warp_forward);
5769   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5770   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5771   int new_scroll_x, new_scroll_y;
5772
5773   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5774   {
5775     // case 1: quick relocation inside visible screen (without scrolling)
5776
5777     RedrawPlayfield();
5778
5779     return;
5780   }
5781
5782   if (!level.shifted_relocation || center_screen)
5783   {
5784     // relocation _with_ centering of screen
5785
5786     new_scroll_x = SCROLL_POSITION_X(x);
5787     new_scroll_y = SCROLL_POSITION_Y(y);
5788   }
5789   else
5790   {
5791     // relocation _without_ centering of screen
5792
5793     // apply distance between old and new player position to scroll position
5794     int shifted_scroll_x = scroll_x + (x - old_x);
5795     int shifted_scroll_y = scroll_y + (y - old_y);
5796
5797     // make sure that shifted scroll position does not scroll beyond screen
5798     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5799     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5800
5801     // special case for teleporting from one end of the playfield to the other
5802     // (this kludge prevents the destination area to be shifted by half a tile
5803     // against the source destination for even screen width or screen height;
5804     // probably most useful when used with high "game.forced_scroll_delay_value"
5805     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5806     if (quick_relocation)
5807     {
5808       if (EVEN(SCR_FIELDX))
5809       {
5810         // relocate (teleport) between left and right border (half or full)
5811         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5812           new_scroll_x = SBX_Right;
5813         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5814           new_scroll_x = SBX_Right - 1;
5815         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5816           new_scroll_x = SBX_Left;
5817         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5818           new_scroll_x = SBX_Left + 1;
5819       }
5820
5821       if (EVEN(SCR_FIELDY))
5822       {
5823         // relocate (teleport) between top and bottom border (half or full)
5824         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5825           new_scroll_y = SBY_Lower;
5826         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5827           new_scroll_y = SBY_Lower - 1;
5828         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5829           new_scroll_y = SBY_Upper;
5830         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5831           new_scroll_y = SBY_Upper + 1;
5832       }
5833     }
5834   }
5835
5836   if (quick_relocation)
5837   {
5838     // case 2: quick relocation (redraw without visible scrolling)
5839
5840     scroll_x = new_scroll_x;
5841     scroll_y = new_scroll_y;
5842
5843     RedrawPlayfield();
5844
5845     return;
5846   }
5847
5848   // case 3: visible relocation (with scrolling to new position)
5849
5850   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5851
5852   SetVideoFrameDelay(wait_delay_value);
5853
5854   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5855   {
5856     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5857     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5858
5859     if (dx == 0 && dy == 0)             // no scrolling needed at all
5860       break;
5861
5862     scroll_x -= dx;
5863     scroll_y -= dy;
5864
5865     // set values for horizontal/vertical screen scrolling (half tile size)
5866     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5867     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5868     int pos_x = dx * TILEX / 2;
5869     int pos_y = dy * TILEY / 2;
5870     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5871     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5872
5873     ScrollLevel(dx, dy);
5874     DrawAllPlayers();
5875
5876     // scroll in two steps of half tile size to make things smoother
5877     BlitScreenToBitmapExt_RND(window, fx, fy);
5878
5879     // scroll second step to align at full tile size
5880     BlitScreenToBitmap(window);
5881   }
5882
5883   DrawAllPlayers();
5884   BackToFront();
5885
5886   SetVideoFrameDelay(frame_delay_value_old);
5887 }
5888
5889 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5890 {
5891   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5892   int player_nr = GET_PLAYER_NR(el_player);
5893   struct PlayerInfo *player = &stored_player[player_nr];
5894   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5895   boolean no_delay = (tape.warp_forward);
5896   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5897   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5898   int old_jx = player->jx;
5899   int old_jy = player->jy;
5900   int old_element = Tile[old_jx][old_jy];
5901   int element = Tile[jx][jy];
5902   boolean player_relocated = (old_jx != jx || old_jy != jy);
5903
5904   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5905   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5906   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5907   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5908   int leave_side_horiz = move_dir_horiz;
5909   int leave_side_vert  = move_dir_vert;
5910   int enter_side = enter_side_horiz | enter_side_vert;
5911   int leave_side = leave_side_horiz | leave_side_vert;
5912
5913   if (player->buried)           // do not reanimate dead player
5914     return;
5915
5916   if (!player_relocated)        // no need to relocate the player
5917     return;
5918
5919   if (IS_PLAYER(jx, jy))        // player already placed at new position
5920   {
5921     RemoveField(jx, jy);        // temporarily remove newly placed player
5922     DrawLevelField(jx, jy);
5923   }
5924
5925   if (player->present)
5926   {
5927     while (player->MovPos)
5928     {
5929       ScrollPlayer(player, SCROLL_GO_ON);
5930       ScrollScreen(NULL, SCROLL_GO_ON);
5931
5932       AdvanceFrameAndPlayerCounters(player->index_nr);
5933
5934       DrawPlayer(player);
5935
5936       BackToFront_WithFrameDelay(wait_delay_value);
5937     }
5938
5939     DrawPlayer(player);         // needed here only to cleanup last field
5940     DrawLevelField(player->jx, player->jy);     // remove player graphic
5941
5942     player->is_moving = FALSE;
5943   }
5944
5945   if (IS_CUSTOM_ELEMENT(old_element))
5946     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5947                                CE_LEFT_BY_PLAYER,
5948                                player->index_bit, leave_side);
5949
5950   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5951                                       CE_PLAYER_LEAVES_X,
5952                                       player->index_bit, leave_side);
5953
5954   Tile[jx][jy] = el_player;
5955   InitPlayerField(jx, jy, el_player, TRUE);
5956
5957   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5958      possible that the relocation target field did not contain a player element,
5959      but a walkable element, to which the new player was relocated -- in this
5960      case, restore that (already initialized!) element on the player field */
5961   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5962   {
5963     Tile[jx][jy] = element;     // restore previously existing element
5964   }
5965
5966   // only visually relocate centered player
5967   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5968                      FALSE, level.instant_relocation);
5969
5970   TestIfPlayerTouchesBadThing(jx, jy);
5971   TestIfPlayerTouchesCustomElement(jx, jy);
5972
5973   if (IS_CUSTOM_ELEMENT(element))
5974     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5975                                player->index_bit, enter_side);
5976
5977   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5978                                       player->index_bit, enter_side);
5979
5980   if (player->is_switching)
5981   {
5982     /* ensure that relocation while still switching an element does not cause
5983        a new element to be treated as also switched directly after relocation
5984        (this is important for teleporter switches that teleport the player to
5985        a place where another teleporter switch is in the same direction, which
5986        would then incorrectly be treated as immediately switched before the
5987        direction key that caused the switch was released) */
5988
5989     player->switch_x += jx - old_jx;
5990     player->switch_y += jy - old_jy;
5991   }
5992 }
5993
5994 static void Explode(int ex, int ey, int phase, int mode)
5995 {
5996   int x, y;
5997   int last_phase;
5998   int border_element;
5999
6000   if (game.explosions_delayed)
6001   {
6002     ExplodeField[ex][ey] = mode;
6003     return;
6004   }
6005
6006   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
6007   {
6008     int center_element = Tile[ex][ey];
6009     int ce_value = CustomValue[ex][ey];
6010     int ce_score = element_info[center_element].collect_score;
6011     int artwork_element, explosion_element;     // set these values later
6012
6013     // remove things displayed in background while burning dynamite
6014     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
6015       Back[ex][ey] = 0;
6016
6017     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6018     {
6019       // put moving element to center field (and let it explode there)
6020       center_element = MovingOrBlocked2Element(ex, ey);
6021       RemoveMovingField(ex, ey);
6022       Tile[ex][ey] = center_element;
6023     }
6024
6025     // now "center_element" is finally determined -- set related values now
6026     artwork_element = center_element;           // for custom player artwork
6027     explosion_element = center_element;         // for custom player artwork
6028
6029     if (IS_PLAYER(ex, ey))
6030     {
6031       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6032
6033       artwork_element = stored_player[player_nr].artwork_element;
6034
6035       if (level.use_explosion_element[player_nr])
6036       {
6037         explosion_element = level.explosion_element[player_nr];
6038         artwork_element = explosion_element;
6039       }
6040     }
6041
6042     if (mode == EX_TYPE_NORMAL ||
6043         mode == EX_TYPE_CENTER ||
6044         mode == EX_TYPE_CROSS)
6045       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6046
6047     last_phase = element_info[explosion_element].explosion_delay + 1;
6048
6049     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6050     {
6051       int xx = x - ex + 1;
6052       int yy = y - ey + 1;
6053       int element;
6054
6055       if (!IN_LEV_FIELD(x, y) ||
6056           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6057           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6058         continue;
6059
6060       element = Tile[x][y];
6061
6062       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6063       {
6064         element = MovingOrBlocked2Element(x, y);
6065
6066         if (!IS_EXPLOSION_PROOF(element))
6067           RemoveMovingField(x, y);
6068       }
6069
6070       // indestructible elements can only explode in center (but not flames)
6071       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6072                                            mode == EX_TYPE_BORDER)) ||
6073           element == EL_FLAMES)
6074         continue;
6075
6076       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6077          behaviour, for example when touching a yamyam that explodes to rocks
6078          with active deadly shield, a rock is created under the player !!! */
6079       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6080 #if 0
6081       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6082           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6083            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6084 #else
6085       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6086 #endif
6087       {
6088         if (IS_ACTIVE_BOMB(element))
6089         {
6090           // re-activate things under the bomb like gate or penguin
6091           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6092           Back[x][y] = 0;
6093         }
6094
6095         continue;
6096       }
6097
6098       // save walkable background elements while explosion on same tile
6099       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6100           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6101         Back[x][y] = element;
6102
6103       // ignite explodable elements reached by other explosion
6104       if (element == EL_EXPLOSION)
6105         element = Store2[x][y];
6106
6107       if (AmoebaNr[x][y] &&
6108           (element == EL_AMOEBA_FULL ||
6109            element == EL_BD_AMOEBA ||
6110            element == EL_AMOEBA_GROWING))
6111       {
6112         AmoebaCnt[AmoebaNr[x][y]]--;
6113         AmoebaCnt2[AmoebaNr[x][y]]--;
6114       }
6115
6116       RemoveField(x, y);
6117
6118       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6119       {
6120         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6121
6122         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6123
6124         if (PLAYERINFO(ex, ey)->use_murphy)
6125           Store[x][y] = EL_EMPTY;
6126       }
6127
6128       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6129       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6130       else if (IS_PLAYER_ELEMENT(center_element))
6131         Store[x][y] = EL_EMPTY;
6132       else if (center_element == EL_YAMYAM)
6133         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6134       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6135         Store[x][y] = element_info[center_element].content.e[xx][yy];
6136 #if 1
6137       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6138       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6139       // otherwise) -- FIX THIS !!!
6140       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6141         Store[x][y] = element_info[element].content.e[1][1];
6142 #else
6143       else if (!CAN_EXPLODE(element))
6144         Store[x][y] = element_info[element].content.e[1][1];
6145 #endif
6146       else
6147         Store[x][y] = EL_EMPTY;
6148
6149       if (IS_CUSTOM_ELEMENT(center_element))
6150         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6151                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6152                        Store[x][y] >= EL_PREV_CE_8 &&
6153                        Store[x][y] <= EL_NEXT_CE_8 ?
6154                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6155                        Store[x][y]);
6156
6157       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6158           center_element == EL_AMOEBA_TO_DIAMOND)
6159         Store2[x][y] = element;
6160
6161       Tile[x][y] = EL_EXPLOSION;
6162       GfxElement[x][y] = artwork_element;
6163
6164       ExplodePhase[x][y] = 1;
6165       ExplodeDelay[x][y] = last_phase;
6166
6167       Stop[x][y] = TRUE;
6168     }
6169
6170     if (center_element == EL_YAMYAM)
6171       game.yamyam_content_nr =
6172         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6173
6174     return;
6175   }
6176
6177   if (Stop[ex][ey])
6178     return;
6179
6180   x = ex;
6181   y = ey;
6182
6183   if (phase == 1)
6184     GfxFrame[x][y] = 0;         // restart explosion animation
6185
6186   last_phase = ExplodeDelay[x][y];
6187
6188   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6189
6190   // this can happen if the player leaves an explosion just in time
6191   if (GfxElement[x][y] == EL_UNDEFINED)
6192     GfxElement[x][y] = EL_EMPTY;
6193
6194   border_element = Store2[x][y];
6195   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6196     border_element = StorePlayer[x][y];
6197
6198   if (phase == element_info[border_element].ignition_delay ||
6199       phase == last_phase)
6200   {
6201     boolean border_explosion = FALSE;
6202
6203     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6204         !PLAYER_EXPLOSION_PROTECTED(x, y))
6205     {
6206       KillPlayerUnlessExplosionProtected(x, y);
6207       border_explosion = TRUE;
6208     }
6209     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6210     {
6211       Tile[x][y] = Store2[x][y];
6212       Store2[x][y] = 0;
6213       Bang(x, y);
6214       border_explosion = TRUE;
6215     }
6216     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6217     {
6218       AmoebaToDiamond(x, y);
6219       Store2[x][y] = 0;
6220       border_explosion = TRUE;
6221     }
6222
6223     // if an element just explodes due to another explosion (chain-reaction),
6224     // do not immediately end the new explosion when it was the last frame of
6225     // the explosion (as it would be done in the following "if"-statement!)
6226     if (border_explosion && phase == last_phase)
6227       return;
6228   }
6229
6230   // this can happen if the player was just killed by an explosion
6231   if (GfxElement[x][y] == EL_UNDEFINED)
6232     GfxElement[x][y] = EL_EMPTY;
6233
6234   if (phase == last_phase)
6235   {
6236     int element;
6237
6238     element = Tile[x][y] = Store[x][y];
6239     Store[x][y] = Store2[x][y] = 0;
6240     GfxElement[x][y] = EL_UNDEFINED;
6241
6242     // player can escape from explosions and might therefore be still alive
6243     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6244         element <= EL_PLAYER_IS_EXPLODING_4)
6245     {
6246       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6247       int explosion_element = EL_PLAYER_1 + player_nr;
6248       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6249       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6250
6251       if (level.use_explosion_element[player_nr])
6252         explosion_element = level.explosion_element[player_nr];
6253
6254       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6255                     element_info[explosion_element].content.e[xx][yy]);
6256     }
6257
6258     // restore probably existing indestructible background element
6259     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6260       element = Tile[x][y] = Back[x][y];
6261     Back[x][y] = 0;
6262
6263     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6264     GfxDir[x][y] = MV_NONE;
6265     ChangeDelay[x][y] = 0;
6266     ChangePage[x][y] = -1;
6267
6268     CustomValue[x][y] = 0;
6269
6270     InitField_WithBug2(x, y, FALSE);
6271
6272     TEST_DrawLevelField(x, y);
6273
6274     TestIfElementTouchesCustomElement(x, y);
6275
6276     if (GFX_CRUMBLED(element))
6277       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6278
6279     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6280       StorePlayer[x][y] = 0;
6281
6282     if (IS_PLAYER_ELEMENT(element))
6283       RelocatePlayer(x, y, element);
6284   }
6285   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6286   {
6287     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6288     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6289
6290     if (phase == 1)
6291       TEST_DrawLevelFieldCrumbled(x, y);
6292
6293     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6294     {
6295       DrawLevelElement(x, y, Back[x][y]);
6296       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6297     }
6298     else if (IS_WALKABLE_UNDER(Back[x][y]))
6299     {
6300       DrawLevelGraphic(x, y, graphic, frame);
6301       DrawLevelElementThruMask(x, y, Back[x][y]);
6302     }
6303     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6304       DrawLevelGraphic(x, y, graphic, frame);
6305   }
6306 }
6307
6308 static void DynaExplode(int ex, int ey)
6309 {
6310   int i, j;
6311   int dynabomb_element = Tile[ex][ey];
6312   int dynabomb_size = 1;
6313   boolean dynabomb_xl = FALSE;
6314   struct PlayerInfo *player;
6315   struct XY *xy = xy_topdown;
6316
6317   if (IS_ACTIVE_BOMB(dynabomb_element))
6318   {
6319     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6320     dynabomb_size = player->dynabomb_size;
6321     dynabomb_xl = player->dynabomb_xl;
6322     player->dynabombs_left++;
6323   }
6324
6325   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6326
6327   for (i = 0; i < NUM_DIRECTIONS; i++)
6328   {
6329     for (j = 1; j <= dynabomb_size; j++)
6330     {
6331       int x = ex + j * xy[i].x;
6332       int y = ey + j * xy[i].y;
6333       int element;
6334
6335       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6336         break;
6337
6338       element = Tile[x][y];
6339
6340       // do not restart explosions of fields with active bombs
6341       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6342         continue;
6343
6344       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6345
6346       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6347           !IS_DIGGABLE(element) && !dynabomb_xl)
6348         break;
6349     }
6350   }
6351 }
6352
6353 void Bang(int x, int y)
6354 {
6355   int element = MovingOrBlocked2Element(x, y);
6356   int explosion_type = EX_TYPE_NORMAL;
6357
6358   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6359   {
6360     struct PlayerInfo *player = PLAYERINFO(x, y);
6361
6362     element = Tile[x][y] = player->initial_element;
6363
6364     if (level.use_explosion_element[player->index_nr])
6365     {
6366       int explosion_element = level.explosion_element[player->index_nr];
6367
6368       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6369         explosion_type = EX_TYPE_CROSS;
6370       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6371         explosion_type = EX_TYPE_CENTER;
6372     }
6373   }
6374
6375   switch (element)
6376   {
6377     case EL_BUG:
6378     case EL_SPACESHIP:
6379     case EL_BD_BUTTERFLY:
6380     case EL_BD_FIREFLY:
6381     case EL_YAMYAM:
6382     case EL_DARK_YAMYAM:
6383     case EL_ROBOT:
6384     case EL_PACMAN:
6385     case EL_MOLE:
6386       RaiseScoreElement(element);
6387       break;
6388
6389     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6390     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6391     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6392     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6393     case EL_DYNABOMB_INCREASE_NUMBER:
6394     case EL_DYNABOMB_INCREASE_SIZE:
6395     case EL_DYNABOMB_INCREASE_POWER:
6396       explosion_type = EX_TYPE_DYNA;
6397       break;
6398
6399     case EL_DC_LANDMINE:
6400       explosion_type = EX_TYPE_CENTER;
6401       break;
6402
6403     case EL_PENGUIN:
6404     case EL_LAMP:
6405     case EL_LAMP_ACTIVE:
6406     case EL_AMOEBA_TO_DIAMOND:
6407       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6408         explosion_type = EX_TYPE_CENTER;
6409       break;
6410
6411     default:
6412       if (element_info[element].explosion_type == EXPLODES_CROSS)
6413         explosion_type = EX_TYPE_CROSS;
6414       else if (element_info[element].explosion_type == EXPLODES_1X1)
6415         explosion_type = EX_TYPE_CENTER;
6416       break;
6417   }
6418
6419   if (explosion_type == EX_TYPE_DYNA)
6420     DynaExplode(x, y);
6421   else
6422     Explode(x, y, EX_PHASE_START, explosion_type);
6423
6424   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6425 }
6426
6427 static void SplashAcid(int x, int y)
6428 {
6429   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6430       (!IN_LEV_FIELD(x - 1, y - 2) ||
6431        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6432     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6433
6434   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6435       (!IN_LEV_FIELD(x + 1, y - 2) ||
6436        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6437     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6438
6439   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6440 }
6441
6442 static void InitBeltMovement(void)
6443 {
6444   static int belt_base_element[4] =
6445   {
6446     EL_CONVEYOR_BELT_1_LEFT,
6447     EL_CONVEYOR_BELT_2_LEFT,
6448     EL_CONVEYOR_BELT_3_LEFT,
6449     EL_CONVEYOR_BELT_4_LEFT
6450   };
6451   static int belt_base_active_element[4] =
6452   {
6453     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6454     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6455     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6456     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6457   };
6458
6459   int x, y, i, j;
6460
6461   // set frame order for belt animation graphic according to belt direction
6462   for (i = 0; i < NUM_BELTS; i++)
6463   {
6464     int belt_nr = i;
6465
6466     for (j = 0; j < NUM_BELT_PARTS; j++)
6467     {
6468       int element = belt_base_active_element[belt_nr] + j;
6469       int graphic_1 = el2img(element);
6470       int graphic_2 = el2panelimg(element);
6471
6472       if (game.belt_dir[i] == MV_LEFT)
6473       {
6474         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6475         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6476       }
6477       else
6478       {
6479         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6480         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6481       }
6482     }
6483   }
6484
6485   SCAN_PLAYFIELD(x, y)
6486   {
6487     int element = Tile[x][y];
6488
6489     for (i = 0; i < NUM_BELTS; i++)
6490     {
6491       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6492       {
6493         int e_belt_nr = getBeltNrFromBeltElement(element);
6494         int belt_nr = i;
6495
6496         if (e_belt_nr == belt_nr)
6497         {
6498           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6499
6500           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6501         }
6502       }
6503     }
6504   }
6505 }
6506
6507 static void ToggleBeltSwitch(int x, int y)
6508 {
6509   static int belt_base_element[4] =
6510   {
6511     EL_CONVEYOR_BELT_1_LEFT,
6512     EL_CONVEYOR_BELT_2_LEFT,
6513     EL_CONVEYOR_BELT_3_LEFT,
6514     EL_CONVEYOR_BELT_4_LEFT
6515   };
6516   static int belt_base_active_element[4] =
6517   {
6518     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6519     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6520     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6521     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6522   };
6523   static int belt_base_switch_element[4] =
6524   {
6525     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6526     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6527     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6528     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6529   };
6530   static int belt_move_dir[4] =
6531   {
6532     MV_LEFT,
6533     MV_NONE,
6534     MV_RIGHT,
6535     MV_NONE,
6536   };
6537
6538   int element = Tile[x][y];
6539   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6540   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6541   int belt_dir = belt_move_dir[belt_dir_nr];
6542   int xx, yy, i;
6543
6544   if (!IS_BELT_SWITCH(element))
6545     return;
6546
6547   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6548   game.belt_dir[belt_nr] = belt_dir;
6549
6550   if (belt_dir_nr == 3)
6551     belt_dir_nr = 1;
6552
6553   // set frame order for belt animation graphic according to belt direction
6554   for (i = 0; i < NUM_BELT_PARTS; i++)
6555   {
6556     int element = belt_base_active_element[belt_nr] + i;
6557     int graphic_1 = el2img(element);
6558     int graphic_2 = el2panelimg(element);
6559
6560     if (belt_dir == MV_LEFT)
6561     {
6562       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6563       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6564     }
6565     else
6566     {
6567       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6568       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6569     }
6570   }
6571
6572   SCAN_PLAYFIELD(xx, yy)
6573   {
6574     int element = Tile[xx][yy];
6575
6576     if (IS_BELT_SWITCH(element))
6577     {
6578       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6579
6580       if (e_belt_nr == belt_nr)
6581       {
6582         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6583         TEST_DrawLevelField(xx, yy);
6584       }
6585     }
6586     else if (IS_BELT(element) && belt_dir != MV_NONE)
6587     {
6588       int e_belt_nr = getBeltNrFromBeltElement(element);
6589
6590       if (e_belt_nr == belt_nr)
6591       {
6592         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6593
6594         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6595         TEST_DrawLevelField(xx, yy);
6596       }
6597     }
6598     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6599     {
6600       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6601
6602       if (e_belt_nr == belt_nr)
6603       {
6604         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6605
6606         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6607         TEST_DrawLevelField(xx, yy);
6608       }
6609     }
6610   }
6611 }
6612
6613 static void ToggleSwitchgateSwitch(void)
6614 {
6615   int xx, yy;
6616
6617   game.switchgate_pos = !game.switchgate_pos;
6618
6619   SCAN_PLAYFIELD(xx, yy)
6620   {
6621     int element = Tile[xx][yy];
6622
6623     if (element == EL_SWITCHGATE_SWITCH_UP)
6624     {
6625       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6626       TEST_DrawLevelField(xx, yy);
6627     }
6628     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6629     {
6630       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6634     {
6635       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6636       TEST_DrawLevelField(xx, yy);
6637     }
6638     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6639     {
6640       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6641       TEST_DrawLevelField(xx, yy);
6642     }
6643     else if (element == EL_SWITCHGATE_OPEN ||
6644              element == EL_SWITCHGATE_OPENING)
6645     {
6646       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6647
6648       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6649     }
6650     else if (element == EL_SWITCHGATE_CLOSED ||
6651              element == EL_SWITCHGATE_CLOSING)
6652     {
6653       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6654
6655       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6656     }
6657   }
6658 }
6659
6660 static int getInvisibleActiveFromInvisibleElement(int element)
6661 {
6662   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6663           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6664           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6665           element);
6666 }
6667
6668 static int getInvisibleFromInvisibleActiveElement(int element)
6669 {
6670   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6671           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6672           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6673           element);
6674 }
6675
6676 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6677 {
6678   int x, y;
6679
6680   SCAN_PLAYFIELD(x, y)
6681   {
6682     int element = Tile[x][y];
6683
6684     if (element == EL_LIGHT_SWITCH &&
6685         game.light_time_left > 0)
6686     {
6687       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6688       TEST_DrawLevelField(x, y);
6689     }
6690     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6691              game.light_time_left == 0)
6692     {
6693       Tile[x][y] = EL_LIGHT_SWITCH;
6694       TEST_DrawLevelField(x, y);
6695     }
6696     else if (element == EL_EMC_DRIPPER &&
6697              game.light_time_left > 0)
6698     {
6699       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6700       TEST_DrawLevelField(x, y);
6701     }
6702     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6703              game.light_time_left == 0)
6704     {
6705       Tile[x][y] = EL_EMC_DRIPPER;
6706       TEST_DrawLevelField(x, y);
6707     }
6708     else if (element == EL_INVISIBLE_STEELWALL ||
6709              element == EL_INVISIBLE_WALL ||
6710              element == EL_INVISIBLE_SAND)
6711     {
6712       if (game.light_time_left > 0)
6713         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6714
6715       TEST_DrawLevelField(x, y);
6716
6717       // uncrumble neighbour fields, if needed
6718       if (element == EL_INVISIBLE_SAND)
6719         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6720     }
6721     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6722              element == EL_INVISIBLE_WALL_ACTIVE ||
6723              element == EL_INVISIBLE_SAND_ACTIVE)
6724     {
6725       if (game.light_time_left == 0)
6726         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6727
6728       TEST_DrawLevelField(x, y);
6729
6730       // re-crumble neighbour fields, if needed
6731       if (element == EL_INVISIBLE_SAND)
6732         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6733     }
6734   }
6735 }
6736
6737 static void RedrawAllInvisibleElementsForLenses(void)
6738 {
6739   int x, y;
6740
6741   SCAN_PLAYFIELD(x, y)
6742   {
6743     int element = Tile[x][y];
6744
6745     if (element == EL_EMC_DRIPPER &&
6746         game.lenses_time_left > 0)
6747     {
6748       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6749       TEST_DrawLevelField(x, y);
6750     }
6751     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6752              game.lenses_time_left == 0)
6753     {
6754       Tile[x][y] = EL_EMC_DRIPPER;
6755       TEST_DrawLevelField(x, y);
6756     }
6757     else if (element == EL_INVISIBLE_STEELWALL ||
6758              element == EL_INVISIBLE_WALL ||
6759              element == EL_INVISIBLE_SAND)
6760     {
6761       if (game.lenses_time_left > 0)
6762         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6763
6764       TEST_DrawLevelField(x, y);
6765
6766       // uncrumble neighbour fields, if needed
6767       if (element == EL_INVISIBLE_SAND)
6768         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6769     }
6770     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6771              element == EL_INVISIBLE_WALL_ACTIVE ||
6772              element == EL_INVISIBLE_SAND_ACTIVE)
6773     {
6774       if (game.lenses_time_left == 0)
6775         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6776
6777       TEST_DrawLevelField(x, y);
6778
6779       // re-crumble neighbour fields, if needed
6780       if (element == EL_INVISIBLE_SAND)
6781         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6782     }
6783   }
6784 }
6785
6786 static void RedrawAllInvisibleElementsForMagnifier(void)
6787 {
6788   int x, y;
6789
6790   SCAN_PLAYFIELD(x, y)
6791   {
6792     int element = Tile[x][y];
6793
6794     if (element == EL_EMC_FAKE_GRASS &&
6795         game.magnify_time_left > 0)
6796     {
6797       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6798       TEST_DrawLevelField(x, y);
6799     }
6800     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6801              game.magnify_time_left == 0)
6802     {
6803       Tile[x][y] = EL_EMC_FAKE_GRASS;
6804       TEST_DrawLevelField(x, y);
6805     }
6806     else if (IS_GATE_GRAY(element) &&
6807              game.magnify_time_left > 0)
6808     {
6809       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6810                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6811                     IS_EM_GATE_GRAY(element) ?
6812                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6813                     IS_EMC_GATE_GRAY(element) ?
6814                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6815                     IS_DC_GATE_GRAY(element) ?
6816                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6817                     element);
6818       TEST_DrawLevelField(x, y);
6819     }
6820     else if (IS_GATE_GRAY_ACTIVE(element) &&
6821              game.magnify_time_left == 0)
6822     {
6823       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6824                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6825                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6826                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6827                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6828                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6829                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6830                     EL_DC_GATE_WHITE_GRAY :
6831                     element);
6832       TEST_DrawLevelField(x, y);
6833     }
6834   }
6835 }
6836
6837 static void ToggleLightSwitch(int x, int y)
6838 {
6839   int element = Tile[x][y];
6840
6841   game.light_time_left =
6842     (element == EL_LIGHT_SWITCH ?
6843      level.time_light * FRAMES_PER_SECOND : 0);
6844
6845   RedrawAllLightSwitchesAndInvisibleElements();
6846 }
6847
6848 static void ActivateTimegateSwitch(int x, int y)
6849 {
6850   int xx, yy;
6851
6852   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6853
6854   SCAN_PLAYFIELD(xx, yy)
6855   {
6856     int element = Tile[xx][yy];
6857
6858     if (element == EL_TIMEGATE_CLOSED ||
6859         element == EL_TIMEGATE_CLOSING)
6860     {
6861       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6862       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6863     }
6864
6865     /*
6866     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6867     {
6868       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6869       TEST_DrawLevelField(xx, yy);
6870     }
6871     */
6872
6873   }
6874
6875   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6876                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6877 }
6878
6879 static void Impact(int x, int y)
6880 {
6881   boolean last_line = (y == lev_fieldy - 1);
6882   boolean object_hit = FALSE;
6883   boolean impact = (last_line || object_hit);
6884   int element = Tile[x][y];
6885   int smashed = EL_STEELWALL;
6886
6887   if (!last_line)       // check if element below was hit
6888   {
6889     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6890       return;
6891
6892     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6893                                          MovDir[x][y + 1] != MV_DOWN ||
6894                                          MovPos[x][y + 1] <= TILEY / 2));
6895
6896     // do not smash moving elements that left the smashed field in time
6897     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6898         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6899       object_hit = FALSE;
6900
6901 #if USE_QUICKSAND_IMPACT_BUGFIX
6902     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6903     {
6904       RemoveMovingField(x, y + 1);
6905       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6906       Tile[x][y + 2] = EL_ROCK;
6907       TEST_DrawLevelField(x, y + 2);
6908
6909       object_hit = TRUE;
6910     }
6911
6912     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6913     {
6914       RemoveMovingField(x, y + 1);
6915       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6916       Tile[x][y + 2] = EL_ROCK;
6917       TEST_DrawLevelField(x, y + 2);
6918
6919       object_hit = TRUE;
6920     }
6921 #endif
6922
6923     if (object_hit)
6924       smashed = MovingOrBlocked2Element(x, y + 1);
6925
6926     impact = (last_line || object_hit);
6927   }
6928
6929   if (!last_line && smashed == EL_ACID) // element falls into acid
6930   {
6931     SplashAcid(x, y + 1);
6932     return;
6933   }
6934
6935   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6936   // only reset graphic animation if graphic really changes after impact
6937   if (impact &&
6938       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6939   {
6940     ResetGfxAnimation(x, y);
6941     TEST_DrawLevelField(x, y);
6942   }
6943
6944   if (impact && CAN_EXPLODE_IMPACT(element))
6945   {
6946     Bang(x, y);
6947     return;
6948   }
6949   else if (impact && element == EL_PEARL &&
6950            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6951   {
6952     ResetGfxAnimation(x, y);
6953
6954     Tile[x][y] = EL_PEARL_BREAKING;
6955     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6956     return;
6957   }
6958   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6959   {
6960     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6961
6962     return;
6963   }
6964
6965   if (impact && element == EL_AMOEBA_DROP)
6966   {
6967     if (object_hit && IS_PLAYER(x, y + 1))
6968       KillPlayerUnlessEnemyProtected(x, y + 1);
6969     else if (object_hit && smashed == EL_PENGUIN)
6970       Bang(x, y + 1);
6971     else
6972     {
6973       Tile[x][y] = EL_AMOEBA_GROWING;
6974       Store[x][y] = EL_AMOEBA_WET;
6975
6976       ResetRandomAnimationValue(x, y);
6977     }
6978     return;
6979   }
6980
6981   if (object_hit)               // check which object was hit
6982   {
6983     if ((CAN_PASS_MAGIC_WALL(element) && 
6984          (smashed == EL_MAGIC_WALL ||
6985           smashed == EL_BD_MAGIC_WALL)) ||
6986         (CAN_PASS_DC_MAGIC_WALL(element) &&
6987          smashed == EL_DC_MAGIC_WALL))
6988     {
6989       int xx, yy;
6990       int activated_magic_wall =
6991         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6992          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6993          EL_DC_MAGIC_WALL_ACTIVE);
6994
6995       // activate magic wall / mill
6996       SCAN_PLAYFIELD(xx, yy)
6997       {
6998         if (Tile[xx][yy] == smashed)
6999           Tile[xx][yy] = activated_magic_wall;
7000       }
7001
7002       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7003       game.magic_wall_active = TRUE;
7004
7005       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7006                             SND_MAGIC_WALL_ACTIVATING :
7007                             smashed == EL_BD_MAGIC_WALL ?
7008                             SND_BD_MAGIC_WALL_ACTIVATING :
7009                             SND_DC_MAGIC_WALL_ACTIVATING));
7010     }
7011
7012     if (IS_PLAYER(x, y + 1))
7013     {
7014       if (CAN_SMASH_PLAYER(element))
7015       {
7016         KillPlayerUnlessEnemyProtected(x, y + 1);
7017         return;
7018       }
7019     }
7020     else if (smashed == EL_PENGUIN)
7021     {
7022       if (CAN_SMASH_PLAYER(element))
7023       {
7024         Bang(x, y + 1);
7025         return;
7026       }
7027     }
7028     else if (element == EL_BD_DIAMOND)
7029     {
7030       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7031       {
7032         Bang(x, y + 1);
7033         return;
7034       }
7035     }
7036     else if (((element == EL_SP_INFOTRON ||
7037                element == EL_SP_ZONK) &&
7038               (smashed == EL_SP_SNIKSNAK ||
7039                smashed == EL_SP_ELECTRON ||
7040                smashed == EL_SP_DISK_ORANGE)) ||
7041              (element == EL_SP_INFOTRON &&
7042               smashed == EL_SP_DISK_YELLOW))
7043     {
7044       Bang(x, y + 1);
7045       return;
7046     }
7047     else if (CAN_SMASH_EVERYTHING(element))
7048     {
7049       if (IS_CLASSIC_ENEMY(smashed) ||
7050           CAN_EXPLODE_SMASHED(smashed))
7051       {
7052         Bang(x, y + 1);
7053         return;
7054       }
7055       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7056       {
7057         if (smashed == EL_LAMP ||
7058             smashed == EL_LAMP_ACTIVE)
7059         {
7060           Bang(x, y + 1);
7061           return;
7062         }
7063         else if (smashed == EL_NUT)
7064         {
7065           Tile[x][y + 1] = EL_NUT_BREAKING;
7066           PlayLevelSound(x, y, SND_NUT_BREAKING);
7067           RaiseScoreElement(EL_NUT);
7068           return;
7069         }
7070         else if (smashed == EL_PEARL)
7071         {
7072           ResetGfxAnimation(x, y);
7073
7074           Tile[x][y + 1] = EL_PEARL_BREAKING;
7075           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7076           return;
7077         }
7078         else if (smashed == EL_DIAMOND)
7079         {
7080           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7081           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7082           return;
7083         }
7084         else if (IS_BELT_SWITCH(smashed))
7085         {
7086           ToggleBeltSwitch(x, y + 1);
7087         }
7088         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7089                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7090                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7091                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7092         {
7093           ToggleSwitchgateSwitch();
7094         }
7095         else if (smashed == EL_LIGHT_SWITCH ||
7096                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7097         {
7098           ToggleLightSwitch(x, y + 1);
7099         }
7100         else
7101         {
7102           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7103
7104           CheckElementChangeBySide(x, y + 1, smashed, element,
7105                                    CE_SWITCHED, CH_SIDE_TOP);
7106           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7107                                             CH_SIDE_TOP);
7108         }
7109       }
7110       else
7111       {
7112         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7113       }
7114     }
7115   }
7116
7117   // play sound of magic wall / mill
7118   if (!last_line &&
7119       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7120        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7121        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7122   {
7123     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7124       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7125     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7126       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7127     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7128       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7129
7130     return;
7131   }
7132
7133   // play sound of object that hits the ground
7134   if (last_line || object_hit)
7135     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7136 }
7137
7138 static void TurnRoundExt(int x, int y)
7139 {
7140   static struct
7141   {
7142     int dx, dy;
7143   } move_xy[] =
7144   {
7145     {  0,  0 },
7146     { -1,  0 },
7147     { +1,  0 },
7148     {  0,  0 },
7149     {  0, -1 },
7150     {  0,  0 }, { 0, 0 }, { 0, 0 },
7151     {  0, +1 }
7152   };
7153   static struct
7154   {
7155     int left, right, back;
7156   } turn[] =
7157   {
7158     { 0,        0,              0        },
7159     { MV_DOWN,  MV_UP,          MV_RIGHT },
7160     { MV_UP,    MV_DOWN,        MV_LEFT  },
7161     { 0,        0,              0        },
7162     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7163     { 0,        0,              0        },
7164     { 0,        0,              0        },
7165     { 0,        0,              0        },
7166     { MV_RIGHT, MV_LEFT,        MV_UP    }
7167   };
7168
7169   int element = Tile[x][y];
7170   int move_pattern = element_info[element].move_pattern;
7171
7172   int old_move_dir = MovDir[x][y];
7173   int left_dir  = turn[old_move_dir].left;
7174   int right_dir = turn[old_move_dir].right;
7175   int back_dir  = turn[old_move_dir].back;
7176
7177   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7178   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7179   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7180   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7181
7182   int left_x  = x + left_dx,  left_y  = y + left_dy;
7183   int right_x = x + right_dx, right_y = y + right_dy;
7184   int move_x  = x + move_dx,  move_y  = y + move_dy;
7185
7186   int xx, yy;
7187
7188   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7189   {
7190     TestIfBadThingTouchesOtherBadThing(x, y);
7191
7192     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7193       MovDir[x][y] = right_dir;
7194     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7195       MovDir[x][y] = left_dir;
7196
7197     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7198       MovDelay[x][y] = 9;
7199     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7200       MovDelay[x][y] = 1;
7201   }
7202   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7203   {
7204     TestIfBadThingTouchesOtherBadThing(x, y);
7205
7206     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7207       MovDir[x][y] = left_dir;
7208     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209       MovDir[x][y] = right_dir;
7210
7211     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7212       MovDelay[x][y] = 9;
7213     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7214       MovDelay[x][y] = 1;
7215   }
7216   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7217   {
7218     TestIfBadThingTouchesOtherBadThing(x, y);
7219
7220     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7221       MovDir[x][y] = left_dir;
7222     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7223       MovDir[x][y] = right_dir;
7224
7225     if (MovDir[x][y] != old_move_dir)
7226       MovDelay[x][y] = 9;
7227   }
7228   else if (element == EL_YAMYAM)
7229   {
7230     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7231     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7232
7233     if (can_turn_left && can_turn_right)
7234       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7235     else if (can_turn_left)
7236       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7237     else if (can_turn_right)
7238       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7239     else
7240       MovDir[x][y] = back_dir;
7241
7242     MovDelay[x][y] = 16 + 16 * RND(3);
7243   }
7244   else if (element == EL_DARK_YAMYAM)
7245   {
7246     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7247                                                          left_x, left_y);
7248     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7249                                                          right_x, right_y);
7250
7251     if (can_turn_left && can_turn_right)
7252       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7253     else if (can_turn_left)
7254       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7255     else if (can_turn_right)
7256       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7257     else
7258       MovDir[x][y] = back_dir;
7259
7260     MovDelay[x][y] = 16 + 16 * RND(3);
7261   }
7262   else if (element == EL_PACMAN)
7263   {
7264     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7265     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7266
7267     if (can_turn_left && can_turn_right)
7268       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7269     else if (can_turn_left)
7270       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7271     else if (can_turn_right)
7272       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7273     else
7274       MovDir[x][y] = back_dir;
7275
7276     MovDelay[x][y] = 6 + RND(40);
7277   }
7278   else if (element == EL_PIG)
7279   {
7280     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7281     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7282     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7283     boolean should_turn_left, should_turn_right, should_move_on;
7284     int rnd_value = 24;
7285     int rnd = RND(rnd_value);
7286
7287     should_turn_left = (can_turn_left &&
7288                         (!can_move_on ||
7289                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7290                                                    y + back_dy + left_dy)));
7291     should_turn_right = (can_turn_right &&
7292                          (!can_move_on ||
7293                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7294                                                     y + back_dy + right_dy)));
7295     should_move_on = (can_move_on &&
7296                       (!can_turn_left ||
7297                        !can_turn_right ||
7298                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7299                                                  y + move_dy + left_dy) ||
7300                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7301                                                  y + move_dy + right_dy)));
7302
7303     if (should_turn_left || should_turn_right || should_move_on)
7304     {
7305       if (should_turn_left && should_turn_right && should_move_on)
7306         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7307                         rnd < 2 * rnd_value / 3 ? right_dir :
7308                         old_move_dir);
7309       else if (should_turn_left && should_turn_right)
7310         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7311       else if (should_turn_left && should_move_on)
7312         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7313       else if (should_turn_right && should_move_on)
7314         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7315       else if (should_turn_left)
7316         MovDir[x][y] = left_dir;
7317       else if (should_turn_right)
7318         MovDir[x][y] = right_dir;
7319       else if (should_move_on)
7320         MovDir[x][y] = old_move_dir;
7321     }
7322     else if (can_move_on && rnd > rnd_value / 8)
7323       MovDir[x][y] = old_move_dir;
7324     else if (can_turn_left && can_turn_right)
7325       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7326     else if (can_turn_left && rnd > rnd_value / 8)
7327       MovDir[x][y] = left_dir;
7328     else if (can_turn_right && rnd > rnd_value/8)
7329       MovDir[x][y] = right_dir;
7330     else
7331       MovDir[x][y] = back_dir;
7332
7333     xx = x + move_xy[MovDir[x][y]].dx;
7334     yy = y + move_xy[MovDir[x][y]].dy;
7335
7336     if (!IN_LEV_FIELD(xx, yy) ||
7337         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7338       MovDir[x][y] = old_move_dir;
7339
7340     MovDelay[x][y] = 0;
7341   }
7342   else if (element == EL_DRAGON)
7343   {
7344     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7345     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7346     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7347     int rnd_value = 24;
7348     int rnd = RND(rnd_value);
7349
7350     if (can_move_on && rnd > rnd_value / 8)
7351       MovDir[x][y] = old_move_dir;
7352     else if (can_turn_left && can_turn_right)
7353       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7354     else if (can_turn_left && rnd > rnd_value / 8)
7355       MovDir[x][y] = left_dir;
7356     else if (can_turn_right && rnd > rnd_value / 8)
7357       MovDir[x][y] = right_dir;
7358     else
7359       MovDir[x][y] = back_dir;
7360
7361     xx = x + move_xy[MovDir[x][y]].dx;
7362     yy = y + move_xy[MovDir[x][y]].dy;
7363
7364     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7365       MovDir[x][y] = old_move_dir;
7366
7367     MovDelay[x][y] = 0;
7368   }
7369   else if (element == EL_MOLE)
7370   {
7371     boolean can_move_on =
7372       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7373                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7374                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7375     if (!can_move_on)
7376     {
7377       boolean can_turn_left =
7378         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7379                               IS_AMOEBOID(Tile[left_x][left_y])));
7380
7381       boolean can_turn_right =
7382         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7383                               IS_AMOEBOID(Tile[right_x][right_y])));
7384
7385       if (can_turn_left && can_turn_right)
7386         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7387       else if (can_turn_left)
7388         MovDir[x][y] = left_dir;
7389       else
7390         MovDir[x][y] = right_dir;
7391     }
7392
7393     if (MovDir[x][y] != old_move_dir)
7394       MovDelay[x][y] = 9;
7395   }
7396   else if (element == EL_BALLOON)
7397   {
7398     MovDir[x][y] = game.wind_direction;
7399     MovDelay[x][y] = 0;
7400   }
7401   else if (element == EL_SPRING)
7402   {
7403     if (MovDir[x][y] & MV_HORIZONTAL)
7404     {
7405       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7406           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7407       {
7408         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7409         ResetGfxAnimation(move_x, move_y);
7410         TEST_DrawLevelField(move_x, move_y);
7411
7412         MovDir[x][y] = back_dir;
7413       }
7414       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7415                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7416         MovDir[x][y] = MV_NONE;
7417     }
7418
7419     MovDelay[x][y] = 0;
7420   }
7421   else if (element == EL_ROBOT ||
7422            element == EL_SATELLITE ||
7423            element == EL_PENGUIN ||
7424            element == EL_EMC_ANDROID)
7425   {
7426     int attr_x = -1, attr_y = -1;
7427
7428     if (game.all_players_gone)
7429     {
7430       attr_x = game.exit_x;
7431       attr_y = game.exit_y;
7432     }
7433     else
7434     {
7435       int i;
7436
7437       for (i = 0; i < MAX_PLAYERS; i++)
7438       {
7439         struct PlayerInfo *player = &stored_player[i];
7440         int jx = player->jx, jy = player->jy;
7441
7442         if (!player->active)
7443           continue;
7444
7445         if (attr_x == -1 ||
7446             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7447         {
7448           attr_x = jx;
7449           attr_y = jy;
7450         }
7451       }
7452     }
7453
7454     if (element == EL_ROBOT &&
7455         game.robot_wheel_x >= 0 &&
7456         game.robot_wheel_y >= 0 &&
7457         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7458          game.engine_version < VERSION_IDENT(3,1,0,0)))
7459     {
7460       attr_x = game.robot_wheel_x;
7461       attr_y = game.robot_wheel_y;
7462     }
7463
7464     if (element == EL_PENGUIN)
7465     {
7466       int i;
7467       struct XY *xy = xy_topdown;
7468
7469       for (i = 0; i < NUM_DIRECTIONS; i++)
7470       {
7471         int ex = x + xy[i].x;
7472         int ey = y + xy[i].y;
7473
7474         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7475                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7476                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7477                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7478         {
7479           attr_x = ex;
7480           attr_y = ey;
7481           break;
7482         }
7483       }
7484     }
7485
7486     MovDir[x][y] = MV_NONE;
7487     if (attr_x < x)
7488       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7489     else if (attr_x > x)
7490       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7491     if (attr_y < y)
7492       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7493     else if (attr_y > y)
7494       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7495
7496     if (element == EL_ROBOT)
7497     {
7498       int newx, newy;
7499
7500       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7501         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7502       Moving2Blocked(x, y, &newx, &newy);
7503
7504       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7505         MovDelay[x][y] = 8 + 8 * !RND(3);
7506       else
7507         MovDelay[x][y] = 16;
7508     }
7509     else if (element == EL_PENGUIN)
7510     {
7511       int newx, newy;
7512
7513       MovDelay[x][y] = 1;
7514
7515       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7516       {
7517         boolean first_horiz = RND(2);
7518         int new_move_dir = MovDir[x][y];
7519
7520         MovDir[x][y] =
7521           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522         Moving2Blocked(x, y, &newx, &newy);
7523
7524         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7525           return;
7526
7527         MovDir[x][y] =
7528           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529         Moving2Blocked(x, y, &newx, &newy);
7530
7531         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7532           return;
7533
7534         MovDir[x][y] = old_move_dir;
7535         return;
7536       }
7537     }
7538     else if (element == EL_SATELLITE)
7539     {
7540       int newx, newy;
7541
7542       MovDelay[x][y] = 1;
7543
7544       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7545       {
7546         boolean first_horiz = RND(2);
7547         int new_move_dir = MovDir[x][y];
7548
7549         MovDir[x][y] =
7550           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7551         Moving2Blocked(x, y, &newx, &newy);
7552
7553         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7554           return;
7555
7556         MovDir[x][y] =
7557           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7558         Moving2Blocked(x, y, &newx, &newy);
7559
7560         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7561           return;
7562
7563         MovDir[x][y] = old_move_dir;
7564         return;
7565       }
7566     }
7567     else if (element == EL_EMC_ANDROID)
7568     {
7569       static int check_pos[16] =
7570       {
7571         -1,             //  0 => (invalid)
7572         7,              //  1 => MV_LEFT
7573         3,              //  2 => MV_RIGHT
7574         -1,             //  3 => (invalid)
7575         1,              //  4 =>            MV_UP
7576         0,              //  5 => MV_LEFT  | MV_UP
7577         2,              //  6 => MV_RIGHT | MV_UP
7578         -1,             //  7 => (invalid)
7579         5,              //  8 =>            MV_DOWN
7580         6,              //  9 => MV_LEFT  | MV_DOWN
7581         4,              // 10 => MV_RIGHT | MV_DOWN
7582         -1,             // 11 => (invalid)
7583         -1,             // 12 => (invalid)
7584         -1,             // 13 => (invalid)
7585         -1,             // 14 => (invalid)
7586         -1,             // 15 => (invalid)
7587       };
7588       static struct
7589       {
7590         int dx, dy;
7591         int dir;
7592       } check_xy[8] =
7593       {
7594         { -1, -1,       MV_LEFT  | MV_UP   },
7595         {  0, -1,                  MV_UP   },
7596         { +1, -1,       MV_RIGHT | MV_UP   },
7597         { +1,  0,       MV_RIGHT           },
7598         { +1, +1,       MV_RIGHT | MV_DOWN },
7599         {  0, +1,                  MV_DOWN },
7600         { -1, +1,       MV_LEFT  | MV_DOWN },
7601         { -1,  0,       MV_LEFT            },
7602       };
7603       int start_pos, check_order;
7604       boolean can_clone = FALSE;
7605       int i;
7606
7607       // check if there is any free field around current position
7608       for (i = 0; i < 8; i++)
7609       {
7610         int newx = x + check_xy[i].dx;
7611         int newy = y + check_xy[i].dy;
7612
7613         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7614         {
7615           can_clone = TRUE;
7616
7617           break;
7618         }
7619       }
7620
7621       if (can_clone)            // randomly find an element to clone
7622       {
7623         can_clone = FALSE;
7624
7625         start_pos = check_pos[RND(8)];
7626         check_order = (RND(2) ? -1 : +1);
7627
7628         for (i = 0; i < 8; i++)
7629         {
7630           int pos_raw = start_pos + i * check_order;
7631           int pos = (pos_raw + 8) % 8;
7632           int newx = x + check_xy[pos].dx;
7633           int newy = y + check_xy[pos].dy;
7634
7635           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7636           {
7637             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7638             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7639
7640             Store[x][y] = Tile[newx][newy];
7641
7642             can_clone = TRUE;
7643
7644             break;
7645           }
7646         }
7647       }
7648
7649       if (can_clone)            // randomly find a direction to move
7650       {
7651         can_clone = FALSE;
7652
7653         start_pos = check_pos[RND(8)];
7654         check_order = (RND(2) ? -1 : +1);
7655
7656         for (i = 0; i < 8; i++)
7657         {
7658           int pos_raw = start_pos + i * check_order;
7659           int pos = (pos_raw + 8) % 8;
7660           int newx = x + check_xy[pos].dx;
7661           int newy = y + check_xy[pos].dy;
7662           int new_move_dir = check_xy[pos].dir;
7663
7664           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7665           {
7666             MovDir[x][y] = new_move_dir;
7667             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7668
7669             can_clone = TRUE;
7670
7671             break;
7672           }
7673         }
7674       }
7675
7676       if (can_clone)            // cloning and moving successful
7677         return;
7678
7679       // cannot clone -- try to move towards player
7680
7681       start_pos = check_pos[MovDir[x][y] & 0x0f];
7682       check_order = (RND(2) ? -1 : +1);
7683
7684       for (i = 0; i < 3; i++)
7685       {
7686         // first check start_pos, then previous/next or (next/previous) pos
7687         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7688         int pos = (pos_raw + 8) % 8;
7689         int newx = x + check_xy[pos].dx;
7690         int newy = y + check_xy[pos].dy;
7691         int new_move_dir = check_xy[pos].dir;
7692
7693         if (IS_PLAYER(newx, newy))
7694           break;
7695
7696         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7697         {
7698           MovDir[x][y] = new_move_dir;
7699           MovDelay[x][y] = level.android_move_time * 8 + 1;
7700
7701           break;
7702         }
7703       }
7704     }
7705   }
7706   else if (move_pattern == MV_TURNING_LEFT ||
7707            move_pattern == MV_TURNING_RIGHT ||
7708            move_pattern == MV_TURNING_LEFT_RIGHT ||
7709            move_pattern == MV_TURNING_RIGHT_LEFT ||
7710            move_pattern == MV_TURNING_RANDOM ||
7711            move_pattern == MV_ALL_DIRECTIONS)
7712   {
7713     boolean can_turn_left =
7714       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7715     boolean can_turn_right =
7716       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7717
7718     if (element_info[element].move_stepsize == 0)       // "not moving"
7719       return;
7720
7721     if (move_pattern == MV_TURNING_LEFT)
7722       MovDir[x][y] = left_dir;
7723     else if (move_pattern == MV_TURNING_RIGHT)
7724       MovDir[x][y] = right_dir;
7725     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7726       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7727     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7728       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7729     else if (move_pattern == MV_TURNING_RANDOM)
7730       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7731                       can_turn_right && !can_turn_left ? right_dir :
7732                       RND(2) ? left_dir : right_dir);
7733     else if (can_turn_left && can_turn_right)
7734       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7735     else if (can_turn_left)
7736       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7737     else if (can_turn_right)
7738       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7739     else
7740       MovDir[x][y] = back_dir;
7741
7742     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7743   }
7744   else if (move_pattern == MV_HORIZONTAL ||
7745            move_pattern == MV_VERTICAL)
7746   {
7747     if (move_pattern & old_move_dir)
7748       MovDir[x][y] = back_dir;
7749     else if (move_pattern == MV_HORIZONTAL)
7750       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7751     else if (move_pattern == MV_VERTICAL)
7752       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7753
7754     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7755   }
7756   else if (move_pattern & MV_ANY_DIRECTION)
7757   {
7758     MovDir[x][y] = move_pattern;
7759     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7760   }
7761   else if (move_pattern & MV_WIND_DIRECTION)
7762   {
7763     MovDir[x][y] = game.wind_direction;
7764     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7765   }
7766   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7767   {
7768     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7769       MovDir[x][y] = left_dir;
7770     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7771       MovDir[x][y] = right_dir;
7772
7773     if (MovDir[x][y] != old_move_dir)
7774       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7775   }
7776   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7777   {
7778     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7779       MovDir[x][y] = right_dir;
7780     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7781       MovDir[x][y] = left_dir;
7782
7783     if (MovDir[x][y] != old_move_dir)
7784       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7785   }
7786   else if (move_pattern == MV_TOWARDS_PLAYER ||
7787            move_pattern == MV_AWAY_FROM_PLAYER)
7788   {
7789     int attr_x = -1, attr_y = -1;
7790     int newx, newy;
7791     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7792
7793     if (game.all_players_gone)
7794     {
7795       attr_x = game.exit_x;
7796       attr_y = game.exit_y;
7797     }
7798     else
7799     {
7800       int i;
7801
7802       for (i = 0; i < MAX_PLAYERS; i++)
7803       {
7804         struct PlayerInfo *player = &stored_player[i];
7805         int jx = player->jx, jy = player->jy;
7806
7807         if (!player->active)
7808           continue;
7809
7810         if (attr_x == -1 ||
7811             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7812         {
7813           attr_x = jx;
7814           attr_y = jy;
7815         }
7816       }
7817     }
7818
7819     MovDir[x][y] = MV_NONE;
7820     if (attr_x < x)
7821       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7822     else if (attr_x > x)
7823       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7824     if (attr_y < y)
7825       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7826     else if (attr_y > y)
7827       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7828
7829     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7830
7831     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7832     {
7833       boolean first_horiz = RND(2);
7834       int new_move_dir = MovDir[x][y];
7835
7836       if (element_info[element].move_stepsize == 0)     // "not moving"
7837       {
7838         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7839         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7840
7841         return;
7842       }
7843
7844       MovDir[x][y] =
7845         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7846       Moving2Blocked(x, y, &newx, &newy);
7847
7848       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7849         return;
7850
7851       MovDir[x][y] =
7852         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7853       Moving2Blocked(x, y, &newx, &newy);
7854
7855       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7856         return;
7857
7858       MovDir[x][y] = old_move_dir;
7859     }
7860   }
7861   else if (move_pattern == MV_WHEN_PUSHED ||
7862            move_pattern == MV_WHEN_DROPPED)
7863   {
7864     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7865       MovDir[x][y] = MV_NONE;
7866
7867     MovDelay[x][y] = 0;
7868   }
7869   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7870   {
7871     struct XY *test_xy = xy_topdown;
7872     static int test_dir[4] =
7873     {
7874       MV_UP,
7875       MV_LEFT,
7876       MV_RIGHT,
7877       MV_DOWN
7878     };
7879     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7880     int move_preference = -1000000;     // start with very low preference
7881     int new_move_dir = MV_NONE;
7882     int start_test = RND(4);
7883     int i;
7884
7885     for (i = 0; i < NUM_DIRECTIONS; i++)
7886     {
7887       int j = (start_test + i) % 4;
7888       int move_dir = test_dir[j];
7889       int move_dir_preference;
7890
7891       xx = x + test_xy[j].x;
7892       yy = y + test_xy[j].y;
7893
7894       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7895           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7896       {
7897         new_move_dir = move_dir;
7898
7899         break;
7900       }
7901
7902       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7903         continue;
7904
7905       move_dir_preference = -1 * RunnerVisit[xx][yy];
7906       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7907         move_dir_preference = PlayerVisit[xx][yy];
7908
7909       if (move_dir_preference > move_preference)
7910       {
7911         // prefer field that has not been visited for the longest time
7912         move_preference = move_dir_preference;
7913         new_move_dir = move_dir;
7914       }
7915       else if (move_dir_preference == move_preference &&
7916                move_dir == old_move_dir)
7917       {
7918         // prefer last direction when all directions are preferred equally
7919         move_preference = move_dir_preference;
7920         new_move_dir = move_dir;
7921       }
7922     }
7923
7924     MovDir[x][y] = new_move_dir;
7925     if (old_move_dir != new_move_dir)
7926       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7927   }
7928 }
7929
7930 static void TurnRound(int x, int y)
7931 {
7932   int direction = MovDir[x][y];
7933
7934   TurnRoundExt(x, y);
7935
7936   GfxDir[x][y] = MovDir[x][y];
7937
7938   if (direction != MovDir[x][y])
7939     GfxFrame[x][y] = 0;
7940
7941   if (MovDelay[x][y])
7942     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7943
7944   ResetGfxFrame(x, y);
7945 }
7946
7947 static boolean JustBeingPushed(int x, int y)
7948 {
7949   int i;
7950
7951   for (i = 0; i < MAX_PLAYERS; i++)
7952   {
7953     struct PlayerInfo *player = &stored_player[i];
7954
7955     if (player->active && player->is_pushing && player->MovPos)
7956     {
7957       int next_jx = player->jx + (player->jx - player->last_jx);
7958       int next_jy = player->jy + (player->jy - player->last_jy);
7959
7960       if (x == next_jx && y == next_jy)
7961         return TRUE;
7962     }
7963   }
7964
7965   return FALSE;
7966 }
7967
7968 static void StartMoving(int x, int y)
7969 {
7970   boolean started_moving = FALSE;       // some elements can fall _and_ move
7971   int element = Tile[x][y];
7972
7973   if (Stop[x][y])
7974     return;
7975
7976   if (MovDelay[x][y] == 0)
7977     GfxAction[x][y] = ACTION_DEFAULT;
7978
7979   if (CAN_FALL(element) && y < lev_fieldy - 1)
7980   {
7981     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7982         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7983       if (JustBeingPushed(x, y))
7984         return;
7985
7986     if (element == EL_QUICKSAND_FULL)
7987     {
7988       if (IS_FREE(x, y + 1))
7989       {
7990         InitMovingField(x, y, MV_DOWN);
7991         started_moving = TRUE;
7992
7993         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7994 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7995         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7996           Store[x][y] = EL_ROCK;
7997 #else
7998         Store[x][y] = EL_ROCK;
7999 #endif
8000
8001         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8002       }
8003       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8004       {
8005         if (!MovDelay[x][y])
8006         {
8007           MovDelay[x][y] = TILEY + 1;
8008
8009           ResetGfxAnimation(x, y);
8010           ResetGfxAnimation(x, y + 1);
8011         }
8012
8013         if (MovDelay[x][y])
8014         {
8015           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8016           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8017
8018           MovDelay[x][y]--;
8019           if (MovDelay[x][y])
8020             return;
8021         }
8022
8023         Tile[x][y] = EL_QUICKSAND_EMPTY;
8024         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8025         Store[x][y + 1] = Store[x][y];
8026         Store[x][y] = 0;
8027
8028         PlayLevelSoundAction(x, y, ACTION_FILLING);
8029       }
8030       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8031       {
8032         if (!MovDelay[x][y])
8033         {
8034           MovDelay[x][y] = TILEY + 1;
8035
8036           ResetGfxAnimation(x, y);
8037           ResetGfxAnimation(x, y + 1);
8038         }
8039
8040         if (MovDelay[x][y])
8041         {
8042           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8043           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8044
8045           MovDelay[x][y]--;
8046           if (MovDelay[x][y])
8047             return;
8048         }
8049
8050         Tile[x][y] = EL_QUICKSAND_EMPTY;
8051         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8052         Store[x][y + 1] = Store[x][y];
8053         Store[x][y] = 0;
8054
8055         PlayLevelSoundAction(x, y, ACTION_FILLING);
8056       }
8057     }
8058     else if (element == EL_QUICKSAND_FAST_FULL)
8059     {
8060       if (IS_FREE(x, y + 1))
8061       {
8062         InitMovingField(x, y, MV_DOWN);
8063         started_moving = TRUE;
8064
8065         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8066 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8067         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8068           Store[x][y] = EL_ROCK;
8069 #else
8070         Store[x][y] = EL_ROCK;
8071 #endif
8072
8073         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8074       }
8075       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8076       {
8077         if (!MovDelay[x][y])
8078         {
8079           MovDelay[x][y] = TILEY + 1;
8080
8081           ResetGfxAnimation(x, y);
8082           ResetGfxAnimation(x, y + 1);
8083         }
8084
8085         if (MovDelay[x][y])
8086         {
8087           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8088           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8089
8090           MovDelay[x][y]--;
8091           if (MovDelay[x][y])
8092             return;
8093         }
8094
8095         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8096         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8097         Store[x][y + 1] = Store[x][y];
8098         Store[x][y] = 0;
8099
8100         PlayLevelSoundAction(x, y, ACTION_FILLING);
8101       }
8102       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8103       {
8104         if (!MovDelay[x][y])
8105         {
8106           MovDelay[x][y] = TILEY + 1;
8107
8108           ResetGfxAnimation(x, y);
8109           ResetGfxAnimation(x, y + 1);
8110         }
8111
8112         if (MovDelay[x][y])
8113         {
8114           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8115           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8116
8117           MovDelay[x][y]--;
8118           if (MovDelay[x][y])
8119             return;
8120         }
8121
8122         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8123         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8124         Store[x][y + 1] = Store[x][y];
8125         Store[x][y] = 0;
8126
8127         PlayLevelSoundAction(x, y, ACTION_FILLING);
8128       }
8129     }
8130     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8131              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8132     {
8133       InitMovingField(x, y, MV_DOWN);
8134       started_moving = TRUE;
8135
8136       Tile[x][y] = EL_QUICKSAND_FILLING;
8137       Store[x][y] = element;
8138
8139       PlayLevelSoundAction(x, y, ACTION_FILLING);
8140     }
8141     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8142              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8143     {
8144       InitMovingField(x, y, MV_DOWN);
8145       started_moving = TRUE;
8146
8147       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8148       Store[x][y] = element;
8149
8150       PlayLevelSoundAction(x, y, ACTION_FILLING);
8151     }
8152     else if (element == EL_MAGIC_WALL_FULL)
8153     {
8154       if (IS_FREE(x, y + 1))
8155       {
8156         InitMovingField(x, y, MV_DOWN);
8157         started_moving = TRUE;
8158
8159         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8160         Store[x][y] = EL_CHANGED(Store[x][y]);
8161       }
8162       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8163       {
8164         if (!MovDelay[x][y])
8165           MovDelay[x][y] = TILEY / 4 + 1;
8166
8167         if (MovDelay[x][y])
8168         {
8169           MovDelay[x][y]--;
8170           if (MovDelay[x][y])
8171             return;
8172         }
8173
8174         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8175         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8176         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8177         Store[x][y] = 0;
8178       }
8179     }
8180     else if (element == EL_BD_MAGIC_WALL_FULL)
8181     {
8182       if (IS_FREE(x, y + 1))
8183       {
8184         InitMovingField(x, y, MV_DOWN);
8185         started_moving = TRUE;
8186
8187         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8188         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8189       }
8190       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8191       {
8192         if (!MovDelay[x][y])
8193           MovDelay[x][y] = TILEY / 4 + 1;
8194
8195         if (MovDelay[x][y])
8196         {
8197           MovDelay[x][y]--;
8198           if (MovDelay[x][y])
8199             return;
8200         }
8201
8202         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8203         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8204         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8205         Store[x][y] = 0;
8206       }
8207     }
8208     else if (element == EL_DC_MAGIC_WALL_FULL)
8209     {
8210       if (IS_FREE(x, y + 1))
8211       {
8212         InitMovingField(x, y, MV_DOWN);
8213         started_moving = TRUE;
8214
8215         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8216         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8217       }
8218       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8219       {
8220         if (!MovDelay[x][y])
8221           MovDelay[x][y] = TILEY / 4 + 1;
8222
8223         if (MovDelay[x][y])
8224         {
8225           MovDelay[x][y]--;
8226           if (MovDelay[x][y])
8227             return;
8228         }
8229
8230         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8231         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8232         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8233         Store[x][y] = 0;
8234       }
8235     }
8236     else if ((CAN_PASS_MAGIC_WALL(element) &&
8237               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8238                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8239              (CAN_PASS_DC_MAGIC_WALL(element) &&
8240               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8241
8242     {
8243       InitMovingField(x, y, MV_DOWN);
8244       started_moving = TRUE;
8245
8246       Tile[x][y] =
8247         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8248          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8249          EL_DC_MAGIC_WALL_FILLING);
8250       Store[x][y] = element;
8251     }
8252     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8253     {
8254       SplashAcid(x, y + 1);
8255
8256       InitMovingField(x, y, MV_DOWN);
8257       started_moving = TRUE;
8258
8259       Store[x][y] = EL_ACID;
8260     }
8261     else if (
8262              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8263               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8264              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8265               CAN_FALL(element) && WasJustFalling[x][y] &&
8266               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8267
8268              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8269               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8270               (Tile[x][y + 1] == EL_BLOCKED)))
8271     {
8272       /* this is needed for a special case not covered by calling "Impact()"
8273          from "ContinueMoving()": if an element moves to a tile directly below
8274          another element which was just falling on that tile (which was empty
8275          in the previous frame), the falling element above would just stop
8276          instead of smashing the element below (in previous version, the above
8277          element was just checked for "moving" instead of "falling", resulting
8278          in incorrect smashes caused by horizontal movement of the above
8279          element; also, the case of the player being the element to smash was
8280          simply not covered here... :-/ ) */
8281
8282       CheckCollision[x][y] = 0;
8283       CheckImpact[x][y] = 0;
8284
8285       Impact(x, y);
8286     }
8287     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8288     {
8289       if (MovDir[x][y] == MV_NONE)
8290       {
8291         InitMovingField(x, y, MV_DOWN);
8292         started_moving = TRUE;
8293       }
8294     }
8295     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8296     {
8297       if (WasJustFalling[x][y]) // prevent animation from being restarted
8298         MovDir[x][y] = MV_DOWN;
8299
8300       InitMovingField(x, y, MV_DOWN);
8301       started_moving = TRUE;
8302     }
8303     else if (element == EL_AMOEBA_DROP)
8304     {
8305       Tile[x][y] = EL_AMOEBA_GROWING;
8306       Store[x][y] = EL_AMOEBA_WET;
8307     }
8308     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8309               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8310              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8311              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8312     {
8313       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8314                                 (IS_FREE(x - 1, y + 1) ||
8315                                  Tile[x - 1][y + 1] == EL_ACID));
8316       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8317                                 (IS_FREE(x + 1, y + 1) ||
8318                                  Tile[x + 1][y + 1] == EL_ACID));
8319       boolean can_fall_any  = (can_fall_left || can_fall_right);
8320       boolean can_fall_both = (can_fall_left && can_fall_right);
8321       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8322
8323       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8324       {
8325         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8326           can_fall_right = FALSE;
8327         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8328           can_fall_left = FALSE;
8329         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8330           can_fall_right = FALSE;
8331         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8332           can_fall_left = FALSE;
8333
8334         can_fall_any  = (can_fall_left || can_fall_right);
8335         can_fall_both = FALSE;
8336       }
8337
8338       if (can_fall_both)
8339       {
8340         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8341           can_fall_right = FALSE;       // slip down on left side
8342         else
8343           can_fall_left = !(can_fall_right = RND(2));
8344
8345         can_fall_both = FALSE;
8346       }
8347
8348       if (can_fall_any)
8349       {
8350         // if not determined otherwise, prefer left side for slipping down
8351         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8352         started_moving = TRUE;
8353       }
8354     }
8355     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8356     {
8357       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8358       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8359       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8360       int belt_dir = game.belt_dir[belt_nr];
8361
8362       if ((belt_dir == MV_LEFT  && left_is_free) ||
8363           (belt_dir == MV_RIGHT && right_is_free))
8364       {
8365         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8366
8367         InitMovingField(x, y, belt_dir);
8368         started_moving = TRUE;
8369
8370         Pushed[x][y] = TRUE;
8371         Pushed[nextx][y] = TRUE;
8372
8373         GfxAction[x][y] = ACTION_DEFAULT;
8374       }
8375       else
8376       {
8377         MovDir[x][y] = 0;       // if element was moving, stop it
8378       }
8379     }
8380   }
8381
8382   // not "else if" because of elements that can fall and move (EL_SPRING)
8383   if (CAN_MOVE(element) && !started_moving)
8384   {
8385     int move_pattern = element_info[element].move_pattern;
8386     int newx, newy;
8387
8388     Moving2Blocked(x, y, &newx, &newy);
8389
8390     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8391       return;
8392
8393     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8394         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8395     {
8396       WasJustMoving[x][y] = 0;
8397       CheckCollision[x][y] = 0;
8398
8399       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8400
8401       if (Tile[x][y] != element)        // element has changed
8402         return;
8403     }
8404
8405     if (!MovDelay[x][y])        // start new movement phase
8406     {
8407       // all objects that can change their move direction after each step
8408       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8409
8410       if (element != EL_YAMYAM &&
8411           element != EL_DARK_YAMYAM &&
8412           element != EL_PACMAN &&
8413           !(move_pattern & MV_ANY_DIRECTION) &&
8414           move_pattern != MV_TURNING_LEFT &&
8415           move_pattern != MV_TURNING_RIGHT &&
8416           move_pattern != MV_TURNING_LEFT_RIGHT &&
8417           move_pattern != MV_TURNING_RIGHT_LEFT &&
8418           move_pattern != MV_TURNING_RANDOM)
8419       {
8420         TurnRound(x, y);
8421
8422         if (MovDelay[x][y] && (element == EL_BUG ||
8423                                element == EL_SPACESHIP ||
8424                                element == EL_SP_SNIKSNAK ||
8425                                element == EL_SP_ELECTRON ||
8426                                element == EL_MOLE))
8427           TEST_DrawLevelField(x, y);
8428       }
8429     }
8430
8431     if (MovDelay[x][y])         // wait some time before next movement
8432     {
8433       MovDelay[x][y]--;
8434
8435       if (element == EL_ROBOT ||
8436           element == EL_YAMYAM ||
8437           element == EL_DARK_YAMYAM)
8438       {
8439         DrawLevelElementAnimationIfNeeded(x, y, element);
8440         PlayLevelSoundAction(x, y, ACTION_WAITING);
8441       }
8442       else if (element == EL_SP_ELECTRON)
8443         DrawLevelElementAnimationIfNeeded(x, y, element);
8444       else if (element == EL_DRAGON)
8445       {
8446         int i;
8447         int dir = MovDir[x][y];
8448         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8449         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8450         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8451                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8452                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8453                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8454         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8455
8456         GfxAction[x][y] = ACTION_ATTACKING;
8457
8458         if (IS_PLAYER(x, y))
8459           DrawPlayerField(x, y);
8460         else
8461           TEST_DrawLevelField(x, y);
8462
8463         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8464
8465         for (i = 1; i <= 3; i++)
8466         {
8467           int xx = x + i * dx;
8468           int yy = y + i * dy;
8469           int sx = SCREENX(xx);
8470           int sy = SCREENY(yy);
8471           int flame_graphic = graphic + (i - 1);
8472
8473           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8474             break;
8475
8476           if (MovDelay[x][y])
8477           {
8478             int flamed = MovingOrBlocked2Element(xx, yy);
8479
8480             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8481               Bang(xx, yy);
8482             else
8483               RemoveMovingField(xx, yy);
8484
8485             ChangeDelay[xx][yy] = 0;
8486
8487             Tile[xx][yy] = EL_FLAMES;
8488
8489             if (IN_SCR_FIELD(sx, sy))
8490             {
8491               TEST_DrawLevelFieldCrumbled(xx, yy);
8492               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8493             }
8494           }
8495           else
8496           {
8497             if (Tile[xx][yy] == EL_FLAMES)
8498               Tile[xx][yy] = EL_EMPTY;
8499             TEST_DrawLevelField(xx, yy);
8500           }
8501         }
8502       }
8503
8504       if (MovDelay[x][y])       // element still has to wait some time
8505       {
8506         PlayLevelSoundAction(x, y, ACTION_WAITING);
8507
8508         return;
8509       }
8510     }
8511
8512     // now make next step
8513
8514     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8515
8516     if (DONT_COLLIDE_WITH(element) &&
8517         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8518         !PLAYER_ENEMY_PROTECTED(newx, newy))
8519     {
8520       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8521
8522       return;
8523     }
8524
8525     else if (CAN_MOVE_INTO_ACID(element) &&
8526              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8527              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8528              (MovDir[x][y] == MV_DOWN ||
8529               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8530     {
8531       SplashAcid(newx, newy);
8532       Store[x][y] = EL_ACID;
8533     }
8534     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8535     {
8536       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8537           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8538           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8539           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8540       {
8541         RemoveField(x, y);
8542         TEST_DrawLevelField(x, y);
8543
8544         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8545         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8546           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8547
8548         game.friends_still_needed--;
8549         if (!game.friends_still_needed &&
8550             !game.GameOver &&
8551             game.all_players_gone)
8552           LevelSolved();
8553
8554         return;
8555       }
8556       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8557       {
8558         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8559           TEST_DrawLevelField(newx, newy);
8560         else
8561           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8562       }
8563       else if (!IS_FREE(newx, newy))
8564       {
8565         GfxAction[x][y] = ACTION_WAITING;
8566
8567         if (IS_PLAYER(x, y))
8568           DrawPlayerField(x, y);
8569         else
8570           TEST_DrawLevelField(x, y);
8571
8572         return;
8573       }
8574     }
8575     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8576     {
8577       if (IS_FOOD_PIG(Tile[newx][newy]))
8578       {
8579         if (IS_MOVING(newx, newy))
8580           RemoveMovingField(newx, newy);
8581         else
8582         {
8583           Tile[newx][newy] = EL_EMPTY;
8584           TEST_DrawLevelField(newx, newy);
8585         }
8586
8587         PlayLevelSound(x, y, SND_PIG_DIGGING);
8588       }
8589       else if (!IS_FREE(newx, newy))
8590       {
8591         if (IS_PLAYER(x, y))
8592           DrawPlayerField(x, y);
8593         else
8594           TEST_DrawLevelField(x, y);
8595
8596         return;
8597       }
8598     }
8599     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8600     {
8601       if (Store[x][y] != EL_EMPTY)
8602       {
8603         boolean can_clone = FALSE;
8604         int xx, yy;
8605
8606         // check if element to clone is still there
8607         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8608         {
8609           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8610           {
8611             can_clone = TRUE;
8612
8613             break;
8614           }
8615         }
8616
8617         // cannot clone or target field not free anymore -- do not clone
8618         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8619           Store[x][y] = EL_EMPTY;
8620       }
8621
8622       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8623       {
8624         if (IS_MV_DIAGONAL(MovDir[x][y]))
8625         {
8626           int diagonal_move_dir = MovDir[x][y];
8627           int stored = Store[x][y];
8628           int change_delay = 8;
8629           int graphic;
8630
8631           // android is moving diagonally
8632
8633           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8634
8635           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8636           GfxElement[x][y] = EL_EMC_ANDROID;
8637           GfxAction[x][y] = ACTION_SHRINKING;
8638           GfxDir[x][y] = diagonal_move_dir;
8639           ChangeDelay[x][y] = change_delay;
8640
8641           if (Store[x][y] == EL_EMPTY)
8642             Store[x][y] = GfxElementEmpty[x][y];
8643
8644           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8645                                    GfxDir[x][y]);
8646
8647           DrawLevelGraphicAnimation(x, y, graphic);
8648           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8649
8650           if (Tile[newx][newy] == EL_ACID)
8651           {
8652             SplashAcid(newx, newy);
8653
8654             return;
8655           }
8656
8657           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8658
8659           Store[newx][newy] = EL_EMC_ANDROID;
8660           GfxElement[newx][newy] = EL_EMC_ANDROID;
8661           GfxAction[newx][newy] = ACTION_GROWING;
8662           GfxDir[newx][newy] = diagonal_move_dir;
8663           ChangeDelay[newx][newy] = change_delay;
8664
8665           graphic = el_act_dir2img(GfxElement[newx][newy],
8666                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8667
8668           DrawLevelGraphicAnimation(newx, newy, graphic);
8669           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8670
8671           return;
8672         }
8673         else
8674         {
8675           Tile[newx][newy] = EL_EMPTY;
8676           TEST_DrawLevelField(newx, newy);
8677
8678           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8679         }
8680       }
8681       else if (!IS_FREE(newx, newy))
8682       {
8683         return;
8684       }
8685     }
8686     else if (IS_CUSTOM_ELEMENT(element) &&
8687              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8688     {
8689       if (!DigFieldByCE(newx, newy, element))
8690         return;
8691
8692       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8693       {
8694         RunnerVisit[x][y] = FrameCounter;
8695         PlayerVisit[x][y] /= 8;         // expire player visit path
8696       }
8697     }
8698     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8699     {
8700       if (!IS_FREE(newx, newy))
8701       {
8702         if (IS_PLAYER(x, y))
8703           DrawPlayerField(x, y);
8704         else
8705           TEST_DrawLevelField(x, y);
8706
8707         return;
8708       }
8709       else
8710       {
8711         boolean wanna_flame = !RND(10);
8712         int dx = newx - x, dy = newy - y;
8713         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8714         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8715         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8716                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8717         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8718                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8719
8720         if ((wanna_flame ||
8721              IS_CLASSIC_ENEMY(element1) ||
8722              IS_CLASSIC_ENEMY(element2)) &&
8723             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8724             element1 != EL_FLAMES && element2 != EL_FLAMES)
8725         {
8726           ResetGfxAnimation(x, y);
8727           GfxAction[x][y] = ACTION_ATTACKING;
8728
8729           if (IS_PLAYER(x, y))
8730             DrawPlayerField(x, y);
8731           else
8732             TEST_DrawLevelField(x, y);
8733
8734           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8735
8736           MovDelay[x][y] = 50;
8737
8738           Tile[newx][newy] = EL_FLAMES;
8739           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8740             Tile[newx1][newy1] = EL_FLAMES;
8741           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8742             Tile[newx2][newy2] = EL_FLAMES;
8743
8744           return;
8745         }
8746       }
8747     }
8748     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8749              Tile[newx][newy] == EL_DIAMOND)
8750     {
8751       if (IS_MOVING(newx, newy))
8752         RemoveMovingField(newx, newy);
8753       else
8754       {
8755         Tile[newx][newy] = EL_EMPTY;
8756         TEST_DrawLevelField(newx, newy);
8757       }
8758
8759       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8760     }
8761     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8762              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8763     {
8764       if (AmoebaNr[newx][newy])
8765       {
8766         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8767         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8768             Tile[newx][newy] == EL_BD_AMOEBA)
8769           AmoebaCnt[AmoebaNr[newx][newy]]--;
8770       }
8771
8772       if (IS_MOVING(newx, newy))
8773       {
8774         RemoveMovingField(newx, newy);
8775       }
8776       else
8777       {
8778         Tile[newx][newy] = EL_EMPTY;
8779         TEST_DrawLevelField(newx, newy);
8780       }
8781
8782       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8783     }
8784     else if ((element == EL_PACMAN || element == EL_MOLE)
8785              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8786     {
8787       if (AmoebaNr[newx][newy])
8788       {
8789         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8790         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8791             Tile[newx][newy] == EL_BD_AMOEBA)
8792           AmoebaCnt[AmoebaNr[newx][newy]]--;
8793       }
8794
8795       if (element == EL_MOLE)
8796       {
8797         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8798         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8799
8800         ResetGfxAnimation(x, y);
8801         GfxAction[x][y] = ACTION_DIGGING;
8802         TEST_DrawLevelField(x, y);
8803
8804         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8805
8806         return;                         // wait for shrinking amoeba
8807       }
8808       else      // element == EL_PACMAN
8809       {
8810         Tile[newx][newy] = EL_EMPTY;
8811         TEST_DrawLevelField(newx, newy);
8812         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8813       }
8814     }
8815     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8816              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8817               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8818     {
8819       // wait for shrinking amoeba to completely disappear
8820       return;
8821     }
8822     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8823     {
8824       // object was running against a wall
8825
8826       TurnRound(x, y);
8827
8828       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8829         DrawLevelElementAnimation(x, y, element);
8830
8831       if (DONT_TOUCH(element))
8832         TestIfBadThingTouchesPlayer(x, y);
8833
8834       return;
8835     }
8836
8837     InitMovingField(x, y, MovDir[x][y]);
8838
8839     PlayLevelSoundAction(x, y, ACTION_MOVING);
8840   }
8841
8842   if (MovDir[x][y])
8843     ContinueMoving(x, y);
8844 }
8845
8846 void ContinueMoving(int x, int y)
8847 {
8848   int element = Tile[x][y];
8849   struct ElementInfo *ei = &element_info[element];
8850   int direction = MovDir[x][y];
8851   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8852   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8853   int newx = x + dx, newy = y + dy;
8854   int stored = Store[x][y];
8855   int stored_new = Store[newx][newy];
8856   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8857   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8858   boolean last_line = (newy == lev_fieldy - 1);
8859   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8860
8861   if (pushed_by_player)         // special case: moving object pushed by player
8862   {
8863     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8864   }
8865   else if (use_step_delay)      // special case: moving object has step delay
8866   {
8867     if (!MovDelay[x][y])
8868       MovPos[x][y] += getElementMoveStepsize(x, y);
8869
8870     if (MovDelay[x][y])
8871       MovDelay[x][y]--;
8872     else
8873       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8874
8875     if (MovDelay[x][y])
8876     {
8877       TEST_DrawLevelField(x, y);
8878
8879       return;   // element is still waiting
8880     }
8881   }
8882   else                          // normal case: generically moving object
8883   {
8884     MovPos[x][y] += getElementMoveStepsize(x, y);
8885   }
8886
8887   if (ABS(MovPos[x][y]) < TILEX)
8888   {
8889     TEST_DrawLevelField(x, y);
8890
8891     return;     // element is still moving
8892   }
8893
8894   // element reached destination field
8895
8896   Tile[x][y] = EL_EMPTY;
8897   Tile[newx][newy] = element;
8898   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8899
8900   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8901   {
8902     element = Tile[newx][newy] = EL_ACID;
8903   }
8904   else if (element == EL_MOLE)
8905   {
8906     Tile[x][y] = EL_SAND;
8907
8908     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8909   }
8910   else if (element == EL_QUICKSAND_FILLING)
8911   {
8912     element = Tile[newx][newy] = get_next_element(element);
8913     Store[newx][newy] = Store[x][y];
8914   }
8915   else if (element == EL_QUICKSAND_EMPTYING)
8916   {
8917     Tile[x][y] = get_next_element(element);
8918     element = Tile[newx][newy] = Store[x][y];
8919   }
8920   else if (element == EL_QUICKSAND_FAST_FILLING)
8921   {
8922     element = Tile[newx][newy] = get_next_element(element);
8923     Store[newx][newy] = Store[x][y];
8924   }
8925   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8926   {
8927     Tile[x][y] = get_next_element(element);
8928     element = Tile[newx][newy] = Store[x][y];
8929   }
8930   else if (element == EL_MAGIC_WALL_FILLING)
8931   {
8932     element = Tile[newx][newy] = get_next_element(element);
8933     if (!game.magic_wall_active)
8934       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8935     Store[newx][newy] = Store[x][y];
8936   }
8937   else if (element == EL_MAGIC_WALL_EMPTYING)
8938   {
8939     Tile[x][y] = get_next_element(element);
8940     if (!game.magic_wall_active)
8941       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8942     element = Tile[newx][newy] = Store[x][y];
8943
8944     InitField(newx, newy, FALSE);
8945   }
8946   else if (element == EL_BD_MAGIC_WALL_FILLING)
8947   {
8948     element = Tile[newx][newy] = get_next_element(element);
8949     if (!game.magic_wall_active)
8950       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8951     Store[newx][newy] = Store[x][y];
8952   }
8953   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8954   {
8955     Tile[x][y] = get_next_element(element);
8956     if (!game.magic_wall_active)
8957       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8958     element = Tile[newx][newy] = Store[x][y];
8959
8960     InitField(newx, newy, FALSE);
8961   }
8962   else if (element == EL_DC_MAGIC_WALL_FILLING)
8963   {
8964     element = Tile[newx][newy] = get_next_element(element);
8965     if (!game.magic_wall_active)
8966       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8967     Store[newx][newy] = Store[x][y];
8968   }
8969   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8970   {
8971     Tile[x][y] = get_next_element(element);
8972     if (!game.magic_wall_active)
8973       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8974     element = Tile[newx][newy] = Store[x][y];
8975
8976     InitField(newx, newy, FALSE);
8977   }
8978   else if (element == EL_AMOEBA_DROPPING)
8979   {
8980     Tile[x][y] = get_next_element(element);
8981     element = Tile[newx][newy] = Store[x][y];
8982   }
8983   else if (element == EL_SOKOBAN_OBJECT)
8984   {
8985     if (Back[x][y])
8986       Tile[x][y] = Back[x][y];
8987
8988     if (Back[newx][newy])
8989       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8990
8991     Back[x][y] = Back[newx][newy] = 0;
8992   }
8993
8994   Store[x][y] = EL_EMPTY;
8995   MovPos[x][y] = 0;
8996   MovDir[x][y] = 0;
8997   MovDelay[x][y] = 0;
8998
8999   MovDelay[newx][newy] = 0;
9000
9001   if (CAN_CHANGE_OR_HAS_ACTION(element))
9002   {
9003     // copy element change control values to new field
9004     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9005     ChangePage[newx][newy]  = ChangePage[x][y];
9006     ChangeCount[newx][newy] = ChangeCount[x][y];
9007     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9008   }
9009
9010   CustomValue[newx][newy] = CustomValue[x][y];
9011
9012   ChangeDelay[x][y] = 0;
9013   ChangePage[x][y] = -1;
9014   ChangeCount[x][y] = 0;
9015   ChangeEvent[x][y] = -1;
9016
9017   CustomValue[x][y] = 0;
9018
9019   // copy animation control values to new field
9020   GfxFrame[newx][newy]  = GfxFrame[x][y];
9021   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
9022   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
9023   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
9024
9025   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9026
9027   // some elements can leave other elements behind after moving
9028   if (ei->move_leave_element != EL_EMPTY &&
9029       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9030       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9031   {
9032     int move_leave_element = ei->move_leave_element;
9033
9034     // this makes it possible to leave the removed element again
9035     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9036       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9037
9038     Tile[x][y] = move_leave_element;
9039
9040     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9041       MovDir[x][y] = direction;
9042
9043     InitField(x, y, FALSE);
9044
9045     if (GFX_CRUMBLED(Tile[x][y]))
9046       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9047
9048     if (IS_PLAYER_ELEMENT(move_leave_element))
9049       RelocatePlayer(x, y, move_leave_element);
9050   }
9051
9052   // do this after checking for left-behind element
9053   ResetGfxAnimation(x, y);      // reset animation values for old field
9054
9055   if (!CAN_MOVE(element) ||
9056       (CAN_FALL(element) && direction == MV_DOWN &&
9057        (element == EL_SPRING ||
9058         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9059         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9060     GfxDir[x][y] = MovDir[newx][newy] = 0;
9061
9062   TEST_DrawLevelField(x, y);
9063   TEST_DrawLevelField(newx, newy);
9064
9065   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9066
9067   // prevent pushed element from moving on in pushed direction
9068   if (pushed_by_player && CAN_MOVE(element) &&
9069       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9070       !(element_info[element].move_pattern & direction))
9071     TurnRound(newx, newy);
9072
9073   // prevent elements on conveyor belt from moving on in last direction
9074   if (pushed_by_conveyor && CAN_FALL(element) &&
9075       direction & MV_HORIZONTAL)
9076     MovDir[newx][newy] = 0;
9077
9078   if (!pushed_by_player)
9079   {
9080     int nextx = newx + dx, nexty = newy + dy;
9081     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9082
9083     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9084
9085     if (CAN_FALL(element) && direction == MV_DOWN)
9086       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9087
9088     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9089       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9090
9091     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9092       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9093   }
9094
9095   if (DONT_TOUCH(element))      // object may be nasty to player or others
9096   {
9097     TestIfBadThingTouchesPlayer(newx, newy);
9098     TestIfBadThingTouchesFriend(newx, newy);
9099
9100     if (!IS_CUSTOM_ELEMENT(element))
9101       TestIfBadThingTouchesOtherBadThing(newx, newy);
9102   }
9103   else if (element == EL_PENGUIN)
9104     TestIfFriendTouchesBadThing(newx, newy);
9105
9106   if (DONT_GET_HIT_BY(element))
9107   {
9108     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9109   }
9110
9111   // give the player one last chance (one more frame) to move away
9112   if (CAN_FALL(element) && direction == MV_DOWN &&
9113       (last_line || (!IS_FREE(x, newy + 1) &&
9114                      (!IS_PLAYER(x, newy + 1) ||
9115                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9116     Impact(x, newy);
9117
9118   if (pushed_by_player && !game.use_change_when_pushing_bug)
9119   {
9120     int push_side = MV_DIR_OPPOSITE(direction);
9121     struct PlayerInfo *player = PLAYERINFO(x, y);
9122
9123     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9124                                player->index_bit, push_side);
9125     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9126                                         player->index_bit, push_side);
9127   }
9128
9129   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9130     MovDelay[newx][newy] = 1;
9131
9132   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9133
9134   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9135   TestIfElementHitsCustomElement(newx, newy, direction);
9136   TestIfPlayerTouchesCustomElement(newx, newy);
9137   TestIfElementTouchesCustomElement(newx, newy);
9138
9139   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9140       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9141     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9142                              MV_DIR_OPPOSITE(direction));
9143 }
9144
9145 int AmoebaNeighbourNr(int ax, int ay)
9146 {
9147   int i;
9148   int element = Tile[ax][ay];
9149   int group_nr = 0;
9150   struct XY *xy = xy_topdown;
9151
9152   for (i = 0; i < NUM_DIRECTIONS; i++)
9153   {
9154     int x = ax + xy[i].x;
9155     int y = ay + xy[i].y;
9156
9157     if (!IN_LEV_FIELD(x, y))
9158       continue;
9159
9160     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9161       group_nr = AmoebaNr[x][y];
9162   }
9163
9164   return group_nr;
9165 }
9166
9167 static void AmoebaMerge(int ax, int ay)
9168 {
9169   int i, x, y, xx, yy;
9170   int new_group_nr = AmoebaNr[ax][ay];
9171   struct XY *xy = xy_topdown;
9172
9173   if (new_group_nr == 0)
9174     return;
9175
9176   for (i = 0; i < NUM_DIRECTIONS; i++)
9177   {
9178     x = ax + xy[i].x;
9179     y = ay + xy[i].y;
9180
9181     if (!IN_LEV_FIELD(x, y))
9182       continue;
9183
9184     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9185          Tile[x][y] == EL_BD_AMOEBA ||
9186          Tile[x][y] == EL_AMOEBA_DEAD) &&
9187         AmoebaNr[x][y] != new_group_nr)
9188     {
9189       int old_group_nr = AmoebaNr[x][y];
9190
9191       if (old_group_nr == 0)
9192         return;
9193
9194       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9195       AmoebaCnt[old_group_nr] = 0;
9196       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9197       AmoebaCnt2[old_group_nr] = 0;
9198
9199       SCAN_PLAYFIELD(xx, yy)
9200       {
9201         if (AmoebaNr[xx][yy] == old_group_nr)
9202           AmoebaNr[xx][yy] = new_group_nr;
9203       }
9204     }
9205   }
9206 }
9207
9208 void AmoebaToDiamond(int ax, int ay)
9209 {
9210   int i, x, y;
9211
9212   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9213   {
9214     int group_nr = AmoebaNr[ax][ay];
9215
9216 #ifdef DEBUG
9217     if (group_nr == 0)
9218     {
9219       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9220       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9221
9222       return;
9223     }
9224 #endif
9225
9226     SCAN_PLAYFIELD(x, y)
9227     {
9228       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9229       {
9230         AmoebaNr[x][y] = 0;
9231         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9232       }
9233     }
9234
9235     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9236                             SND_AMOEBA_TURNING_TO_GEM :
9237                             SND_AMOEBA_TURNING_TO_ROCK));
9238     Bang(ax, ay);
9239   }
9240   else
9241   {
9242     struct XY *xy = xy_topdown;
9243
9244     for (i = 0; i < NUM_DIRECTIONS; i++)
9245     {
9246       x = ax + xy[i].x;
9247       y = ay + xy[i].y;
9248
9249       if (!IN_LEV_FIELD(x, y))
9250         continue;
9251
9252       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9253       {
9254         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9255                               SND_AMOEBA_TURNING_TO_GEM :
9256                               SND_AMOEBA_TURNING_TO_ROCK));
9257         Bang(x, y);
9258       }
9259     }
9260   }
9261 }
9262
9263 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9264 {
9265   int x, y;
9266   int group_nr = AmoebaNr[ax][ay];
9267   boolean done = FALSE;
9268
9269 #ifdef DEBUG
9270   if (group_nr == 0)
9271   {
9272     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9273     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9274
9275     return;
9276   }
9277 #endif
9278
9279   SCAN_PLAYFIELD(x, y)
9280   {
9281     if (AmoebaNr[x][y] == group_nr &&
9282         (Tile[x][y] == EL_AMOEBA_DEAD ||
9283          Tile[x][y] == EL_BD_AMOEBA ||
9284          Tile[x][y] == EL_AMOEBA_GROWING))
9285     {
9286       AmoebaNr[x][y] = 0;
9287       Tile[x][y] = new_element;
9288       InitField(x, y, FALSE);
9289       TEST_DrawLevelField(x, y);
9290       done = TRUE;
9291     }
9292   }
9293
9294   if (done)
9295     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9296                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9297                             SND_BD_AMOEBA_TURNING_TO_GEM));
9298 }
9299
9300 static void AmoebaGrowing(int x, int y)
9301 {
9302   static DelayCounter sound_delay = { 0 };
9303
9304   if (!MovDelay[x][y])          // start new growing cycle
9305   {
9306     MovDelay[x][y] = 7;
9307
9308     if (DelayReached(&sound_delay))
9309     {
9310       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9311       sound_delay.value = 30;
9312     }
9313   }
9314
9315   if (MovDelay[x][y])           // wait some time before growing bigger
9316   {
9317     MovDelay[x][y]--;
9318     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9319     {
9320       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9321                                            6 - MovDelay[x][y]);
9322
9323       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9324     }
9325
9326     if (!MovDelay[x][y])
9327     {
9328       Tile[x][y] = Store[x][y];
9329       Store[x][y] = 0;
9330       TEST_DrawLevelField(x, y);
9331     }
9332   }
9333 }
9334
9335 static void AmoebaShrinking(int x, int y)
9336 {
9337   static DelayCounter sound_delay = { 0 };
9338
9339   if (!MovDelay[x][y])          // start new shrinking cycle
9340   {
9341     MovDelay[x][y] = 7;
9342
9343     if (DelayReached(&sound_delay))
9344       sound_delay.value = 30;
9345   }
9346
9347   if (MovDelay[x][y])           // wait some time before shrinking
9348   {
9349     MovDelay[x][y]--;
9350     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9351     {
9352       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9353                                            6 - MovDelay[x][y]);
9354
9355       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9356     }
9357
9358     if (!MovDelay[x][y])
9359     {
9360       Tile[x][y] = EL_EMPTY;
9361       TEST_DrawLevelField(x, y);
9362
9363       // don't let mole enter this field in this cycle;
9364       // (give priority to objects falling to this field from above)
9365       Stop[x][y] = TRUE;
9366     }
9367   }
9368 }
9369
9370 static void AmoebaReproduce(int ax, int ay)
9371 {
9372   int i;
9373   int element = Tile[ax][ay];
9374   int graphic = el2img(element);
9375   int newax = ax, neway = ay;
9376   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9377   struct XY *xy = xy_topdown;
9378
9379   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9380   {
9381     Tile[ax][ay] = EL_AMOEBA_DEAD;
9382     TEST_DrawLevelField(ax, ay);
9383     return;
9384   }
9385
9386   if (IS_ANIMATED(graphic))
9387     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9388
9389   if (!MovDelay[ax][ay])        // start making new amoeba field
9390     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9391
9392   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9393   {
9394     MovDelay[ax][ay]--;
9395     if (MovDelay[ax][ay])
9396       return;
9397   }
9398
9399   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9400   {
9401     int start = RND(4);
9402     int x = ax + xy[start].x;
9403     int y = ay + xy[start].y;
9404
9405     if (!IN_LEV_FIELD(x, y))
9406       return;
9407
9408     if (IS_FREE(x, y) ||
9409         CAN_GROW_INTO(Tile[x][y]) ||
9410         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9411         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9412     {
9413       newax = x;
9414       neway = y;
9415     }
9416
9417     if (newax == ax && neway == ay)
9418       return;
9419   }
9420   else                          // normal or "filled" (BD style) amoeba
9421   {
9422     int start = RND(4);
9423     boolean waiting_for_player = FALSE;
9424
9425     for (i = 0; i < NUM_DIRECTIONS; i++)
9426     {
9427       int j = (start + i) % 4;
9428       int x = ax + xy[j].x;
9429       int y = ay + xy[j].y;
9430
9431       if (!IN_LEV_FIELD(x, y))
9432         continue;
9433
9434       if (IS_FREE(x, y) ||
9435           CAN_GROW_INTO(Tile[x][y]) ||
9436           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9437           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9438       {
9439         newax = x;
9440         neway = y;
9441         break;
9442       }
9443       else if (IS_PLAYER(x, y))
9444         waiting_for_player = TRUE;
9445     }
9446
9447     if (newax == ax && neway == ay)             // amoeba cannot grow
9448     {
9449       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9450       {
9451         Tile[ax][ay] = EL_AMOEBA_DEAD;
9452         TEST_DrawLevelField(ax, ay);
9453         AmoebaCnt[AmoebaNr[ax][ay]]--;
9454
9455         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9456         {
9457           if (element == EL_AMOEBA_FULL)
9458             AmoebaToDiamond(ax, ay);
9459           else if (element == EL_BD_AMOEBA)
9460             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9461         }
9462       }
9463       return;
9464     }
9465     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9466     {
9467       // amoeba gets larger by growing in some direction
9468
9469       int new_group_nr = AmoebaNr[ax][ay];
9470
9471 #ifdef DEBUG
9472   if (new_group_nr == 0)
9473   {
9474     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9475           newax, neway);
9476     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9477
9478     return;
9479   }
9480 #endif
9481
9482       AmoebaNr[newax][neway] = new_group_nr;
9483       AmoebaCnt[new_group_nr]++;
9484       AmoebaCnt2[new_group_nr]++;
9485
9486       // if amoeba touches other amoeba(s) after growing, unify them
9487       AmoebaMerge(newax, neway);
9488
9489       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9490       {
9491         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9492         return;
9493       }
9494     }
9495   }
9496
9497   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9498       (neway == lev_fieldy - 1 && newax != ax))
9499   {
9500     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9501     Store[newax][neway] = element;
9502   }
9503   else if (neway == ay || element == EL_EMC_DRIPPER)
9504   {
9505     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9506
9507     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9508   }
9509   else
9510   {
9511     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9512     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9513     Store[ax][ay] = EL_AMOEBA_DROP;
9514     ContinueMoving(ax, ay);
9515     return;
9516   }
9517
9518   TEST_DrawLevelField(newax, neway);
9519 }
9520
9521 static void Life(int ax, int ay)
9522 {
9523   int x1, y1, x2, y2;
9524   int life_time = 40;
9525   int element = Tile[ax][ay];
9526   int graphic = el2img(element);
9527   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9528                          level.biomaze);
9529   boolean changed = FALSE;
9530
9531   if (IS_ANIMATED(graphic))
9532     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9533
9534   if (Stop[ax][ay])
9535     return;
9536
9537   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9538     MovDelay[ax][ay] = life_time;
9539
9540   if (MovDelay[ax][ay])         // wait some time before next cycle
9541   {
9542     MovDelay[ax][ay]--;
9543     if (MovDelay[ax][ay])
9544       return;
9545   }
9546
9547   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9548   {
9549     int xx = ax + x1, yy = ay + y1;
9550     int old_element = Tile[xx][yy];
9551     int num_neighbours = 0;
9552
9553     if (!IN_LEV_FIELD(xx, yy))
9554       continue;
9555
9556     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9557     {
9558       int x = xx + x2, y = yy + y2;
9559
9560       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9561         continue;
9562
9563       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9564       boolean is_neighbour = FALSE;
9565
9566       if (level.use_life_bugs)
9567         is_neighbour =
9568           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9569            (IS_FREE(x, y)                             &&  Stop[x][y]));
9570       else
9571         is_neighbour =
9572           (Last[x][y] == element || is_player_cell);
9573
9574       if (is_neighbour)
9575         num_neighbours++;
9576     }
9577
9578     boolean is_free = FALSE;
9579
9580     if (level.use_life_bugs)
9581       is_free = (IS_FREE(xx, yy));
9582     else
9583       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9584
9585     if (xx == ax && yy == ay)           // field in the middle
9586     {
9587       if (num_neighbours < life_parameter[0] ||
9588           num_neighbours > life_parameter[1])
9589       {
9590         Tile[xx][yy] = EL_EMPTY;
9591         if (Tile[xx][yy] != old_element)
9592           TEST_DrawLevelField(xx, yy);
9593         Stop[xx][yy] = TRUE;
9594         changed = TRUE;
9595       }
9596     }
9597     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9598     {                                   // free border field
9599       if (num_neighbours >= life_parameter[2] &&
9600           num_neighbours <= life_parameter[3])
9601       {
9602         Tile[xx][yy] = element;
9603         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9604         if (Tile[xx][yy] != old_element)
9605           TEST_DrawLevelField(xx, yy);
9606         Stop[xx][yy] = TRUE;
9607         changed = TRUE;
9608       }
9609     }
9610   }
9611
9612   if (changed)
9613     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9614                    SND_GAME_OF_LIFE_GROWING);
9615 }
9616
9617 static void InitRobotWheel(int x, int y)
9618 {
9619   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9620 }
9621
9622 static void RunRobotWheel(int x, int y)
9623 {
9624   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9625 }
9626
9627 static void StopRobotWheel(int x, int y)
9628 {
9629   if (game.robot_wheel_x == x &&
9630       game.robot_wheel_y == y)
9631   {
9632     game.robot_wheel_x = -1;
9633     game.robot_wheel_y = -1;
9634     game.robot_wheel_active = FALSE;
9635   }
9636 }
9637
9638 static void InitTimegateWheel(int x, int y)
9639 {
9640   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9641 }
9642
9643 static void RunTimegateWheel(int x, int y)
9644 {
9645   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9646 }
9647
9648 static void InitMagicBallDelay(int x, int y)
9649 {
9650   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9651 }
9652
9653 static void ActivateMagicBall(int bx, int by)
9654 {
9655   int x, y;
9656
9657   if (level.ball_random)
9658   {
9659     int pos_border = RND(8);    // select one of the eight border elements
9660     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9661     int xx = pos_content % 3;
9662     int yy = pos_content / 3;
9663
9664     x = bx - 1 + xx;
9665     y = by - 1 + yy;
9666
9667     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9668       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9669   }
9670   else
9671   {
9672     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9673     {
9674       int xx = x - bx + 1;
9675       int yy = y - by + 1;
9676
9677       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9678         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9679     }
9680   }
9681
9682   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9683 }
9684
9685 static void CheckExit(int x, int y)
9686 {
9687   if (game.gems_still_needed > 0 ||
9688       game.sokoban_fields_still_needed > 0 ||
9689       game.sokoban_objects_still_needed > 0 ||
9690       game.lights_still_needed > 0)
9691   {
9692     int element = Tile[x][y];
9693     int graphic = el2img(element);
9694
9695     if (IS_ANIMATED(graphic))
9696       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9697
9698     return;
9699   }
9700
9701   // do not re-open exit door closed after last player
9702   if (game.all_players_gone)
9703     return;
9704
9705   Tile[x][y] = EL_EXIT_OPENING;
9706
9707   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9708 }
9709
9710 static void CheckExitEM(int x, int y)
9711 {
9712   if (game.gems_still_needed > 0 ||
9713       game.sokoban_fields_still_needed > 0 ||
9714       game.sokoban_objects_still_needed > 0 ||
9715       game.lights_still_needed > 0)
9716   {
9717     int element = Tile[x][y];
9718     int graphic = el2img(element);
9719
9720     if (IS_ANIMATED(graphic))
9721       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9722
9723     return;
9724   }
9725
9726   // do not re-open exit door closed after last player
9727   if (game.all_players_gone)
9728     return;
9729
9730   Tile[x][y] = EL_EM_EXIT_OPENING;
9731
9732   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9733 }
9734
9735 static void CheckExitSteel(int x, int y)
9736 {
9737   if (game.gems_still_needed > 0 ||
9738       game.sokoban_fields_still_needed > 0 ||
9739       game.sokoban_objects_still_needed > 0 ||
9740       game.lights_still_needed > 0)
9741   {
9742     int element = Tile[x][y];
9743     int graphic = el2img(element);
9744
9745     if (IS_ANIMATED(graphic))
9746       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9747
9748     return;
9749   }
9750
9751   // do not re-open exit door closed after last player
9752   if (game.all_players_gone)
9753     return;
9754
9755   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9756
9757   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9758 }
9759
9760 static void CheckExitSteelEM(int x, int y)
9761 {
9762   if (game.gems_still_needed > 0 ||
9763       game.sokoban_fields_still_needed > 0 ||
9764       game.sokoban_objects_still_needed > 0 ||
9765       game.lights_still_needed > 0)
9766   {
9767     int element = Tile[x][y];
9768     int graphic = el2img(element);
9769
9770     if (IS_ANIMATED(graphic))
9771       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9772
9773     return;
9774   }
9775
9776   // do not re-open exit door closed after last player
9777   if (game.all_players_gone)
9778     return;
9779
9780   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9781
9782   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9783 }
9784
9785 static void CheckExitSP(int x, int y)
9786 {
9787   if (game.gems_still_needed > 0)
9788   {
9789     int element = Tile[x][y];
9790     int graphic = el2img(element);
9791
9792     if (IS_ANIMATED(graphic))
9793       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9794
9795     return;
9796   }
9797
9798   // do not re-open exit door closed after last player
9799   if (game.all_players_gone)
9800     return;
9801
9802   Tile[x][y] = EL_SP_EXIT_OPENING;
9803
9804   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9805 }
9806
9807 static void CloseAllOpenTimegates(void)
9808 {
9809   int x, y;
9810
9811   SCAN_PLAYFIELD(x, y)
9812   {
9813     int element = Tile[x][y];
9814
9815     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9816     {
9817       Tile[x][y] = EL_TIMEGATE_CLOSING;
9818
9819       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9820     }
9821   }
9822 }
9823
9824 static void DrawTwinkleOnField(int x, int y)
9825 {
9826   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9827     return;
9828
9829   if (Tile[x][y] == EL_BD_DIAMOND)
9830     return;
9831
9832   if (MovDelay[x][y] == 0)      // next animation frame
9833     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9834
9835   if (MovDelay[x][y] != 0)      // wait some time before next frame
9836   {
9837     MovDelay[x][y]--;
9838
9839     DrawLevelElementAnimation(x, y, Tile[x][y]);
9840
9841     if (MovDelay[x][y] != 0)
9842     {
9843       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9844                                            10 - MovDelay[x][y]);
9845
9846       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9847     }
9848   }
9849 }
9850
9851 static void WallGrowing(int x, int y)
9852 {
9853   int delay = 6;
9854
9855   if (!MovDelay[x][y])          // next animation frame
9856     MovDelay[x][y] = 3 * delay;
9857
9858   if (MovDelay[x][y])           // wait some time before next frame
9859   {
9860     MovDelay[x][y]--;
9861
9862     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9863     {
9864       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9865       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9866
9867       DrawLevelGraphic(x, y, graphic, frame);
9868     }
9869
9870     if (!MovDelay[x][y])
9871     {
9872       if (MovDir[x][y] == MV_LEFT)
9873       {
9874         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9875           TEST_DrawLevelField(x - 1, y);
9876       }
9877       else if (MovDir[x][y] == MV_RIGHT)
9878       {
9879         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9880           TEST_DrawLevelField(x + 1, y);
9881       }
9882       else if (MovDir[x][y] == MV_UP)
9883       {
9884         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9885           TEST_DrawLevelField(x, y - 1);
9886       }
9887       else
9888       {
9889         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9890           TEST_DrawLevelField(x, y + 1);
9891       }
9892
9893       Tile[x][y] = Store[x][y];
9894       Store[x][y] = 0;
9895       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9896       TEST_DrawLevelField(x, y);
9897     }
9898   }
9899 }
9900
9901 static void CheckWallGrowing(int ax, int ay)
9902 {
9903   int element = Tile[ax][ay];
9904   int graphic = el2img(element);
9905   boolean free_top    = FALSE;
9906   boolean free_bottom = FALSE;
9907   boolean free_left   = FALSE;
9908   boolean free_right  = FALSE;
9909   boolean stop_top    = FALSE;
9910   boolean stop_bottom = FALSE;
9911   boolean stop_left   = FALSE;
9912   boolean stop_right  = FALSE;
9913   boolean new_wall    = FALSE;
9914
9915   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9916                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9917                            element == EL_EXPANDABLE_STEELWALL_ANY);
9918
9919   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9920                              element == EL_EXPANDABLE_WALL_ANY ||
9921                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9922                              element == EL_EXPANDABLE_STEELWALL_ANY);
9923
9924   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9925                              element == EL_EXPANDABLE_WALL_ANY ||
9926                              element == EL_EXPANDABLE_WALL ||
9927                              element == EL_BD_EXPANDABLE_WALL ||
9928                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9929                              element == EL_EXPANDABLE_STEELWALL_ANY);
9930
9931   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9932                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9933
9934   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9935                              element == EL_EXPANDABLE_WALL ||
9936                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9937
9938   int wall_growing = (is_steelwall ?
9939                       EL_EXPANDABLE_STEELWALL_GROWING :
9940                       EL_EXPANDABLE_WALL_GROWING);
9941
9942   int gfx_wall_growing_up    = (is_steelwall ?
9943                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9944                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9945   int gfx_wall_growing_down  = (is_steelwall ?
9946                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9947                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9948   int gfx_wall_growing_left  = (is_steelwall ?
9949                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9950                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9951   int gfx_wall_growing_right = (is_steelwall ?
9952                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9953                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9954
9955   if (IS_ANIMATED(graphic))
9956     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9957
9958   if (!MovDelay[ax][ay])        // start building new wall
9959     MovDelay[ax][ay] = 6;
9960
9961   if (MovDelay[ax][ay])         // wait some time before building new wall
9962   {
9963     MovDelay[ax][ay]--;
9964     if (MovDelay[ax][ay])
9965       return;
9966   }
9967
9968   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9969     free_top = TRUE;
9970   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9971     free_bottom = TRUE;
9972   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9973     free_left = TRUE;
9974   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9975     free_right = TRUE;
9976
9977   if (grow_vertical)
9978   {
9979     if (free_top)
9980     {
9981       Tile[ax][ay - 1] = wall_growing;
9982       Store[ax][ay - 1] = element;
9983       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9984
9985       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9986         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9987
9988       new_wall = TRUE;
9989     }
9990
9991     if (free_bottom)
9992     {
9993       Tile[ax][ay + 1] = wall_growing;
9994       Store[ax][ay + 1] = element;
9995       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9996
9997       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9998         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9999
10000       new_wall = TRUE;
10001     }
10002   }
10003
10004   if (grow_horizontal)
10005   {
10006     if (free_left)
10007     {
10008       Tile[ax - 1][ay] = wall_growing;
10009       Store[ax - 1][ay] = element;
10010       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
10011
10012       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
10013         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
10014
10015       new_wall = TRUE;
10016     }
10017
10018     if (free_right)
10019     {
10020       Tile[ax + 1][ay] = wall_growing;
10021       Store[ax + 1][ay] = element;
10022       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
10023
10024       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10025         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10026
10027       new_wall = TRUE;
10028     }
10029   }
10030
10031   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10032     TEST_DrawLevelField(ax, ay);
10033
10034   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10035     stop_top = TRUE;
10036   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10037     stop_bottom = TRUE;
10038   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10039     stop_left = TRUE;
10040   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10041     stop_right = TRUE;
10042
10043   if (((stop_top && stop_bottom) || stop_horizontal) &&
10044       ((stop_left && stop_right) || stop_vertical))
10045     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10046
10047   if (new_wall)
10048     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10049 }
10050
10051 static void CheckForDragon(int x, int y)
10052 {
10053   int i, j;
10054   boolean dragon_found = FALSE;
10055   struct XY *xy = xy_topdown;
10056
10057   for (i = 0; i < NUM_DIRECTIONS; i++)
10058   {
10059     for (j = 0; j < 4; j++)
10060     {
10061       int xx = x + j * xy[i].x;
10062       int yy = y + j * xy[i].y;
10063
10064       if (IN_LEV_FIELD(xx, yy) &&
10065           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10066       {
10067         if (Tile[xx][yy] == EL_DRAGON)
10068           dragon_found = TRUE;
10069       }
10070       else
10071         break;
10072     }
10073   }
10074
10075   if (!dragon_found)
10076   {
10077     for (i = 0; i < NUM_DIRECTIONS; i++)
10078     {
10079       for (j = 0; j < 3; j++)
10080       {
10081         int xx = x + j * xy[i].x;
10082         int yy = y + j * xy[i].y;
10083
10084         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10085         {
10086           Tile[xx][yy] = EL_EMPTY;
10087           TEST_DrawLevelField(xx, yy);
10088         }
10089         else
10090           break;
10091       }
10092     }
10093   }
10094 }
10095
10096 static void InitBuggyBase(int x, int y)
10097 {
10098   int element = Tile[x][y];
10099   int activating_delay = FRAMES_PER_SECOND / 4;
10100
10101   ChangeDelay[x][y] =
10102     (element == EL_SP_BUGGY_BASE ?
10103      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10104      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10105      activating_delay :
10106      element == EL_SP_BUGGY_BASE_ACTIVE ?
10107      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10108 }
10109
10110 static void WarnBuggyBase(int x, int y)
10111 {
10112   int i;
10113   struct XY *xy = xy_topdown;
10114
10115   for (i = 0; i < NUM_DIRECTIONS; i++)
10116   {
10117     int xx = x + xy[i].x;
10118     int yy = y + xy[i].y;
10119
10120     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10121     {
10122       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10123
10124       break;
10125     }
10126   }
10127 }
10128
10129 static void InitTrap(int x, int y)
10130 {
10131   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10132 }
10133
10134 static void ActivateTrap(int x, int y)
10135 {
10136   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10137 }
10138
10139 static void ChangeActiveTrap(int x, int y)
10140 {
10141   int graphic = IMG_TRAP_ACTIVE;
10142
10143   // if new animation frame was drawn, correct crumbled sand border
10144   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10145     TEST_DrawLevelFieldCrumbled(x, y);
10146 }
10147
10148 static int getSpecialActionElement(int element, int number, int base_element)
10149 {
10150   return (element != EL_EMPTY ? element :
10151           number != -1 ? base_element + number - 1 :
10152           EL_EMPTY);
10153 }
10154
10155 static int getModifiedActionNumber(int value_old, int operator, int operand,
10156                                    int value_min, int value_max)
10157 {
10158   int value_new = (operator == CA_MODE_SET      ? operand :
10159                    operator == CA_MODE_ADD      ? value_old + operand :
10160                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10161                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10162                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10163                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10164                    value_old);
10165
10166   return (value_new < value_min ? value_min :
10167           value_new > value_max ? value_max :
10168           value_new);
10169 }
10170
10171 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10172 {
10173   struct ElementInfo *ei = &element_info[element];
10174   struct ElementChangeInfo *change = &ei->change_page[page];
10175   int target_element = change->target_element;
10176   int action_type = change->action_type;
10177   int action_mode = change->action_mode;
10178   int action_arg = change->action_arg;
10179   int action_element = change->action_element;
10180   int i;
10181
10182   if (!change->has_action)
10183     return;
10184
10185   // ---------- determine action paramater values -----------------------------
10186
10187   int level_time_value =
10188     (level.time > 0 ? TimeLeft :
10189      TimePlayed);
10190
10191   int action_arg_element_raw =
10192     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10193      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10194      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10195      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10196      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10197      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10198      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10199      EL_EMPTY);
10200   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10201
10202   int action_arg_direction =
10203     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10204      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10205      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10206      change->actual_trigger_side :
10207      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10208      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10209      MV_NONE);
10210
10211   int action_arg_number_min =
10212     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10213      CA_ARG_MIN);
10214
10215   int action_arg_number_max =
10216     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10217      action_type == CA_SET_LEVEL_GEMS ? 999 :
10218      action_type == CA_SET_LEVEL_TIME ? 9999 :
10219      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10220      action_type == CA_SET_CE_VALUE ? 9999 :
10221      action_type == CA_SET_CE_SCORE ? 9999 :
10222      CA_ARG_MAX);
10223
10224   int action_arg_number_reset =
10225     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10226      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10227      action_type == CA_SET_LEVEL_TIME ? level.time :
10228      action_type == CA_SET_LEVEL_SCORE ? 0 :
10229      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10230      action_type == CA_SET_CE_SCORE ? 0 :
10231      0);
10232
10233   int action_arg_number =
10234     (action_arg <= CA_ARG_MAX ? action_arg :
10235      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10236      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10237      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10238      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10239      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10240      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10241      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10242      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10243      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10244      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10245      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10246      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10247      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10248      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10249      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10250      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10251      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10252      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10253      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10254      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10255      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10256      -1);
10257
10258   int action_arg_number_old =
10259     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10260      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10261      action_type == CA_SET_LEVEL_SCORE ? game.score :
10262      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10263      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10264      0);
10265
10266   int action_arg_number_new =
10267     getModifiedActionNumber(action_arg_number_old,
10268                             action_mode, action_arg_number,
10269                             action_arg_number_min, action_arg_number_max);
10270
10271   int trigger_player_bits =
10272     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10273      change->actual_trigger_player_bits : change->trigger_player);
10274
10275   int action_arg_player_bits =
10276     (action_arg >= CA_ARG_PLAYER_1 &&
10277      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10278      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10279      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10280      PLAYER_BITS_ANY);
10281
10282   // ---------- execute action  -----------------------------------------------
10283
10284   switch (action_type)
10285   {
10286     case CA_NO_ACTION:
10287     {
10288       return;
10289     }
10290
10291     // ---------- level actions  ----------------------------------------------
10292
10293     case CA_RESTART_LEVEL:
10294     {
10295       game.restart_level = TRUE;
10296
10297       break;
10298     }
10299
10300     case CA_SHOW_ENVELOPE:
10301     {
10302       int element = getSpecialActionElement(action_arg_element,
10303                                             action_arg_number, EL_ENVELOPE_1);
10304
10305       if (IS_ENVELOPE(element))
10306         local_player->show_envelope = element;
10307
10308       break;
10309     }
10310
10311     case CA_SET_LEVEL_TIME:
10312     {
10313       if (level.time > 0)       // only modify limited time value
10314       {
10315         TimeLeft = action_arg_number_new;
10316
10317         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10318
10319         DisplayGameControlValues();
10320
10321         if (!TimeLeft && game.time_limit)
10322           for (i = 0; i < MAX_PLAYERS; i++)
10323             KillPlayer(&stored_player[i]);
10324       }
10325
10326       break;
10327     }
10328
10329     case CA_SET_LEVEL_SCORE:
10330     {
10331       game.score = action_arg_number_new;
10332
10333       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10334
10335       DisplayGameControlValues();
10336
10337       break;
10338     }
10339
10340     case CA_SET_LEVEL_GEMS:
10341     {
10342       game.gems_still_needed = action_arg_number_new;
10343
10344       game.snapshot.collected_item = TRUE;
10345
10346       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10347
10348       DisplayGameControlValues();
10349
10350       break;
10351     }
10352
10353     case CA_SET_LEVEL_WIND:
10354     {
10355       game.wind_direction = action_arg_direction;
10356
10357       break;
10358     }
10359
10360     case CA_SET_LEVEL_RANDOM_SEED:
10361     {
10362       // ensure that setting a new random seed while playing is predictable
10363       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10364
10365       break;
10366     }
10367
10368     // ---------- player actions  ---------------------------------------------
10369
10370     case CA_MOVE_PLAYER:
10371     case CA_MOVE_PLAYER_NEW:
10372     {
10373       // automatically move to the next field in specified direction
10374       for (i = 0; i < MAX_PLAYERS; i++)
10375         if (trigger_player_bits & (1 << i))
10376           if (action_type == CA_MOVE_PLAYER ||
10377               stored_player[i].MovPos == 0)
10378             stored_player[i].programmed_action = action_arg_direction;
10379
10380       break;
10381     }
10382
10383     case CA_EXIT_PLAYER:
10384     {
10385       for (i = 0; i < MAX_PLAYERS; i++)
10386         if (action_arg_player_bits & (1 << i))
10387           ExitPlayer(&stored_player[i]);
10388
10389       if (game.players_still_needed == 0)
10390         LevelSolved();
10391
10392       break;
10393     }
10394
10395     case CA_KILL_PLAYER:
10396     {
10397       for (i = 0; i < MAX_PLAYERS; i++)
10398         if (action_arg_player_bits & (1 << i))
10399           KillPlayer(&stored_player[i]);
10400
10401       break;
10402     }
10403
10404     case CA_SET_PLAYER_KEYS:
10405     {
10406       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10407       int element = getSpecialActionElement(action_arg_element,
10408                                             action_arg_number, EL_KEY_1);
10409
10410       if (IS_KEY(element))
10411       {
10412         for (i = 0; i < MAX_PLAYERS; i++)
10413         {
10414           if (trigger_player_bits & (1 << i))
10415           {
10416             stored_player[i].key[KEY_NR(element)] = key_state;
10417
10418             DrawGameDoorValues();
10419           }
10420         }
10421       }
10422
10423       break;
10424     }
10425
10426     case CA_SET_PLAYER_SPEED:
10427     {
10428       for (i = 0; i < MAX_PLAYERS; i++)
10429       {
10430         if (trigger_player_bits & (1 << i))
10431         {
10432           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10433
10434           if (action_arg == CA_ARG_SPEED_FASTER &&
10435               stored_player[i].cannot_move)
10436           {
10437             action_arg_number = STEPSIZE_VERY_SLOW;
10438           }
10439           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10440                    action_arg == CA_ARG_SPEED_FASTER)
10441           {
10442             action_arg_number = 2;
10443             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10444                            CA_MODE_MULTIPLY);
10445           }
10446           else if (action_arg == CA_ARG_NUMBER_RESET)
10447           {
10448             action_arg_number = level.initial_player_stepsize[i];
10449           }
10450
10451           move_stepsize =
10452             getModifiedActionNumber(move_stepsize,
10453                                     action_mode,
10454                                     action_arg_number,
10455                                     action_arg_number_min,
10456                                     action_arg_number_max);
10457
10458           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10459         }
10460       }
10461
10462       break;
10463     }
10464
10465     case CA_SET_PLAYER_SHIELD:
10466     {
10467       for (i = 0; i < MAX_PLAYERS; i++)
10468       {
10469         if (trigger_player_bits & (1 << i))
10470         {
10471           if (action_arg == CA_ARG_SHIELD_OFF)
10472           {
10473             stored_player[i].shield_normal_time_left = 0;
10474             stored_player[i].shield_deadly_time_left = 0;
10475           }
10476           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10477           {
10478             stored_player[i].shield_normal_time_left = 999999;
10479           }
10480           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10481           {
10482             stored_player[i].shield_normal_time_left = 999999;
10483             stored_player[i].shield_deadly_time_left = 999999;
10484           }
10485         }
10486       }
10487
10488       break;
10489     }
10490
10491     case CA_SET_PLAYER_GRAVITY:
10492     {
10493       for (i = 0; i < MAX_PLAYERS; i++)
10494       {
10495         if (trigger_player_bits & (1 << i))
10496         {
10497           stored_player[i].gravity =
10498             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10499              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10500              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10501              stored_player[i].gravity);
10502         }
10503       }
10504
10505       break;
10506     }
10507
10508     case CA_SET_PLAYER_ARTWORK:
10509     {
10510       for (i = 0; i < MAX_PLAYERS; i++)
10511       {
10512         if (trigger_player_bits & (1 << i))
10513         {
10514           int artwork_element = action_arg_element;
10515
10516           if (action_arg == CA_ARG_ELEMENT_RESET)
10517             artwork_element =
10518               (level.use_artwork_element[i] ? level.artwork_element[i] :
10519                stored_player[i].element_nr);
10520
10521           if (stored_player[i].artwork_element != artwork_element)
10522             stored_player[i].Frame = 0;
10523
10524           stored_player[i].artwork_element = artwork_element;
10525
10526           SetPlayerWaiting(&stored_player[i], FALSE);
10527
10528           // set number of special actions for bored and sleeping animation
10529           stored_player[i].num_special_action_bored =
10530             get_num_special_action(artwork_element,
10531                                    ACTION_BORING_1, ACTION_BORING_LAST);
10532           stored_player[i].num_special_action_sleeping =
10533             get_num_special_action(artwork_element,
10534                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10535         }
10536       }
10537
10538       break;
10539     }
10540
10541     case CA_SET_PLAYER_INVENTORY:
10542     {
10543       for (i = 0; i < MAX_PLAYERS; i++)
10544       {
10545         struct PlayerInfo *player = &stored_player[i];
10546         int j, k;
10547
10548         if (trigger_player_bits & (1 << i))
10549         {
10550           int inventory_element = action_arg_element;
10551
10552           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10553               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10554               action_arg == CA_ARG_ELEMENT_ACTION)
10555           {
10556             int element = inventory_element;
10557             int collect_count = element_info[element].collect_count_initial;
10558
10559             if (!IS_CUSTOM_ELEMENT(element))
10560               collect_count = 1;
10561
10562             if (collect_count == 0)
10563               player->inventory_infinite_element = element;
10564             else
10565               for (k = 0; k < collect_count; k++)
10566                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10567                   player->inventory_element[player->inventory_size++] =
10568                     element;
10569           }
10570           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10571                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10572                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10573           {
10574             if (player->inventory_infinite_element != EL_UNDEFINED &&
10575                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10576                                      action_arg_element_raw))
10577               player->inventory_infinite_element = EL_UNDEFINED;
10578
10579             for (k = 0, j = 0; j < player->inventory_size; j++)
10580             {
10581               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10582                                         action_arg_element_raw))
10583                 player->inventory_element[k++] = player->inventory_element[j];
10584             }
10585
10586             player->inventory_size = k;
10587           }
10588           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10589           {
10590             if (player->inventory_size > 0)
10591             {
10592               for (j = 0; j < player->inventory_size - 1; j++)
10593                 player->inventory_element[j] = player->inventory_element[j + 1];
10594
10595               player->inventory_size--;
10596             }
10597           }
10598           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10599           {
10600             if (player->inventory_size > 0)
10601               player->inventory_size--;
10602           }
10603           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10604           {
10605             player->inventory_infinite_element = EL_UNDEFINED;
10606             player->inventory_size = 0;
10607           }
10608           else if (action_arg == CA_ARG_INVENTORY_RESET)
10609           {
10610             player->inventory_infinite_element = EL_UNDEFINED;
10611             player->inventory_size = 0;
10612
10613             if (level.use_initial_inventory[i])
10614             {
10615               for (j = 0; j < level.initial_inventory_size[i]; j++)
10616               {
10617                 int element = level.initial_inventory_content[i][j];
10618                 int collect_count = element_info[element].collect_count_initial;
10619
10620                 if (!IS_CUSTOM_ELEMENT(element))
10621                   collect_count = 1;
10622
10623                 if (collect_count == 0)
10624                   player->inventory_infinite_element = element;
10625                 else
10626                   for (k = 0; k < collect_count; k++)
10627                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10628                       player->inventory_element[player->inventory_size++] =
10629                         element;
10630               }
10631             }
10632           }
10633         }
10634       }
10635
10636       break;
10637     }
10638
10639     // ---------- CE actions  -------------------------------------------------
10640
10641     case CA_SET_CE_VALUE:
10642     {
10643       int last_ce_value = CustomValue[x][y];
10644
10645       CustomValue[x][y] = action_arg_number_new;
10646
10647       if (CustomValue[x][y] != last_ce_value)
10648       {
10649         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10650         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10651
10652         if (CustomValue[x][y] == 0)
10653         {
10654           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10655           ChangeCount[x][y] = 0;        // allow at least one more change
10656
10657           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10658           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10659         }
10660       }
10661
10662       break;
10663     }
10664
10665     case CA_SET_CE_SCORE:
10666     {
10667       int last_ce_score = ei->collect_score;
10668
10669       ei->collect_score = action_arg_number_new;
10670
10671       if (ei->collect_score != last_ce_score)
10672       {
10673         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10674         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10675
10676         if (ei->collect_score == 0)
10677         {
10678           int xx, yy;
10679
10680           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10681           ChangeCount[x][y] = 0;        // allow at least one more change
10682
10683           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10684           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10685
10686           /*
10687             This is a very special case that seems to be a mixture between
10688             CheckElementChange() and CheckTriggeredElementChange(): while
10689             the first one only affects single elements that are triggered
10690             directly, the second one affects multiple elements in the playfield
10691             that are triggered indirectly by another element. This is a third
10692             case: Changing the CE score always affects multiple identical CEs,
10693             so every affected CE must be checked, not only the single CE for
10694             which the CE score was changed in the first place (as every instance
10695             of that CE shares the same CE score, and therefore also can change)!
10696           */
10697           SCAN_PLAYFIELD(xx, yy)
10698           {
10699             if (Tile[xx][yy] == element)
10700               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10701                                  CE_SCORE_GETS_ZERO);
10702           }
10703         }
10704       }
10705
10706       break;
10707     }
10708
10709     case CA_SET_CE_ARTWORK:
10710     {
10711       int artwork_element = action_arg_element;
10712       boolean reset_frame = FALSE;
10713       int xx, yy;
10714
10715       if (action_arg == CA_ARG_ELEMENT_RESET)
10716         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10717                            element);
10718
10719       if (ei->gfx_element != artwork_element)
10720         reset_frame = TRUE;
10721
10722       ei->gfx_element = artwork_element;
10723
10724       SCAN_PLAYFIELD(xx, yy)
10725       {
10726         if (Tile[xx][yy] == element)
10727         {
10728           if (reset_frame)
10729           {
10730             ResetGfxAnimation(xx, yy);
10731             ResetRandomAnimationValue(xx, yy);
10732           }
10733
10734           TEST_DrawLevelField(xx, yy);
10735         }
10736       }
10737
10738       break;
10739     }
10740
10741     // ---------- engine actions  ---------------------------------------------
10742
10743     case CA_SET_ENGINE_SCAN_MODE:
10744     {
10745       InitPlayfieldScanMode(action_arg);
10746
10747       break;
10748     }
10749
10750     default:
10751       break;
10752   }
10753 }
10754
10755 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10756 {
10757   int old_element = Tile[x][y];
10758   int new_element = GetElementFromGroupElement(element);
10759   int previous_move_direction = MovDir[x][y];
10760   int last_ce_value = CustomValue[x][y];
10761   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10762   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10763   boolean add_player_onto_element = (new_element_is_player &&
10764                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10765                                      IS_WALKABLE(old_element));
10766
10767   if (!add_player_onto_element)
10768   {
10769     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10770       RemoveMovingField(x, y);
10771     else
10772       RemoveField(x, y);
10773
10774     Tile[x][y] = new_element;
10775
10776     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10777       MovDir[x][y] = previous_move_direction;
10778
10779     if (element_info[new_element].use_last_ce_value)
10780       CustomValue[x][y] = last_ce_value;
10781
10782     InitField_WithBug1(x, y, FALSE);
10783
10784     new_element = Tile[x][y];   // element may have changed
10785
10786     ResetGfxAnimation(x, y);
10787     ResetRandomAnimationValue(x, y);
10788
10789     TEST_DrawLevelField(x, y);
10790
10791     if (GFX_CRUMBLED(new_element))
10792       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10793
10794     if (old_element == EL_EXPLOSION)
10795     {
10796       Store[x][y] = Store2[x][y] = 0;
10797
10798       // check if new element replaces an exploding player, requiring cleanup
10799       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10800         StorePlayer[x][y] = 0;
10801     }
10802
10803     // check if element under the player changes from accessible to unaccessible
10804     // (needed for special case of dropping element which then changes)
10805     // (must be checked after creating new element for walkable group elements)
10806     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10807         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10808     {
10809       KillPlayer(PLAYERINFO(x, y));
10810
10811       return;
10812     }
10813   }
10814
10815   // "ChangeCount" not set yet to allow "entered by player" change one time
10816   if (new_element_is_player)
10817     RelocatePlayer(x, y, new_element);
10818
10819   if (is_change)
10820     ChangeCount[x][y]++;        // count number of changes in the same frame
10821
10822   TestIfBadThingTouchesPlayer(x, y);
10823   TestIfPlayerTouchesCustomElement(x, y);
10824   TestIfElementTouchesCustomElement(x, y);
10825 }
10826
10827 static void CreateField(int x, int y, int element)
10828 {
10829   CreateFieldExt(x, y, element, FALSE);
10830 }
10831
10832 static void CreateElementFromChange(int x, int y, int element)
10833 {
10834   element = GET_VALID_RUNTIME_ELEMENT(element);
10835
10836   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10837   {
10838     int old_element = Tile[x][y];
10839
10840     // prevent changed element from moving in same engine frame
10841     // unless both old and new element can either fall or move
10842     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10843         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10844       Stop[x][y] = TRUE;
10845   }
10846
10847   CreateFieldExt(x, y, element, TRUE);
10848 }
10849
10850 static boolean ChangeElement(int x, int y, int element, int page)
10851 {
10852   struct ElementInfo *ei = &element_info[element];
10853   struct ElementChangeInfo *change = &ei->change_page[page];
10854   int ce_value = CustomValue[x][y];
10855   int ce_score = ei->collect_score;
10856   int target_element;
10857   int old_element = Tile[x][y];
10858
10859   // always use default change event to prevent running into a loop
10860   if (ChangeEvent[x][y] == -1)
10861     ChangeEvent[x][y] = CE_DELAY;
10862
10863   if (ChangeEvent[x][y] == CE_DELAY)
10864   {
10865     // reset actual trigger element, trigger player and action element
10866     change->actual_trigger_element = EL_EMPTY;
10867     change->actual_trigger_player = EL_EMPTY;
10868     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10869     change->actual_trigger_side = CH_SIDE_NONE;
10870     change->actual_trigger_ce_value = 0;
10871     change->actual_trigger_ce_score = 0;
10872     change->actual_trigger_x = -1;
10873     change->actual_trigger_y = -1;
10874   }
10875
10876   // do not change elements more than a specified maximum number of changes
10877   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10878     return FALSE;
10879
10880   ChangeCount[x][y]++;          // count number of changes in the same frame
10881
10882   if (ei->has_anim_event)
10883     HandleGlobalAnimEventByElementChange(element, page, x, y,
10884                                          change->actual_trigger_x,
10885                                          change->actual_trigger_y);
10886
10887   if (change->explode)
10888   {
10889     Bang(x, y);
10890
10891     return TRUE;
10892   }
10893
10894   if (change->use_target_content)
10895   {
10896     boolean complete_replace = TRUE;
10897     boolean can_replace[3][3];
10898     int xx, yy;
10899
10900     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10901     {
10902       boolean is_empty;
10903       boolean is_walkable;
10904       boolean is_diggable;
10905       boolean is_collectible;
10906       boolean is_removable;
10907       boolean is_destructible;
10908       int ex = x + xx - 1;
10909       int ey = y + yy - 1;
10910       int content_element = change->target_content.e[xx][yy];
10911       int e;
10912
10913       can_replace[xx][yy] = TRUE;
10914
10915       if (ex == x && ey == y)   // do not check changing element itself
10916         continue;
10917
10918       if (content_element == EL_EMPTY_SPACE)
10919       {
10920         can_replace[xx][yy] = FALSE;    // do not replace border with space
10921
10922         continue;
10923       }
10924
10925       if (!IN_LEV_FIELD(ex, ey))
10926       {
10927         can_replace[xx][yy] = FALSE;
10928         complete_replace = FALSE;
10929
10930         continue;
10931       }
10932
10933       e = Tile[ex][ey];
10934
10935       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10936         e = MovingOrBlocked2Element(ex, ey);
10937
10938       is_empty = (IS_FREE(ex, ey) ||
10939                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10940
10941       is_walkable     = (is_empty || IS_WALKABLE(e));
10942       is_diggable     = (is_empty || IS_DIGGABLE(e));
10943       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10944       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10945       is_removable    = (is_diggable || is_collectible);
10946
10947       can_replace[xx][yy] =
10948         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10949           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10950           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10951           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10952           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10953           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10954          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10955
10956       if (!can_replace[xx][yy])
10957         complete_replace = FALSE;
10958     }
10959
10960     if (!change->only_if_complete || complete_replace)
10961     {
10962       boolean something_has_changed = FALSE;
10963
10964       if (change->only_if_complete && change->use_random_replace &&
10965           RND(100) < change->random_percentage)
10966         return FALSE;
10967
10968       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10969       {
10970         int ex = x + xx - 1;
10971         int ey = y + yy - 1;
10972         int content_element;
10973
10974         if (can_replace[xx][yy] && (!change->use_random_replace ||
10975                                     RND(100) < change->random_percentage))
10976         {
10977           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10978             RemoveMovingField(ex, ey);
10979
10980           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10981
10982           content_element = change->target_content.e[xx][yy];
10983           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10984                                               ce_value, ce_score);
10985
10986           CreateElementFromChange(ex, ey, target_element);
10987
10988           something_has_changed = TRUE;
10989
10990           // for symmetry reasons, freeze newly created border elements
10991           if (ex != x || ey != y)
10992             Stop[ex][ey] = TRUE;        // no more moving in this frame
10993         }
10994       }
10995
10996       if (something_has_changed)
10997       {
10998         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10999         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11000       }
11001     }
11002   }
11003   else
11004   {
11005     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11006                                         ce_value, ce_score);
11007
11008     if (element == EL_DIAGONAL_GROWING ||
11009         element == EL_DIAGONAL_SHRINKING)
11010     {
11011       target_element = Store[x][y];
11012
11013       Store[x][y] = EL_EMPTY;
11014     }
11015
11016     // special case: element changes to player (and may be kept if walkable)
11017     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
11018       CreateElementFromChange(x, y, EL_EMPTY);
11019
11020     CreateElementFromChange(x, y, target_element);
11021
11022     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11023     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11024   }
11025
11026   // this uses direct change before indirect change
11027   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11028
11029   return TRUE;
11030 }
11031
11032 static void HandleElementChange(int x, int y, int page)
11033 {
11034   int element = MovingOrBlocked2Element(x, y);
11035   struct ElementInfo *ei = &element_info[element];
11036   struct ElementChangeInfo *change = &ei->change_page[page];
11037   boolean handle_action_before_change = FALSE;
11038
11039 #ifdef DEBUG
11040   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11041       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11042   {
11043     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11044           x, y, element, element_info[element].token_name);
11045     Debug("game:playing:HandleElementChange", "This should never happen!");
11046   }
11047 #endif
11048
11049   // this can happen with classic bombs on walkable, changing elements
11050   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11051   {
11052     return;
11053   }
11054
11055   if (ChangeDelay[x][y] == 0)           // initialize element change
11056   {
11057     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11058
11059     if (change->can_change)
11060     {
11061       // !!! not clear why graphic animation should be reset at all here !!!
11062       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11063       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11064
11065       /*
11066         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11067
11068         When using an animation frame delay of 1 (this only happens with
11069         "sp_zonk.moving.left/right" in the classic graphics), the default
11070         (non-moving) animation shows wrong animation frames (while the
11071         moving animation, like "sp_zonk.moving.left/right", is correct,
11072         so this graphical bug never shows up with the classic graphics).
11073         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11074         be drawn instead of the correct frames 0,1,2,3. This is caused by
11075         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11076         an element change: First when the change delay ("ChangeDelay[][]")
11077         counter has reached zero after decrementing, then a second time in
11078         the next frame (after "GfxFrame[][]" was already incremented) when
11079         "ChangeDelay[][]" is reset to the initial delay value again.
11080
11081         This causes frame 0 to be drawn twice, while the last frame won't
11082         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11083
11084         As some animations may already be cleverly designed around this bug
11085         (at least the "Snake Bite" snake tail animation does this), it cannot
11086         simply be fixed here without breaking such existing animations.
11087         Unfortunately, it cannot easily be detected if a graphics set was
11088         designed "before" or "after" the bug was fixed. As a workaround,
11089         a new graphics set option "game.graphics_engine_version" was added
11090         to be able to specify the game's major release version for which the
11091         graphics set was designed, which can then be used to decide if the
11092         bugfix should be used (version 4 and above) or not (version 3 or
11093         below, or if no version was specified at all, as with old sets).
11094
11095         (The wrong/fixed animation frames can be tested with the test level set
11096         "test_gfxframe" and level "000", which contains a specially prepared
11097         custom element at level position (x/y) == (11/9) which uses the zonk
11098         animation mentioned above. Using "game.graphics_engine_version: 4"
11099         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11100         This can also be seen from the debug output for this test element.)
11101       */
11102
11103       // when a custom element is about to change (for example by change delay),
11104       // do not reset graphic animation when the custom element is moving
11105       if (game.graphics_engine_version < 4 &&
11106           !IS_MOVING(x, y))
11107       {
11108         ResetGfxAnimation(x, y);
11109         ResetRandomAnimationValue(x, y);
11110       }
11111
11112       if (change->pre_change_function)
11113         change->pre_change_function(x, y);
11114     }
11115   }
11116
11117   ChangeDelay[x][y]--;
11118
11119   if (ChangeDelay[x][y] != 0)           // continue element change
11120   {
11121     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11122
11123     // also needed if CE can not change, but has CE delay with CE action
11124     if (IS_ANIMATED(graphic))
11125       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11126
11127     if (change->can_change)
11128     {
11129       if (change->change_function)
11130         change->change_function(x, y);
11131     }
11132   }
11133   else                                  // finish element change
11134   {
11135     if (ChangePage[x][y] != -1)         // remember page from delayed change
11136     {
11137       page = ChangePage[x][y];
11138       ChangePage[x][y] = -1;
11139
11140       change = &ei->change_page[page];
11141     }
11142
11143     if (IS_MOVING(x, y))                // never change a running system ;-)
11144     {
11145       ChangeDelay[x][y] = 1;            // try change after next move step
11146       ChangePage[x][y] = page;          // remember page to use for change
11147
11148       return;
11149     }
11150
11151     // special case: set new level random seed before changing element
11152     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11153       handle_action_before_change = TRUE;
11154
11155     if (change->has_action && handle_action_before_change)
11156       ExecuteCustomElementAction(x, y, element, page);
11157
11158     if (change->can_change)
11159     {
11160       if (ChangeElement(x, y, element, page))
11161       {
11162         if (change->post_change_function)
11163           change->post_change_function(x, y);
11164       }
11165     }
11166
11167     if (change->has_action && !handle_action_before_change)
11168       ExecuteCustomElementAction(x, y, element, page);
11169   }
11170 }
11171
11172 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11173                                               int trigger_element,
11174                                               int trigger_event,
11175                                               int trigger_player,
11176                                               int trigger_side,
11177                                               int trigger_page)
11178 {
11179   boolean change_done_any = FALSE;
11180   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11181   int i;
11182
11183   if (!(trigger_events[trigger_element][trigger_event]))
11184     return FALSE;
11185
11186   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11187
11188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11189   {
11190     int element = EL_CUSTOM_START + i;
11191     boolean change_done = FALSE;
11192     int p;
11193
11194     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11195         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11196       continue;
11197
11198     for (p = 0; p < element_info[element].num_change_pages; p++)
11199     {
11200       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11201
11202       if (change->can_change_or_has_action &&
11203           change->has_event[trigger_event] &&
11204           change->trigger_side & trigger_side &&
11205           change->trigger_player & trigger_player &&
11206           change->trigger_page & trigger_page_bits &&
11207           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11208       {
11209         change->actual_trigger_element = trigger_element;
11210         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11211         change->actual_trigger_player_bits = trigger_player;
11212         change->actual_trigger_side = trigger_side;
11213         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11214         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11215         change->actual_trigger_x = trigger_x;
11216         change->actual_trigger_y = trigger_y;
11217
11218         if ((change->can_change && !change_done) || change->has_action)
11219         {
11220           int x, y;
11221
11222           SCAN_PLAYFIELD(x, y)
11223           {
11224             if (Tile[x][y] == element)
11225             {
11226               if (change->can_change && !change_done)
11227               {
11228                 // if element already changed in this frame, not only prevent
11229                 // another element change (checked in ChangeElement()), but
11230                 // also prevent additional element actions for this element
11231
11232                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11233                     !level.use_action_after_change_bug)
11234                   continue;
11235
11236                 ChangeDelay[x][y] = 1;
11237                 ChangeEvent[x][y] = trigger_event;
11238
11239                 HandleElementChange(x, y, p);
11240               }
11241               else if (change->has_action)
11242               {
11243                 // if element already changed in this frame, not only prevent
11244                 // another element change (checked in ChangeElement()), but
11245                 // also prevent additional element actions for this element
11246
11247                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11248                     !level.use_action_after_change_bug)
11249                   continue;
11250
11251                 ExecuteCustomElementAction(x, y, element, p);
11252                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11253               }
11254             }
11255           }
11256
11257           if (change->can_change)
11258           {
11259             change_done = TRUE;
11260             change_done_any = TRUE;
11261           }
11262         }
11263       }
11264     }
11265   }
11266
11267   RECURSION_LOOP_DETECTION_END();
11268
11269   return change_done_any;
11270 }
11271
11272 static boolean CheckElementChangeExt(int x, int y,
11273                                      int element,
11274                                      int trigger_element,
11275                                      int trigger_event,
11276                                      int trigger_player,
11277                                      int trigger_side)
11278 {
11279   boolean change_done = FALSE;
11280   int p;
11281
11282   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11283       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11284     return FALSE;
11285
11286   if (Tile[x][y] == EL_BLOCKED)
11287   {
11288     Blocked2Moving(x, y, &x, &y);
11289     element = Tile[x][y];
11290   }
11291
11292   // check if element has already changed or is about to change after moving
11293   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11294        Tile[x][y] != element) ||
11295
11296       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11297        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11298         ChangePage[x][y] != -1)))
11299     return FALSE;
11300
11301   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11302
11303   for (p = 0; p < element_info[element].num_change_pages; p++)
11304   {
11305     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11306
11307     /* check trigger element for all events where the element that is checked
11308        for changing interacts with a directly adjacent element -- this is
11309        different to element changes that affect other elements to change on the
11310        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11311     boolean check_trigger_element =
11312       (trigger_event == CE_NEXT_TO_X ||
11313        trigger_event == CE_TOUCHING_X ||
11314        trigger_event == CE_HITTING_X ||
11315        trigger_event == CE_HIT_BY_X ||
11316        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11317
11318     if (change->can_change_or_has_action &&
11319         change->has_event[trigger_event] &&
11320         change->trigger_side & trigger_side &&
11321         change->trigger_player & trigger_player &&
11322         (!check_trigger_element ||
11323          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11324     {
11325       change->actual_trigger_element = trigger_element;
11326       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11327       change->actual_trigger_player_bits = trigger_player;
11328       change->actual_trigger_side = trigger_side;
11329       change->actual_trigger_ce_value = CustomValue[x][y];
11330       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11331       change->actual_trigger_x = x;
11332       change->actual_trigger_y = y;
11333
11334       // special case: trigger element not at (x,y) position for some events
11335       if (check_trigger_element)
11336       {
11337         static struct
11338         {
11339           int dx, dy;
11340         } move_xy[] =
11341           {
11342             {  0,  0 },
11343             { -1,  0 },
11344             { +1,  0 },
11345             {  0,  0 },
11346             {  0, -1 },
11347             {  0,  0 }, { 0, 0 }, { 0, 0 },
11348             {  0, +1 }
11349           };
11350
11351         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11352         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11353
11354         change->actual_trigger_ce_value = CustomValue[xx][yy];
11355         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11356         change->actual_trigger_x = xx;
11357         change->actual_trigger_y = yy;
11358       }
11359
11360       if (change->can_change && !change_done)
11361       {
11362         ChangeDelay[x][y] = 1;
11363         ChangeEvent[x][y] = trigger_event;
11364
11365         HandleElementChange(x, y, p);
11366
11367         change_done = TRUE;
11368       }
11369       else if (change->has_action)
11370       {
11371         ExecuteCustomElementAction(x, y, element, p);
11372         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11373       }
11374     }
11375   }
11376
11377   RECURSION_LOOP_DETECTION_END();
11378
11379   return change_done;
11380 }
11381
11382 static void PlayPlayerSound(struct PlayerInfo *player)
11383 {
11384   int jx = player->jx, jy = player->jy;
11385   int sound_element = player->artwork_element;
11386   int last_action = player->last_action_waiting;
11387   int action = player->action_waiting;
11388
11389   if (player->is_waiting)
11390   {
11391     if (action != last_action)
11392       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11393     else
11394       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11395   }
11396   else
11397   {
11398     if (action != last_action)
11399       StopSound(element_info[sound_element].sound[last_action]);
11400
11401     if (last_action == ACTION_SLEEPING)
11402       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11403   }
11404 }
11405
11406 static void PlayAllPlayersSound(void)
11407 {
11408   int i;
11409
11410   for (i = 0; i < MAX_PLAYERS; i++)
11411     if (stored_player[i].active)
11412       PlayPlayerSound(&stored_player[i]);
11413 }
11414
11415 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11416 {
11417   boolean last_waiting = player->is_waiting;
11418   int move_dir = player->MovDir;
11419
11420   player->dir_waiting = move_dir;
11421   player->last_action_waiting = player->action_waiting;
11422
11423   if (is_waiting)
11424   {
11425     if (!last_waiting)          // not waiting -> waiting
11426     {
11427       player->is_waiting = TRUE;
11428
11429       player->frame_counter_bored =
11430         FrameCounter +
11431         game.player_boring_delay_fixed +
11432         GetSimpleRandom(game.player_boring_delay_random);
11433       player->frame_counter_sleeping =
11434         FrameCounter +
11435         game.player_sleeping_delay_fixed +
11436         GetSimpleRandom(game.player_sleeping_delay_random);
11437
11438       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11439     }
11440
11441     if (game.player_sleeping_delay_fixed +
11442         game.player_sleeping_delay_random > 0 &&
11443         player->anim_delay_counter == 0 &&
11444         player->post_delay_counter == 0 &&
11445         FrameCounter >= player->frame_counter_sleeping)
11446       player->is_sleeping = TRUE;
11447     else if (game.player_boring_delay_fixed +
11448              game.player_boring_delay_random > 0 &&
11449              FrameCounter >= player->frame_counter_bored)
11450       player->is_bored = TRUE;
11451
11452     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11453                               player->is_bored ? ACTION_BORING :
11454                               ACTION_WAITING);
11455
11456     if (player->is_sleeping && player->use_murphy)
11457     {
11458       // special case for sleeping Murphy when leaning against non-free tile
11459
11460       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11461           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11462            !IS_MOVING(player->jx - 1, player->jy)))
11463         move_dir = MV_LEFT;
11464       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11465                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11466                 !IS_MOVING(player->jx + 1, player->jy)))
11467         move_dir = MV_RIGHT;
11468       else
11469         player->is_sleeping = FALSE;
11470
11471       player->dir_waiting = move_dir;
11472     }
11473
11474     if (player->is_sleeping)
11475     {
11476       if (player->num_special_action_sleeping > 0)
11477       {
11478         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11479         {
11480           int last_special_action = player->special_action_sleeping;
11481           int num_special_action = player->num_special_action_sleeping;
11482           int special_action =
11483             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11484              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11485              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11486              last_special_action + 1 : ACTION_SLEEPING);
11487           int special_graphic =
11488             el_act_dir2img(player->artwork_element, special_action, move_dir);
11489
11490           player->anim_delay_counter =
11491             graphic_info[special_graphic].anim_delay_fixed +
11492             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11493           player->post_delay_counter =
11494             graphic_info[special_graphic].post_delay_fixed +
11495             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11496
11497           player->special_action_sleeping = special_action;
11498         }
11499
11500         if (player->anim_delay_counter > 0)
11501         {
11502           player->action_waiting = player->special_action_sleeping;
11503           player->anim_delay_counter--;
11504         }
11505         else if (player->post_delay_counter > 0)
11506         {
11507           player->post_delay_counter--;
11508         }
11509       }
11510     }
11511     else if (player->is_bored)
11512     {
11513       if (player->num_special_action_bored > 0)
11514       {
11515         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11516         {
11517           int special_action =
11518             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11519           int special_graphic =
11520             el_act_dir2img(player->artwork_element, special_action, move_dir);
11521
11522           player->anim_delay_counter =
11523             graphic_info[special_graphic].anim_delay_fixed +
11524             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11525           player->post_delay_counter =
11526             graphic_info[special_graphic].post_delay_fixed +
11527             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11528
11529           player->special_action_bored = special_action;
11530         }
11531
11532         if (player->anim_delay_counter > 0)
11533         {
11534           player->action_waiting = player->special_action_bored;
11535           player->anim_delay_counter--;
11536         }
11537         else if (player->post_delay_counter > 0)
11538         {
11539           player->post_delay_counter--;
11540         }
11541       }
11542     }
11543   }
11544   else if (last_waiting)        // waiting -> not waiting
11545   {
11546     player->is_waiting = FALSE;
11547     player->is_bored = FALSE;
11548     player->is_sleeping = FALSE;
11549
11550     player->frame_counter_bored = -1;
11551     player->frame_counter_sleeping = -1;
11552
11553     player->anim_delay_counter = 0;
11554     player->post_delay_counter = 0;
11555
11556     player->dir_waiting = player->MovDir;
11557     player->action_waiting = ACTION_DEFAULT;
11558
11559     player->special_action_bored = ACTION_DEFAULT;
11560     player->special_action_sleeping = ACTION_DEFAULT;
11561   }
11562 }
11563
11564 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11565 {
11566   if ((!player->is_moving  && player->was_moving) ||
11567       (player->MovPos == 0 && player->was_moving) ||
11568       (player->is_snapping && !player->was_snapping) ||
11569       (player->is_dropping && !player->was_dropping))
11570   {
11571     if (!CheckSaveEngineSnapshotToList())
11572       return;
11573
11574     player->was_moving = FALSE;
11575     player->was_snapping = TRUE;
11576     player->was_dropping = TRUE;
11577   }
11578   else
11579   {
11580     if (player->is_moving)
11581       player->was_moving = TRUE;
11582
11583     if (!player->is_snapping)
11584       player->was_snapping = FALSE;
11585
11586     if (!player->is_dropping)
11587       player->was_dropping = FALSE;
11588   }
11589
11590   static struct MouseActionInfo mouse_action_last = { 0 };
11591   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11592   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11593
11594   if (new_released)
11595     CheckSaveEngineSnapshotToList();
11596
11597   mouse_action_last = mouse_action;
11598 }
11599
11600 static void CheckSingleStepMode(struct PlayerInfo *player)
11601 {
11602   if (tape.single_step && tape.recording && !tape.pausing)
11603   {
11604     // as it is called "single step mode", just return to pause mode when the
11605     // player stopped moving after one tile (or never starts moving at all)
11606     // (reverse logic needed here in case single step mode used in team mode)
11607     if (player->is_moving ||
11608         player->is_pushing ||
11609         player->is_dropping_pressed ||
11610         player->effective_mouse_action.button)
11611       game.enter_single_step_mode = FALSE;
11612   }
11613
11614   CheckSaveEngineSnapshot(player);
11615 }
11616
11617 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11618 {
11619   int left      = player_action & JOY_LEFT;
11620   int right     = player_action & JOY_RIGHT;
11621   int up        = player_action & JOY_UP;
11622   int down      = player_action & JOY_DOWN;
11623   int button1   = player_action & JOY_BUTTON_1;
11624   int button2   = player_action & JOY_BUTTON_2;
11625   int dx        = (left ? -1 : right ? 1 : 0);
11626   int dy        = (up   ? -1 : down  ? 1 : 0);
11627
11628   if (!player->active || tape.pausing)
11629     return 0;
11630
11631   if (player_action)
11632   {
11633     if (button1)
11634       SnapField(player, dx, dy);
11635     else
11636     {
11637       if (button2)
11638         DropElement(player);
11639
11640       MovePlayer(player, dx, dy);
11641     }
11642
11643     CheckSingleStepMode(player);
11644
11645     SetPlayerWaiting(player, FALSE);
11646
11647     return player_action;
11648   }
11649   else
11650   {
11651     // no actions for this player (no input at player's configured device)
11652
11653     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11654     SnapField(player, 0, 0);
11655     CheckGravityMovementWhenNotMoving(player);
11656
11657     if (player->MovPos == 0)
11658       SetPlayerWaiting(player, TRUE);
11659
11660     if (player->MovPos == 0)    // needed for tape.playing
11661       player->is_moving = FALSE;
11662
11663     player->is_dropping = FALSE;
11664     player->is_dropping_pressed = FALSE;
11665     player->drop_pressed_delay = 0;
11666
11667     CheckSingleStepMode(player);
11668
11669     return 0;
11670   }
11671 }
11672
11673 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11674                                          byte *tape_action)
11675 {
11676   if (!tape.use_mouse_actions)
11677     return;
11678
11679   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11680   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11681   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11682 }
11683
11684 static void SetTapeActionFromMouseAction(byte *tape_action,
11685                                          struct MouseActionInfo *mouse_action)
11686 {
11687   if (!tape.use_mouse_actions)
11688     return;
11689
11690   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11691   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11692   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11693 }
11694
11695 static void CheckLevelSolved(void)
11696 {
11697   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11698   {
11699     if (game_bd.level_solved &&
11700         !game_bd.game_over)                             // game won
11701     {
11702       LevelSolved();
11703
11704       game_bd.game_over = TRUE;
11705
11706       game.all_players_gone = TRUE;
11707     }
11708
11709     if (game_bd.game_over)                              // game lost
11710       game.all_players_gone = TRUE;
11711   }
11712   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11713   {
11714     if (game_em.level_solved &&
11715         !game_em.game_over)                             // game won
11716     {
11717       LevelSolved();
11718
11719       game_em.game_over = TRUE;
11720
11721       game.all_players_gone = TRUE;
11722     }
11723
11724     if (game_em.game_over)                              // game lost
11725       game.all_players_gone = TRUE;
11726   }
11727   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11728   {
11729     if (game_sp.level_solved &&
11730         !game_sp.game_over)                             // game won
11731     {
11732       LevelSolved();
11733
11734       game_sp.game_over = TRUE;
11735
11736       game.all_players_gone = TRUE;
11737     }
11738
11739     if (game_sp.game_over)                              // game lost
11740       game.all_players_gone = TRUE;
11741   }
11742   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11743   {
11744     if (game_mm.level_solved &&
11745         !game_mm.game_over)                             // game won
11746     {
11747       LevelSolved();
11748
11749       game_mm.game_over = TRUE;
11750
11751       game.all_players_gone = TRUE;
11752     }
11753
11754     if (game_mm.game_over)                              // game lost
11755       game.all_players_gone = TRUE;
11756   }
11757 }
11758
11759 static void PlayTimeoutSound(int seconds_left)
11760 {
11761   // will be played directly by BD engine (for classic bonus time sounds)
11762   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11763     return;
11764
11765   // try to use individual "running out of time" sound for each second left
11766   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11767
11768   // if special sound per second not defined, use default sound
11769   if (getSoundInfoEntryFilename(sound) == NULL)
11770     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11771
11772   // if out of time, but player still alive, play special "timeout" sound, if defined
11773   if (seconds_left == 0 && !checkGameFailed())
11774     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11775       sound = SND_GAME_TIMEOUT;
11776
11777   PlaySound(sound);
11778 }
11779
11780 static void CheckLevelTime_StepCounter(void)
11781 {
11782   int i;
11783
11784   TimePlayed++;
11785
11786   if (TimeLeft > 0)
11787   {
11788     TimeLeft--;
11789
11790     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11791       PlayTimeoutSound(TimeLeft);
11792
11793     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11794
11795     DisplayGameControlValues();
11796
11797     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11798       for (i = 0; i < MAX_PLAYERS; i++)
11799         KillPlayer(&stored_player[i]);
11800   }
11801   else if (game.no_level_time_limit && !game.all_players_gone)
11802   {
11803     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11804
11805     DisplayGameControlValues();
11806   }
11807 }
11808
11809 static void CheckLevelTime(void)
11810 {
11811   int frames_per_second = FRAMES_PER_SECOND;
11812   int i;
11813
11814   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11815   {
11816     // level time may be running slower in native BD engine
11817     frames_per_second = getFramesPerSecond_BD();
11818
11819     // if native engine time changed, force main engine time change
11820     if (getTimeLeft_BD() < TimeLeft)
11821       TimeFrames = frames_per_second;
11822
11823     // if last second running, wait for native engine time to exactly reach zero
11824     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11825       TimeFrames = frames_per_second - 1;
11826   }
11827
11828   if (TimeFrames >= frames_per_second)
11829   {
11830     TimeFrames = 0;
11831
11832     for (i = 0; i < MAX_PLAYERS; i++)
11833     {
11834       struct PlayerInfo *player = &stored_player[i];
11835
11836       if (SHIELD_ON(player))
11837       {
11838         player->shield_normal_time_left--;
11839
11840         if (player->shield_deadly_time_left > 0)
11841           player->shield_deadly_time_left--;
11842       }
11843     }
11844
11845     if (!game.LevelSolved && !level.use_step_counter)
11846     {
11847       TimePlayed++;
11848
11849       if (TimeLeft > 0)
11850       {
11851         TimeLeft--;
11852
11853         if (TimeLeft <= 10 && game.time_limit)
11854           PlayTimeoutSound(TimeLeft);
11855
11856         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11857            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11858
11859         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11860
11861         if (!TimeLeft && game.time_limit)
11862         {
11863           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11864           {
11865             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11866               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11867           }
11868           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11869           {
11870             game_em.lev->killed_out_of_time = TRUE;
11871           }
11872           else
11873           {
11874             for (i = 0; i < MAX_PLAYERS; i++)
11875               KillPlayer(&stored_player[i]);
11876           }
11877         }
11878       }
11879       else if (game.no_level_time_limit && !game.all_players_gone)
11880       {
11881         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11882       }
11883
11884       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11885     }
11886   }
11887
11888   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11889   {
11890     TapeTimeFrames = 0;
11891     TapeTime++;
11892
11893     if (tape.recording || tape.playing)
11894       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11895   }
11896
11897   if (tape.recording || tape.playing)
11898     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11899
11900   UpdateAndDisplayGameControlValues();
11901 }
11902
11903 void AdvanceFrameAndPlayerCounters(int player_nr)
11904 {
11905   int i;
11906
11907   // handle game and tape time differently for native BD game engine
11908
11909   // tape time is running in native BD engine even if player is not hatched yet
11910   if (!checkGameRunning())
11911     return;
11912
11913   // advance frame counters (global frame counter and tape time frame counter)
11914   FrameCounter++;
11915   TapeTimeFrames++;
11916
11917   // level time is running in native BD engine after player is being hatched
11918   if (!checkGamePlaying())
11919     return;
11920
11921   // advance time frame counter (used to control available time to solve level)
11922   TimeFrames++;
11923
11924   // advance player counters (counters for move delay, move animation etc.)
11925   for (i = 0; i < MAX_PLAYERS; i++)
11926   {
11927     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11928     int move_delay_value = stored_player[i].move_delay_value;
11929     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11930
11931     if (!advance_player_counters)       // not all players may be affected
11932       continue;
11933
11934     if (move_frames == 0)       // less than one move per game frame
11935     {
11936       int stepsize = TILEX / move_delay_value;
11937       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11938       int count = (stored_player[i].is_moving ?
11939                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11940
11941       if (count % delay == 0)
11942         move_frames = 1;
11943     }
11944
11945     stored_player[i].Frame += move_frames;
11946
11947     if (stored_player[i].MovPos != 0)
11948       stored_player[i].StepFrame += move_frames;
11949
11950     if (stored_player[i].move_delay > 0)
11951       stored_player[i].move_delay--;
11952
11953     // due to bugs in previous versions, counter must count up, not down
11954     if (stored_player[i].push_delay != -1)
11955       stored_player[i].push_delay++;
11956
11957     if (stored_player[i].drop_delay > 0)
11958       stored_player[i].drop_delay--;
11959
11960     if (stored_player[i].is_dropping_pressed)
11961       stored_player[i].drop_pressed_delay++;
11962   }
11963 }
11964
11965 void AdvanceFrameCounter(void)
11966 {
11967   FrameCounter++;
11968 }
11969
11970 void AdvanceGfxFrame(void)
11971 {
11972   int x, y;
11973
11974   SCAN_PLAYFIELD(x, y)
11975   {
11976     GfxFrame[x][y]++;
11977   }
11978 }
11979
11980 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11981                               struct MouseActionInfo *mouse_action_last)
11982 {
11983   if (mouse_action->button)
11984   {
11985     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11986     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11987     int x = mouse_action->lx;
11988     int y = mouse_action->ly;
11989     int element = Tile[x][y];
11990
11991     if (new_button)
11992     {
11993       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11994       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11995                                          ch_button);
11996     }
11997
11998     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11999     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12000                                        ch_button);
12001
12002     if (level.use_step_counter)
12003     {
12004       boolean counted_click = FALSE;
12005
12006       // element clicked that can change when clicked/pressed
12007       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12008           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12009            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12010         counted_click = TRUE;
12011
12012       // element clicked that can trigger change when clicked/pressed
12013       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12014           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12015         counted_click = TRUE;
12016
12017       if (new_button && counted_click)
12018         CheckLevelTime_StepCounter();
12019     }
12020   }
12021 }
12022
12023 void StartGameActions(boolean init_network_game, boolean record_tape,
12024                       int random_seed)
12025 {
12026   unsigned int new_random_seed = InitRND(random_seed);
12027
12028   if (record_tape)
12029     TapeStartRecording(new_random_seed);
12030
12031   if (setup.auto_pause_on_start && !tape.pausing)
12032     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12033
12034   if (init_network_game)
12035   {
12036     SendToServer_LevelFile();
12037     SendToServer_StartPlaying();
12038
12039     return;
12040   }
12041
12042   InitGame();
12043 }
12044
12045 static void GameActionsExt(void)
12046 {
12047 #if 0
12048   static unsigned int game_frame_delay = 0;
12049 #endif
12050   unsigned int game_frame_delay_value;
12051   byte *recorded_player_action;
12052   byte summarized_player_action = 0;
12053   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12054   int i;
12055
12056   // detect endless loops, caused by custom element programming
12057   if (recursion_loop_detected && recursion_loop_depth == 0)
12058   {
12059     char *message = getStringCat3("Internal Error! Element ",
12060                                   EL_NAME(recursion_loop_element),
12061                                   " caused endless loop! Quit the game?");
12062
12063     Warn("element '%s' caused endless loop in game engine",
12064          EL_NAME(recursion_loop_element));
12065
12066     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12067
12068     recursion_loop_detected = FALSE;    // if game should be continued
12069
12070     free(message);
12071
12072     return;
12073   }
12074
12075   if (game.restart_level)
12076     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12077
12078   CheckLevelSolved();
12079
12080   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12081     GameWon();
12082
12083   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12084     TapeStop();
12085
12086   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12087     return;
12088
12089   game_frame_delay_value =
12090     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12091
12092   if (tape.playing && tape.warp_forward && !tape.pausing)
12093     game_frame_delay_value = 0;
12094
12095   SetVideoFrameDelay(game_frame_delay_value);
12096
12097   // (de)activate virtual buttons depending on current game status
12098   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12099   {
12100     if (game.all_players_gone)  // if no players there to be controlled anymore
12101       SetOverlayActive(FALSE);
12102     else if (!tape.playing)     // if game continues after tape stopped playing
12103       SetOverlayActive(TRUE);
12104   }
12105
12106 #if 0
12107 #if 0
12108   // ---------- main game synchronization point ----------
12109
12110   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12111
12112   Debug("game:playing:skip", "skip == %d", skip);
12113
12114 #else
12115   // ---------- main game synchronization point ----------
12116
12117   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12118 #endif
12119 #endif
12120
12121   if (network_playing && !network_player_action_received)
12122   {
12123     // try to get network player actions in time
12124
12125     // last chance to get network player actions without main loop delay
12126     HandleNetworking();
12127
12128     // game was quit by network peer
12129     if (game_status != GAME_MODE_PLAYING)
12130       return;
12131
12132     // check if network player actions still missing and game still running
12133     if (!network_player_action_received && !checkGameEnded())
12134       return;           // failed to get network player actions in time
12135
12136     // do not yet reset "network_player_action_received" (for tape.pausing)
12137   }
12138
12139   if (tape.pausing)
12140     return;
12141
12142   // at this point we know that we really continue executing the game
12143
12144   network_player_action_received = FALSE;
12145
12146   // when playing tape, read previously recorded player input from tape data
12147   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12148
12149   local_player->effective_mouse_action = local_player->mouse_action;
12150
12151   if (recorded_player_action != NULL)
12152     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12153                                  recorded_player_action);
12154
12155   // TapePlayAction() may return NULL when toggling to "pause before death"
12156   if (tape.pausing)
12157     return;
12158
12159   if (tape.set_centered_player)
12160   {
12161     game.centered_player_nr_next = tape.centered_player_nr_next;
12162     game.set_centered_player = TRUE;
12163   }
12164
12165   for (i = 0; i < MAX_PLAYERS; i++)
12166   {
12167     summarized_player_action |= stored_player[i].action;
12168
12169     if (!network_playing && (game.team_mode || tape.playing))
12170       stored_player[i].effective_action = stored_player[i].action;
12171   }
12172
12173   if (network_playing && !checkGameEnded())
12174     SendToServer_MovePlayer(summarized_player_action);
12175
12176   // summarize all actions at local players mapped input device position
12177   // (this allows using different input devices in single player mode)
12178   if (!network.enabled && !game.team_mode)
12179     stored_player[map_player_action[local_player->index_nr]].effective_action =
12180       summarized_player_action;
12181
12182   // summarize all actions at centered player in local team mode
12183   if (tape.recording &&
12184       setup.team_mode && !network.enabled &&
12185       setup.input_on_focus &&
12186       game.centered_player_nr != -1)
12187   {
12188     for (i = 0; i < MAX_PLAYERS; i++)
12189       stored_player[map_player_action[i]].effective_action =
12190         (i == game.centered_player_nr ? summarized_player_action : 0);
12191   }
12192
12193   if (recorded_player_action != NULL)
12194     for (i = 0; i < MAX_PLAYERS; i++)
12195       stored_player[i].effective_action = recorded_player_action[i];
12196
12197   for (i = 0; i < MAX_PLAYERS; i++)
12198   {
12199     tape_action[i] = stored_player[i].effective_action;
12200
12201     /* (this may happen in the RND game engine if a player was not present on
12202        the playfield on level start, but appeared later from a custom element */
12203     if (setup.team_mode &&
12204         tape.recording &&
12205         tape_action[i] &&
12206         !tape.player_participates[i])
12207       tape.player_participates[i] = TRUE;
12208   }
12209
12210   SetTapeActionFromMouseAction(tape_action,
12211                                &local_player->effective_mouse_action);
12212
12213   // only record actions from input devices, but not programmed actions
12214   if (tape.recording)
12215     TapeRecordAction(tape_action);
12216
12217   // remember if game was played (especially after tape stopped playing)
12218   if (!tape.playing && summarized_player_action && !checkGameFailed())
12219     game.GamePlayed = TRUE;
12220
12221 #if USE_NEW_PLAYER_ASSIGNMENTS
12222   // !!! also map player actions in single player mode !!!
12223   // if (game.team_mode)
12224   if (1)
12225   {
12226     byte mapped_action[MAX_PLAYERS];
12227
12228 #if DEBUG_PLAYER_ACTIONS
12229     for (i = 0; i < MAX_PLAYERS; i++)
12230       DebugContinued("", "%d, ", stored_player[i].effective_action);
12231 #endif
12232
12233     for (i = 0; i < MAX_PLAYERS; i++)
12234       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12235
12236     for (i = 0; i < MAX_PLAYERS; i++)
12237       stored_player[i].effective_action = mapped_action[i];
12238
12239 #if DEBUG_PLAYER_ACTIONS
12240     DebugContinued("", "=> ");
12241     for (i = 0; i < MAX_PLAYERS; i++)
12242       DebugContinued("", "%d, ", stored_player[i].effective_action);
12243     DebugContinued("game:playing:player", "\n");
12244 #endif
12245   }
12246 #if DEBUG_PLAYER_ACTIONS
12247   else
12248   {
12249     for (i = 0; i < MAX_PLAYERS; i++)
12250       DebugContinued("", "%d, ", stored_player[i].effective_action);
12251     DebugContinued("game:playing:player", "\n");
12252   }
12253 #endif
12254 #endif
12255
12256   for (i = 0; i < MAX_PLAYERS; i++)
12257   {
12258     // allow engine snapshot in case of changed movement attempt
12259     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12260         (stored_player[i].effective_action & KEY_MOTION))
12261       game.snapshot.changed_action = TRUE;
12262
12263     // allow engine snapshot in case of snapping/dropping attempt
12264     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12265         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12266       game.snapshot.changed_action = TRUE;
12267
12268     game.snapshot.last_action[i] = stored_player[i].effective_action;
12269   }
12270
12271   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12272   {
12273     GameActions_BD_Main();
12274   }
12275   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12276   {
12277     GameActions_EM_Main();
12278   }
12279   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12280   {
12281     GameActions_SP_Main();
12282   }
12283   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12284   {
12285     GameActions_MM_Main();
12286   }
12287   else
12288   {
12289     GameActions_RND_Main();
12290   }
12291
12292   BlitScreenToBitmap(backbuffer);
12293
12294   CheckLevelSolved();
12295   CheckLevelTime();
12296
12297   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12298
12299   if (global.show_frames_per_second)
12300   {
12301     static unsigned int fps_counter = 0;
12302     static int fps_frames = 0;
12303     unsigned int fps_delay_ms = Counter() - fps_counter;
12304
12305     fps_frames++;
12306
12307     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12308     {
12309       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12310
12311       fps_frames = 0;
12312       fps_counter = Counter();
12313
12314       // always draw FPS to screen after FPS value was updated
12315       redraw_mask |= REDRAW_FPS;
12316     }
12317
12318     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12319     if (GetDrawDeactivationMask() == REDRAW_NONE)
12320       redraw_mask |= REDRAW_FPS;
12321   }
12322 }
12323
12324 static void GameActions_CheckSaveEngineSnapshot(void)
12325 {
12326   if (!game.snapshot.save_snapshot)
12327     return;
12328
12329   // clear flag for saving snapshot _before_ saving snapshot
12330   game.snapshot.save_snapshot = FALSE;
12331
12332   SaveEngineSnapshotToList();
12333 }
12334
12335 void GameActions(void)
12336 {
12337   GameActionsExt();
12338
12339   GameActions_CheckSaveEngineSnapshot();
12340 }
12341
12342 void GameActions_BD_Main(void)
12343 {
12344   byte effective_action[MAX_PLAYERS];
12345   int i;
12346
12347   for (i = 0; i < MAX_PLAYERS; i++)
12348     effective_action[i] = stored_player[i].effective_action;
12349
12350   GameActions_BD(effective_action);
12351 }
12352
12353 void GameActions_EM_Main(void)
12354 {
12355   byte effective_action[MAX_PLAYERS];
12356   int i;
12357
12358   for (i = 0; i < MAX_PLAYERS; i++)
12359     effective_action[i] = stored_player[i].effective_action;
12360
12361   GameActions_EM(effective_action);
12362 }
12363
12364 void GameActions_SP_Main(void)
12365 {
12366   byte effective_action[MAX_PLAYERS];
12367   int i;
12368
12369   for (i = 0; i < MAX_PLAYERS; i++)
12370     effective_action[i] = stored_player[i].effective_action;
12371
12372   GameActions_SP(effective_action);
12373
12374   for (i = 0; i < MAX_PLAYERS; i++)
12375   {
12376     if (stored_player[i].force_dropping)
12377       stored_player[i].action |= KEY_BUTTON_DROP;
12378
12379     stored_player[i].force_dropping = FALSE;
12380   }
12381 }
12382
12383 void GameActions_MM_Main(void)
12384 {
12385   AdvanceGfxFrame();
12386
12387   GameActions_MM(local_player->effective_mouse_action);
12388 }
12389
12390 void GameActions_RND_Main(void)
12391 {
12392   GameActions_RND();
12393 }
12394
12395 void GameActions_RND(void)
12396 {
12397   static struct MouseActionInfo mouse_action_last = { 0 };
12398   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12399   int magic_wall_x = 0, magic_wall_y = 0;
12400   int i, x, y, element, graphic, last_gfx_frame;
12401
12402   InitPlayfieldScanModeVars();
12403
12404   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12405   {
12406     SCAN_PLAYFIELD(x, y)
12407     {
12408       ChangeCount[x][y] = 0;
12409       ChangeEvent[x][y] = -1;
12410     }
12411   }
12412
12413   if (game.set_centered_player)
12414   {
12415     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12416
12417     // switching to "all players" only possible if all players fit to screen
12418     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12419     {
12420       game.centered_player_nr_next = game.centered_player_nr;
12421       game.set_centered_player = FALSE;
12422     }
12423
12424     // do not switch focus to non-existing (or non-active) player
12425     if (game.centered_player_nr_next >= 0 &&
12426         !stored_player[game.centered_player_nr_next].active)
12427     {
12428       game.centered_player_nr_next = game.centered_player_nr;
12429       game.set_centered_player = FALSE;
12430     }
12431   }
12432
12433   if (game.set_centered_player &&
12434       ScreenMovPos == 0)        // screen currently aligned at tile position
12435   {
12436     int sx, sy;
12437
12438     if (game.centered_player_nr_next == -1)
12439     {
12440       setScreenCenteredToAllPlayers(&sx, &sy);
12441     }
12442     else
12443     {
12444       sx = stored_player[game.centered_player_nr_next].jx;
12445       sy = stored_player[game.centered_player_nr_next].jy;
12446     }
12447
12448     game.centered_player_nr = game.centered_player_nr_next;
12449     game.set_centered_player = FALSE;
12450
12451     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12452     DrawGameDoorValues();
12453   }
12454
12455   // check single step mode (set flag and clear again if any player is active)
12456   game.enter_single_step_mode =
12457     (tape.single_step && tape.recording && !tape.pausing);
12458
12459   for (i = 0; i < MAX_PLAYERS; i++)
12460   {
12461     int actual_player_action = stored_player[i].effective_action;
12462
12463 #if 1
12464     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12465        - rnd_equinox_tetrachloride 048
12466        - rnd_equinox_tetrachloride_ii 096
12467        - rnd_emanuel_schmieg 002
12468        - doctor_sloan_ww 001, 020
12469     */
12470     if (stored_player[i].MovPos == 0)
12471       CheckGravityMovement(&stored_player[i]);
12472 #endif
12473
12474     // overwrite programmed action with tape action
12475     if (stored_player[i].programmed_action)
12476       actual_player_action = stored_player[i].programmed_action;
12477
12478     PlayerActions(&stored_player[i], actual_player_action);
12479
12480     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12481   }
12482
12483   // single step pause mode may already have been toggled by "ScrollPlayer()"
12484   if (game.enter_single_step_mode && !tape.pausing)
12485     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12486
12487   ScrollScreen(NULL, SCROLL_GO_ON);
12488
12489   /* for backwards compatibility, the following code emulates a fixed bug that
12490      occured when pushing elements (causing elements that just made their last
12491      pushing step to already (if possible) make their first falling step in the
12492      same game frame, which is bad); this code is also needed to use the famous
12493      "spring push bug" which is used in older levels and might be wanted to be
12494      used also in newer levels, but in this case the buggy pushing code is only
12495      affecting the "spring" element and no other elements */
12496
12497   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12498   {
12499     for (i = 0; i < MAX_PLAYERS; i++)
12500     {
12501       struct PlayerInfo *player = &stored_player[i];
12502       int x = player->jx;
12503       int y = player->jy;
12504
12505       if (player->active && player->is_pushing && player->is_moving &&
12506           IS_MOVING(x, y) &&
12507           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12508            Tile[x][y] == EL_SPRING))
12509       {
12510         ContinueMoving(x, y);
12511
12512         // continue moving after pushing (this is actually a bug)
12513         if (!IS_MOVING(x, y))
12514           Stop[x][y] = FALSE;
12515       }
12516     }
12517   }
12518
12519   SCAN_PLAYFIELD(x, y)
12520   {
12521     Last[x][y] = Tile[x][y];
12522
12523     ChangeCount[x][y] = 0;
12524     ChangeEvent[x][y] = -1;
12525
12526     // this must be handled before main playfield loop
12527     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12528     {
12529       MovDelay[x][y]--;
12530       if (MovDelay[x][y] <= 0)
12531         RemoveField(x, y);
12532     }
12533
12534     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12535     {
12536       MovDelay[x][y]--;
12537       if (MovDelay[x][y] <= 0)
12538       {
12539         int element = Store[x][y];
12540         int move_direction = MovDir[x][y];
12541         int player_index_bit = Store2[x][y];
12542
12543         Store[x][y] = 0;
12544         Store2[x][y] = 0;
12545
12546         RemoveField(x, y);
12547         TEST_DrawLevelField(x, y);
12548
12549         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12550
12551         if (IS_ENVELOPE(element))
12552           local_player->show_envelope = element;
12553       }
12554     }
12555
12556 #if DEBUG
12557     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12558     {
12559       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12560             x, y);
12561       Debug("game:playing:GameActions_RND", "This should never happen!");
12562
12563       ChangePage[x][y] = -1;
12564     }
12565 #endif
12566
12567     Stop[x][y] = FALSE;
12568     if (WasJustMoving[x][y] > 0)
12569       WasJustMoving[x][y]--;
12570     if (WasJustFalling[x][y] > 0)
12571       WasJustFalling[x][y]--;
12572     if (CheckCollision[x][y] > 0)
12573       CheckCollision[x][y]--;
12574     if (CheckImpact[x][y] > 0)
12575       CheckImpact[x][y]--;
12576
12577     GfxFrame[x][y]++;
12578
12579     /* reset finished pushing action (not done in ContinueMoving() to allow
12580        continuous pushing animation for elements with zero push delay) */
12581     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12582     {
12583       ResetGfxAnimation(x, y);
12584       TEST_DrawLevelField(x, y);
12585     }
12586
12587 #if DEBUG
12588     if (IS_BLOCKED(x, y))
12589     {
12590       int oldx, oldy;
12591
12592       Blocked2Moving(x, y, &oldx, &oldy);
12593       if (!IS_MOVING(oldx, oldy))
12594       {
12595         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12596         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12597         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12598         Debug("game:playing:GameActions_RND", "This should never happen!");
12599       }
12600     }
12601 #endif
12602   }
12603
12604   HandleMouseAction(&mouse_action, &mouse_action_last);
12605
12606   SCAN_PLAYFIELD(x, y)
12607   {
12608     element = Tile[x][y];
12609     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12610     last_gfx_frame = GfxFrame[x][y];
12611
12612     if (element == EL_EMPTY)
12613       graphic = el2img(GfxElementEmpty[x][y]);
12614
12615     ResetGfxFrame(x, y);
12616
12617     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12618       DrawLevelGraphicAnimation(x, y, graphic);
12619
12620     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12621         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12622       ResetRandomAnimationValue(x, y);
12623
12624     SetRandomAnimationValue(x, y);
12625
12626     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12627
12628     if (IS_INACTIVE(element))
12629     {
12630       if (IS_ANIMATED(graphic))
12631         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12632
12633       continue;
12634     }
12635
12636     // this may take place after moving, so 'element' may have changed
12637     if (IS_CHANGING(x, y) &&
12638         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12639     {
12640       int page = element_info[element].event_page_nr[CE_DELAY];
12641
12642       HandleElementChange(x, y, page);
12643
12644       element = Tile[x][y];
12645       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12646     }
12647
12648     CheckNextToConditions(x, y);
12649
12650     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12651     {
12652       StartMoving(x, y);
12653
12654       element = Tile[x][y];
12655       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12656
12657       if (IS_ANIMATED(graphic) &&
12658           !IS_MOVING(x, y) &&
12659           !Stop[x][y])
12660         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12661
12662       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12663         TEST_DrawTwinkleOnField(x, y);
12664     }
12665     else if (element == EL_ACID)
12666     {
12667       if (!Stop[x][y])
12668         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12669     }
12670     else if ((element == EL_EXIT_OPEN ||
12671               element == EL_EM_EXIT_OPEN ||
12672               element == EL_SP_EXIT_OPEN ||
12673               element == EL_STEEL_EXIT_OPEN ||
12674               element == EL_EM_STEEL_EXIT_OPEN ||
12675               element == EL_SP_TERMINAL ||
12676               element == EL_SP_TERMINAL_ACTIVE ||
12677               element == EL_EXTRA_TIME ||
12678               element == EL_SHIELD_NORMAL ||
12679               element == EL_SHIELD_DEADLY) &&
12680              IS_ANIMATED(graphic))
12681       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12682     else if (IS_MOVING(x, y))
12683       ContinueMoving(x, y);
12684     else if (IS_ACTIVE_BOMB(element))
12685       CheckDynamite(x, y);
12686     else if (element == EL_AMOEBA_GROWING)
12687       AmoebaGrowing(x, y);
12688     else if (element == EL_AMOEBA_SHRINKING)
12689       AmoebaShrinking(x, y);
12690
12691 #if !USE_NEW_AMOEBA_CODE
12692     else if (IS_AMOEBALIVE(element))
12693       AmoebaReproduce(x, y);
12694 #endif
12695
12696     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12697       Life(x, y);
12698     else if (element == EL_EXIT_CLOSED)
12699       CheckExit(x, y);
12700     else if (element == EL_EM_EXIT_CLOSED)
12701       CheckExitEM(x, y);
12702     else if (element == EL_STEEL_EXIT_CLOSED)
12703       CheckExitSteel(x, y);
12704     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12705       CheckExitSteelEM(x, y);
12706     else if (element == EL_SP_EXIT_CLOSED)
12707       CheckExitSP(x, y);
12708     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12709              element == EL_EXPANDABLE_STEELWALL_GROWING)
12710       WallGrowing(x, y);
12711     else if (element == EL_EXPANDABLE_WALL ||
12712              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12713              element == EL_EXPANDABLE_WALL_VERTICAL ||
12714              element == EL_EXPANDABLE_WALL_ANY ||
12715              element == EL_BD_EXPANDABLE_WALL ||
12716              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12717              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12718              element == EL_EXPANDABLE_STEELWALL_ANY)
12719       CheckWallGrowing(x, y);
12720     else if (element == EL_FLAMES)
12721       CheckForDragon(x, y);
12722     else if (element == EL_EXPLOSION)
12723       ; // drawing of correct explosion animation is handled separately
12724     else if (element == EL_ELEMENT_SNAPPING ||
12725              element == EL_DIAGONAL_SHRINKING ||
12726              element == EL_DIAGONAL_GROWING)
12727     {
12728       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12729
12730       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12731     }
12732     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12733       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12734
12735     if (IS_BELT_ACTIVE(element))
12736       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12737
12738     if (game.magic_wall_active)
12739     {
12740       int jx = local_player->jx, jy = local_player->jy;
12741
12742       // play the element sound at the position nearest to the player
12743       if ((element == EL_MAGIC_WALL_FULL ||
12744            element == EL_MAGIC_WALL_ACTIVE ||
12745            element == EL_MAGIC_WALL_EMPTYING ||
12746            element == EL_BD_MAGIC_WALL_FULL ||
12747            element == EL_BD_MAGIC_WALL_ACTIVE ||
12748            element == EL_BD_MAGIC_WALL_EMPTYING ||
12749            element == EL_DC_MAGIC_WALL_FULL ||
12750            element == EL_DC_MAGIC_WALL_ACTIVE ||
12751            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12752           ABS(x - jx) + ABS(y - jy) <
12753           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12754       {
12755         magic_wall_x = x;
12756         magic_wall_y = y;
12757       }
12758     }
12759   }
12760
12761 #if USE_NEW_AMOEBA_CODE
12762   // new experimental amoeba growth stuff
12763   if (!(FrameCounter % 8))
12764   {
12765     static unsigned int random = 1684108901;
12766
12767     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12768     {
12769       x = RND(lev_fieldx);
12770       y = RND(lev_fieldy);
12771       element = Tile[x][y];
12772
12773       if (!IS_PLAYER(x, y) &&
12774           (element == EL_EMPTY ||
12775            CAN_GROW_INTO(element) ||
12776            element == EL_QUICKSAND_EMPTY ||
12777            element == EL_QUICKSAND_FAST_EMPTY ||
12778            element == EL_ACID_SPLASH_LEFT ||
12779            element == EL_ACID_SPLASH_RIGHT))
12780       {
12781         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12782             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12783             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12784             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12785           Tile[x][y] = EL_AMOEBA_DROP;
12786       }
12787
12788       random = random * 129 + 1;
12789     }
12790   }
12791 #endif
12792
12793   game.explosions_delayed = FALSE;
12794
12795   SCAN_PLAYFIELD(x, y)
12796   {
12797     element = Tile[x][y];
12798
12799     if (ExplodeField[x][y])
12800       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12801     else if (element == EL_EXPLOSION)
12802       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12803
12804     ExplodeField[x][y] = EX_TYPE_NONE;
12805   }
12806
12807   game.explosions_delayed = TRUE;
12808
12809   if (game.magic_wall_active)
12810   {
12811     if (!(game.magic_wall_time_left % 4))
12812     {
12813       int element = Tile[magic_wall_x][magic_wall_y];
12814
12815       if (element == EL_BD_MAGIC_WALL_FULL ||
12816           element == EL_BD_MAGIC_WALL_ACTIVE ||
12817           element == EL_BD_MAGIC_WALL_EMPTYING)
12818         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12819       else if (element == EL_DC_MAGIC_WALL_FULL ||
12820                element == EL_DC_MAGIC_WALL_ACTIVE ||
12821                element == EL_DC_MAGIC_WALL_EMPTYING)
12822         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12823       else
12824         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12825     }
12826
12827     if (game.magic_wall_time_left > 0)
12828     {
12829       game.magic_wall_time_left--;
12830
12831       if (!game.magic_wall_time_left)
12832       {
12833         SCAN_PLAYFIELD(x, y)
12834         {
12835           element = Tile[x][y];
12836
12837           if (element == EL_MAGIC_WALL_ACTIVE ||
12838               element == EL_MAGIC_WALL_FULL)
12839           {
12840             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12841             TEST_DrawLevelField(x, y);
12842           }
12843           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12844                    element == EL_BD_MAGIC_WALL_FULL)
12845           {
12846             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12847             TEST_DrawLevelField(x, y);
12848           }
12849           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12850                    element == EL_DC_MAGIC_WALL_FULL)
12851           {
12852             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12853             TEST_DrawLevelField(x, y);
12854           }
12855         }
12856
12857         game.magic_wall_active = FALSE;
12858       }
12859     }
12860   }
12861
12862   if (game.light_time_left > 0)
12863   {
12864     game.light_time_left--;
12865
12866     if (game.light_time_left == 0)
12867       RedrawAllLightSwitchesAndInvisibleElements();
12868   }
12869
12870   if (game.timegate_time_left > 0)
12871   {
12872     game.timegate_time_left--;
12873
12874     if (game.timegate_time_left == 0)
12875       CloseAllOpenTimegates();
12876   }
12877
12878   if (game.lenses_time_left > 0)
12879   {
12880     game.lenses_time_left--;
12881
12882     if (game.lenses_time_left == 0)
12883       RedrawAllInvisibleElementsForLenses();
12884   }
12885
12886   if (game.magnify_time_left > 0)
12887   {
12888     game.magnify_time_left--;
12889
12890     if (game.magnify_time_left == 0)
12891       RedrawAllInvisibleElementsForMagnifier();
12892   }
12893
12894   for (i = 0; i < MAX_PLAYERS; i++)
12895   {
12896     struct PlayerInfo *player = &stored_player[i];
12897
12898     if (SHIELD_ON(player))
12899     {
12900       if (player->shield_deadly_time_left)
12901         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12902       else if (player->shield_normal_time_left)
12903         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12904     }
12905   }
12906
12907 #if USE_DELAYED_GFX_REDRAW
12908   SCAN_PLAYFIELD(x, y)
12909   {
12910     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12911     {
12912       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12913          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12914
12915       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12916         DrawLevelField(x, y);
12917
12918       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12919         DrawLevelFieldCrumbled(x, y);
12920
12921       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12922         DrawLevelFieldCrumbledNeighbours(x, y);
12923
12924       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12925         DrawTwinkleOnField(x, y);
12926     }
12927
12928     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12929   }
12930 #endif
12931
12932   DrawAllPlayers();
12933   PlayAllPlayersSound();
12934
12935   for (i = 0; i < MAX_PLAYERS; i++)
12936   {
12937     struct PlayerInfo *player = &stored_player[i];
12938
12939     if (player->show_envelope != 0 && (!player->active ||
12940                                        player->MovPos == 0))
12941     {
12942       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12943
12944       player->show_envelope = 0;
12945     }
12946   }
12947
12948   // use random number generator in every frame to make it less predictable
12949   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12950     RND(1);
12951
12952   mouse_action_last = mouse_action;
12953 }
12954
12955 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12956 {
12957   int min_x = x, min_y = y, max_x = x, max_y = y;
12958   int scr_fieldx = getScreenFieldSizeX();
12959   int scr_fieldy = getScreenFieldSizeY();
12960   int i;
12961
12962   for (i = 0; i < MAX_PLAYERS; i++)
12963   {
12964     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12965
12966     if (!stored_player[i].active || &stored_player[i] == player)
12967       continue;
12968
12969     min_x = MIN(min_x, jx);
12970     min_y = MIN(min_y, jy);
12971     max_x = MAX(max_x, jx);
12972     max_y = MAX(max_y, jy);
12973   }
12974
12975   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12976 }
12977
12978 static boolean AllPlayersInVisibleScreen(void)
12979 {
12980   int i;
12981
12982   for (i = 0; i < MAX_PLAYERS; i++)
12983   {
12984     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12985
12986     if (!stored_player[i].active)
12987       continue;
12988
12989     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12990       return FALSE;
12991   }
12992
12993   return TRUE;
12994 }
12995
12996 void ScrollLevel(int dx, int dy)
12997 {
12998   int scroll_offset = 2 * TILEX_VAR;
12999   int x, y;
13000
13001   BlitBitmap(drawto_field, drawto_field,
13002              FX + TILEX_VAR * (dx == -1) - scroll_offset,
13003              FY + TILEY_VAR * (dy == -1) - scroll_offset,
13004              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
13005              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
13006              FX + TILEX_VAR * (dx == 1) - scroll_offset,
13007              FY + TILEY_VAR * (dy == 1) - scroll_offset);
13008
13009   if (dx != 0)
13010   {
13011     x = (dx == 1 ? BX1 : BX2);
13012     for (y = BY1; y <= BY2; y++)
13013       DrawScreenField(x, y);
13014   }
13015
13016   if (dy != 0)
13017   {
13018     y = (dy == 1 ? BY1 : BY2);
13019     for (x = BX1; x <= BX2; x++)
13020       DrawScreenField(x, y);
13021   }
13022
13023   redraw_mask |= REDRAW_FIELD;
13024 }
13025
13026 static boolean canFallDown(struct PlayerInfo *player)
13027 {
13028   int jx = player->jx, jy = player->jy;
13029
13030   return (IN_LEV_FIELD(jx, jy + 1) &&
13031           (IS_FREE(jx, jy + 1) ||
13032            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13033           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13034           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13035 }
13036
13037 static boolean canPassField(int x, int y, int move_dir)
13038 {
13039   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13040   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13041   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13042   int nextx = x + dx;
13043   int nexty = y + dy;
13044   int element = Tile[x][y];
13045
13046   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13047           !CAN_MOVE(element) &&
13048           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13049           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13050           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13051 }
13052
13053 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13054 {
13055   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13056   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13057   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13058   int newx = x + dx;
13059   int newy = y + dy;
13060
13061   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13062           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13063           (IS_DIGGABLE(Tile[newx][newy]) ||
13064            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13065            canPassField(newx, newy, move_dir)));
13066 }
13067
13068 static void CheckGravityMovement(struct PlayerInfo *player)
13069 {
13070   if (player->gravity && !player->programmed_action)
13071   {
13072     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13073     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13074     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13075     int jx = player->jx, jy = player->jy;
13076     boolean player_is_moving_to_valid_field =
13077       (!player_is_snapping &&
13078        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13079         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13080     boolean player_can_fall_down = canFallDown(player);
13081
13082     if (player_can_fall_down &&
13083         !player_is_moving_to_valid_field)
13084       player->programmed_action = MV_DOWN;
13085   }
13086 }
13087
13088 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13089 {
13090   return CheckGravityMovement(player);
13091
13092   if (player->gravity && !player->programmed_action)
13093   {
13094     int jx = player->jx, jy = player->jy;
13095     boolean field_under_player_is_free =
13096       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13097     boolean player_is_standing_on_valid_field =
13098       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13099        (IS_WALKABLE(Tile[jx][jy]) &&
13100         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13101
13102     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13103       player->programmed_action = MV_DOWN;
13104   }
13105 }
13106
13107 /*
13108   MovePlayerOneStep()
13109   -----------------------------------------------------------------------------
13110   dx, dy:               direction (non-diagonal) to try to move the player to
13111   real_dx, real_dy:     direction as read from input device (can be diagonal)
13112 */
13113
13114 boolean MovePlayerOneStep(struct PlayerInfo *player,
13115                           int dx, int dy, int real_dx, int real_dy)
13116 {
13117   int jx = player->jx, jy = player->jy;
13118   int new_jx = jx + dx, new_jy = jy + dy;
13119   int can_move;
13120   boolean player_can_move = !player->cannot_move;
13121
13122   if (!player->active || (!dx && !dy))
13123     return MP_NO_ACTION;
13124
13125   player->MovDir = (dx < 0 ? MV_LEFT :
13126                     dx > 0 ? MV_RIGHT :
13127                     dy < 0 ? MV_UP :
13128                     dy > 0 ? MV_DOWN :  MV_NONE);
13129
13130   if (!IN_LEV_FIELD(new_jx, new_jy))
13131     return MP_NO_ACTION;
13132
13133   if (!player_can_move)
13134   {
13135     if (player->MovPos == 0)
13136     {
13137       player->is_moving = FALSE;
13138       player->is_digging = FALSE;
13139       player->is_collecting = FALSE;
13140       player->is_snapping = FALSE;
13141       player->is_pushing = FALSE;
13142     }
13143   }
13144
13145   if (!network.enabled && game.centered_player_nr == -1 &&
13146       !AllPlayersInSight(player, new_jx, new_jy))
13147     return MP_NO_ACTION;
13148
13149   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13150   if (can_move != MP_MOVING)
13151     return can_move;
13152
13153   // check if DigField() has caused relocation of the player
13154   if (player->jx != jx || player->jy != jy)
13155     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13156
13157   StorePlayer[jx][jy] = 0;
13158   player->last_jx = jx;
13159   player->last_jy = jy;
13160   player->jx = new_jx;
13161   player->jy = new_jy;
13162   StorePlayer[new_jx][new_jy] = player->element_nr;
13163
13164   if (player->move_delay_value_next != -1)
13165   {
13166     player->move_delay_value = player->move_delay_value_next;
13167     player->move_delay_value_next = -1;
13168   }
13169
13170   player->MovPos =
13171     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13172
13173   player->step_counter++;
13174
13175   PlayerVisit[jx][jy] = FrameCounter;
13176
13177   player->is_moving = TRUE;
13178
13179 #if 1
13180   // should better be called in MovePlayer(), but this breaks some tapes
13181   ScrollPlayer(player, SCROLL_INIT);
13182 #endif
13183
13184   return MP_MOVING;
13185 }
13186
13187 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13188 {
13189   int jx = player->jx, jy = player->jy;
13190   int old_jx = jx, old_jy = jy;
13191   int moved = MP_NO_ACTION;
13192
13193   if (!player->active)
13194     return FALSE;
13195
13196   if (!dx && !dy)
13197   {
13198     if (player->MovPos == 0)
13199     {
13200       player->is_moving = FALSE;
13201       player->is_digging = FALSE;
13202       player->is_collecting = FALSE;
13203       player->is_snapping = FALSE;
13204       player->is_pushing = FALSE;
13205     }
13206
13207     return FALSE;
13208   }
13209
13210   if (player->move_delay > 0)
13211     return FALSE;
13212
13213   player->move_delay = -1;              // set to "uninitialized" value
13214
13215   // store if player is automatically moved to next field
13216   player->is_auto_moving = (player->programmed_action != MV_NONE);
13217
13218   // remove the last programmed player action
13219   player->programmed_action = 0;
13220
13221   if (player->MovPos)
13222   {
13223     // should only happen if pre-1.2 tape recordings are played
13224     // this is only for backward compatibility
13225
13226     int original_move_delay_value = player->move_delay_value;
13227
13228 #if DEBUG
13229     Debug("game:playing:MovePlayer",
13230           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13231           tape.counter);
13232 #endif
13233
13234     // scroll remaining steps with finest movement resolution
13235     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13236
13237     while (player->MovPos)
13238     {
13239       ScrollPlayer(player, SCROLL_GO_ON);
13240       ScrollScreen(NULL, SCROLL_GO_ON);
13241
13242       AdvanceFrameAndPlayerCounters(player->index_nr);
13243
13244       DrawAllPlayers();
13245       BackToFront_WithFrameDelay(0);
13246     }
13247
13248     player->move_delay_value = original_move_delay_value;
13249   }
13250
13251   player->is_active = FALSE;
13252
13253   if (player->last_move_dir & MV_HORIZONTAL)
13254   {
13255     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13256       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13257   }
13258   else
13259   {
13260     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13261       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13262   }
13263
13264   if (!moved && !player->is_active)
13265   {
13266     player->is_moving = FALSE;
13267     player->is_digging = FALSE;
13268     player->is_collecting = FALSE;
13269     player->is_snapping = FALSE;
13270     player->is_pushing = FALSE;
13271   }
13272
13273   jx = player->jx;
13274   jy = player->jy;
13275
13276   if (moved & MP_MOVING && !ScreenMovPos &&
13277       (player->index_nr == game.centered_player_nr ||
13278        game.centered_player_nr == -1))
13279   {
13280     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13281
13282     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13283     {
13284       // actual player has left the screen -- scroll in that direction
13285       if (jx != old_jx)         // player has moved horizontally
13286         scroll_x += (jx - old_jx);
13287       else                      // player has moved vertically
13288         scroll_y += (jy - old_jy);
13289     }
13290     else
13291     {
13292       int offset_raw = game.scroll_delay_value;
13293
13294       if (jx != old_jx)         // player has moved horizontally
13295       {
13296         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13297         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13298         int new_scroll_x = jx - MIDPOSX + offset_x;
13299
13300         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13301             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13302           scroll_x = new_scroll_x;
13303
13304         // don't scroll over playfield boundaries
13305         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13306
13307         // don't scroll more than one field at a time
13308         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13309
13310         // don't scroll against the player's moving direction
13311         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13312             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13313           scroll_x = old_scroll_x;
13314       }
13315       else                      // player has moved vertically
13316       {
13317         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13318         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13319         int new_scroll_y = jy - MIDPOSY + offset_y;
13320
13321         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13322             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13323           scroll_y = new_scroll_y;
13324
13325         // don't scroll over playfield boundaries
13326         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13327
13328         // don't scroll more than one field at a time
13329         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13330
13331         // don't scroll against the player's moving direction
13332         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13333             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13334           scroll_y = old_scroll_y;
13335       }
13336     }
13337
13338     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13339     {
13340       if (!network.enabled && game.centered_player_nr == -1 &&
13341           !AllPlayersInVisibleScreen())
13342       {
13343         scroll_x = old_scroll_x;
13344         scroll_y = old_scroll_y;
13345       }
13346       else
13347       {
13348         ScrollScreen(player, SCROLL_INIT);
13349         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13350       }
13351     }
13352   }
13353
13354   player->StepFrame = 0;
13355
13356   if (moved & MP_MOVING)
13357   {
13358     if (old_jx != jx && old_jy == jy)
13359       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13360     else if (old_jx == jx && old_jy != jy)
13361       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13362
13363     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13364
13365     player->last_move_dir = player->MovDir;
13366     player->is_moving = TRUE;
13367     player->is_snapping = FALSE;
13368     player->is_switching = FALSE;
13369     player->is_dropping = FALSE;
13370     player->is_dropping_pressed = FALSE;
13371     player->drop_pressed_delay = 0;
13372
13373 #if 0
13374     // should better be called here than above, but this breaks some tapes
13375     ScrollPlayer(player, SCROLL_INIT);
13376 #endif
13377   }
13378   else
13379   {
13380     CheckGravityMovementWhenNotMoving(player);
13381
13382     player->is_moving = FALSE;
13383
13384     /* at this point, the player is allowed to move, but cannot move right now
13385        (e.g. because of something blocking the way) -- ensure that the player
13386        is also allowed to move in the next frame (in old versions before 3.1.1,
13387        the player was forced to wait again for eight frames before next try) */
13388
13389     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13390       player->move_delay = 0;   // allow direct movement in the next frame
13391   }
13392
13393   if (player->move_delay == -1)         // not yet initialized by DigField()
13394     player->move_delay = player->move_delay_value;
13395
13396   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13397   {
13398     TestIfPlayerTouchesBadThing(jx, jy);
13399     TestIfPlayerTouchesCustomElement(jx, jy);
13400   }
13401
13402   if (!player->active)
13403     RemovePlayer(player);
13404
13405   return moved;
13406 }
13407
13408 void ScrollPlayer(struct PlayerInfo *player, int mode)
13409 {
13410   int jx = player->jx, jy = player->jy;
13411   int last_jx = player->last_jx, last_jy = player->last_jy;
13412   int move_stepsize = TILEX / player->move_delay_value;
13413
13414   if (!player->active)
13415     return;
13416
13417   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13418     return;
13419
13420   if (mode == SCROLL_INIT)
13421   {
13422     player->actual_frame_counter.count = FrameCounter;
13423     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13424
13425     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13426         Tile[last_jx][last_jy] == EL_EMPTY)
13427     {
13428       int last_field_block_delay = 0;   // start with no blocking at all
13429       int block_delay_adjustment = player->block_delay_adjustment;
13430
13431       // if player blocks last field, add delay for exactly one move
13432       if (player->block_last_field)
13433       {
13434         last_field_block_delay += player->move_delay_value;
13435
13436         // when blocking enabled, prevent moving up despite gravity
13437         if (player->gravity && player->MovDir == MV_UP)
13438           block_delay_adjustment = -1;
13439       }
13440
13441       // add block delay adjustment (also possible when not blocking)
13442       last_field_block_delay += block_delay_adjustment;
13443
13444       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13445       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13446     }
13447
13448     if (player->MovPos != 0)    // player has not yet reached destination
13449       return;
13450   }
13451   else if (!FrameReached(&player->actual_frame_counter))
13452     return;
13453
13454   if (player->MovPos != 0)
13455   {
13456     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13457     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13458
13459     // before DrawPlayer() to draw correct player graphic for this case
13460     if (player->MovPos == 0)
13461       CheckGravityMovement(player);
13462   }
13463
13464   if (player->MovPos == 0)      // player reached destination field
13465   {
13466     if (player->move_delay_reset_counter > 0)
13467     {
13468       player->move_delay_reset_counter--;
13469
13470       if (player->move_delay_reset_counter == 0)
13471       {
13472         // continue with normal speed after quickly moving through gate
13473         HALVE_PLAYER_SPEED(player);
13474
13475         // be able to make the next move without delay
13476         player->move_delay = 0;
13477       }
13478     }
13479
13480     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13481         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13482         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13483         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13484         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13485         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13486         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13487         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13488     {
13489       ExitPlayer(player);
13490
13491       if (game.players_still_needed == 0 &&
13492           (game.friends_still_needed == 0 ||
13493            IS_SP_ELEMENT(Tile[jx][jy])))
13494         LevelSolved();
13495     }
13496
13497     player->last_jx = jx;
13498     player->last_jy = jy;
13499
13500     // this breaks one level: "machine", level 000
13501     {
13502       int move_direction = player->MovDir;
13503       int enter_side = MV_DIR_OPPOSITE(move_direction);
13504       int leave_side = move_direction;
13505       int old_jx = last_jx;
13506       int old_jy = last_jy;
13507       int old_element = Tile[old_jx][old_jy];
13508       int new_element = Tile[jx][jy];
13509
13510       if (IS_CUSTOM_ELEMENT(old_element))
13511         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13512                                    CE_LEFT_BY_PLAYER,
13513                                    player->index_bit, leave_side);
13514
13515       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13516                                           CE_PLAYER_LEAVES_X,
13517                                           player->index_bit, leave_side);
13518
13519       // needed because pushed element has not yet reached its destination,
13520       // so it would trigger a change event at its previous field location
13521       if (!player->is_pushing)
13522       {
13523         if (IS_CUSTOM_ELEMENT(new_element))
13524           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13525                                      player->index_bit, enter_side);
13526
13527         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13528                                             CE_PLAYER_ENTERS_X,
13529                                             player->index_bit, enter_side);
13530       }
13531
13532       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13533                                         CE_MOVE_OF_X, move_direction);
13534     }
13535
13536     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13537     {
13538       TestIfPlayerTouchesBadThing(jx, jy);
13539       TestIfPlayerTouchesCustomElement(jx, jy);
13540
13541       // needed because pushed element has not yet reached its destination,
13542       // so it would trigger a change event at its previous field location
13543       if (!player->is_pushing)
13544         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13545
13546       if (level.finish_dig_collect &&
13547           (player->is_digging || player->is_collecting))
13548       {
13549         int last_element = player->last_removed_element;
13550         int move_direction = player->MovDir;
13551         int enter_side = MV_DIR_OPPOSITE(move_direction);
13552         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13553                             CE_PLAYER_COLLECTS_X);
13554
13555         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13556                                             player->index_bit, enter_side);
13557
13558         player->last_removed_element = EL_UNDEFINED;
13559       }
13560
13561       if (!player->active)
13562         RemovePlayer(player);
13563     }
13564
13565     if (level.use_step_counter)
13566       CheckLevelTime_StepCounter();
13567
13568     if (tape.single_step && tape.recording && !tape.pausing &&
13569         !player->programmed_action)
13570       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13571
13572     if (!player->programmed_action)
13573       CheckSaveEngineSnapshot(player);
13574   }
13575 }
13576
13577 void ScrollScreen(struct PlayerInfo *player, int mode)
13578 {
13579   static DelayCounter screen_frame_counter = { 0 };
13580
13581   if (mode == SCROLL_INIT)
13582   {
13583     // set scrolling step size according to actual player's moving speed
13584     ScrollStepSize = TILEX / player->move_delay_value;
13585
13586     screen_frame_counter.count = FrameCounter;
13587     screen_frame_counter.value = 1;
13588
13589     ScreenMovDir = player->MovDir;
13590     ScreenMovPos = player->MovPos;
13591     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13592     return;
13593   }
13594   else if (!FrameReached(&screen_frame_counter))
13595     return;
13596
13597   if (ScreenMovPos)
13598   {
13599     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13600     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13601     redraw_mask |= REDRAW_FIELD;
13602   }
13603   else
13604     ScreenMovDir = MV_NONE;
13605 }
13606
13607 void CheckNextToConditions(int x, int y)
13608 {
13609   int element = Tile[x][y];
13610
13611   if (IS_PLAYER(x, y))
13612     TestIfPlayerNextToCustomElement(x, y);
13613
13614   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13615       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13616     TestIfElementNextToCustomElement(x, y);
13617 }
13618
13619 void TestIfPlayerNextToCustomElement(int x, int y)
13620 {
13621   struct XY *xy = xy_topdown;
13622   static int trigger_sides[4][2] =
13623   {
13624     // center side       border side
13625     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13626     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13627     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13628     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13629   };
13630   int i;
13631
13632   if (!IS_PLAYER(x, y))
13633     return;
13634
13635   struct PlayerInfo *player = PLAYERINFO(x, y);
13636
13637   if (player->is_moving)
13638     return;
13639
13640   for (i = 0; i < NUM_DIRECTIONS; i++)
13641   {
13642     int xx = x + xy[i].x;
13643     int yy = y + xy[i].y;
13644     int border_side = trigger_sides[i][1];
13645     int border_element;
13646
13647     if (!IN_LEV_FIELD(xx, yy))
13648       continue;
13649
13650     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13651       continue;         // center and border element not connected
13652
13653     border_element = Tile[xx][yy];
13654
13655     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13656                                player->index_bit, border_side);
13657     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13658                                         CE_PLAYER_NEXT_TO_X,
13659                                         player->index_bit, border_side);
13660
13661     /* use player element that is initially defined in the level playfield,
13662        not the player element that corresponds to the runtime player number
13663        (example: a level that contains EL_PLAYER_3 as the only player would
13664        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13665
13666     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13667                              CE_NEXT_TO_X, border_side);
13668   }
13669 }
13670
13671 void TestIfPlayerTouchesCustomElement(int x, int y)
13672 {
13673   struct XY *xy = xy_topdown;
13674   static int trigger_sides[4][2] =
13675   {
13676     // center side       border side
13677     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13678     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13679     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13680     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13681   };
13682   static int touch_dir[4] =
13683   {
13684     MV_LEFT | MV_RIGHT,
13685     MV_UP   | MV_DOWN,
13686     MV_UP   | MV_DOWN,
13687     MV_LEFT | MV_RIGHT
13688   };
13689   int center_element = Tile[x][y];      // should always be non-moving!
13690   int i;
13691
13692   for (i = 0; i < NUM_DIRECTIONS; i++)
13693   {
13694     int xx = x + xy[i].x;
13695     int yy = y + xy[i].y;
13696     int center_side = trigger_sides[i][0];
13697     int border_side = trigger_sides[i][1];
13698     int border_element;
13699
13700     if (!IN_LEV_FIELD(xx, yy))
13701       continue;
13702
13703     if (IS_PLAYER(x, y))                // player found at center element
13704     {
13705       struct PlayerInfo *player = PLAYERINFO(x, y);
13706
13707       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13708         border_element = Tile[xx][yy];          // may be moving!
13709       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13710         border_element = Tile[xx][yy];
13711       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13712         border_element = MovingOrBlocked2Element(xx, yy);
13713       else
13714         continue;               // center and border element do not touch
13715
13716       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13717                                  player->index_bit, border_side);
13718       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13719                                           CE_PLAYER_TOUCHES_X,
13720                                           player->index_bit, border_side);
13721
13722       {
13723         /* use player element that is initially defined in the level playfield,
13724            not the player element that corresponds to the runtime player number
13725            (example: a level that contains EL_PLAYER_3 as the only player would
13726            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13727         int player_element = PLAYERINFO(x, y)->initial_element;
13728
13729         // as element "X" is the player here, check opposite (center) side
13730         CheckElementChangeBySide(xx, yy, border_element, player_element,
13731                                  CE_TOUCHING_X, center_side);
13732       }
13733     }
13734     else if (IS_PLAYER(xx, yy))         // player found at border element
13735     {
13736       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13737
13738       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13739       {
13740         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13741           continue;             // center and border element do not touch
13742       }
13743
13744       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13745                                  player->index_bit, center_side);
13746       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13747                                           CE_PLAYER_TOUCHES_X,
13748                                           player->index_bit, center_side);
13749
13750       {
13751         /* use player element that is initially defined in the level playfield,
13752            not the player element that corresponds to the runtime player number
13753            (example: a level that contains EL_PLAYER_3 as the only player would
13754            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13755         int player_element = PLAYERINFO(xx, yy)->initial_element;
13756
13757         // as element "X" is the player here, check opposite (border) side
13758         CheckElementChangeBySide(x, y, center_element, player_element,
13759                                  CE_TOUCHING_X, border_side);
13760       }
13761
13762       break;
13763     }
13764   }
13765 }
13766
13767 void TestIfElementNextToCustomElement(int x, int y)
13768 {
13769   struct XY *xy = xy_topdown;
13770   static int trigger_sides[4][2] =
13771   {
13772     // center side      border side
13773     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13774     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13775     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13776     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13777   };
13778   int center_element = Tile[x][y];      // should always be non-moving!
13779   int i;
13780
13781   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13782     return;
13783
13784   for (i = 0; i < NUM_DIRECTIONS; i++)
13785   {
13786     int xx = x + xy[i].x;
13787     int yy = y + xy[i].y;
13788     int border_side = trigger_sides[i][1];
13789     int border_element;
13790
13791     if (!IN_LEV_FIELD(xx, yy))
13792       continue;
13793
13794     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13795       continue;                 // center and border element not connected
13796
13797     border_element = Tile[xx][yy];
13798
13799     // check for change of center element (but change it only once)
13800     if (CheckElementChangeBySide(x, y, center_element, border_element,
13801                                  CE_NEXT_TO_X, border_side))
13802       break;
13803   }
13804 }
13805
13806 void TestIfElementTouchesCustomElement(int x, int y)
13807 {
13808   struct XY *xy = xy_topdown;
13809   static int trigger_sides[4][2] =
13810   {
13811     // center side      border side
13812     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13813     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13814     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13815     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13816   };
13817   static int touch_dir[4] =
13818   {
13819     MV_LEFT | MV_RIGHT,
13820     MV_UP   | MV_DOWN,
13821     MV_UP   | MV_DOWN,
13822     MV_LEFT | MV_RIGHT
13823   };
13824   boolean change_center_element = FALSE;
13825   int center_element = Tile[x][y];      // should always be non-moving!
13826   int border_element_old[NUM_DIRECTIONS];
13827   int i;
13828
13829   for (i = 0; i < NUM_DIRECTIONS; i++)
13830   {
13831     int xx = x + xy[i].x;
13832     int yy = y + xy[i].y;
13833     int border_element;
13834
13835     border_element_old[i] = -1;
13836
13837     if (!IN_LEV_FIELD(xx, yy))
13838       continue;
13839
13840     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13841       border_element = Tile[xx][yy];    // may be moving!
13842     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13843       border_element = Tile[xx][yy];
13844     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13845       border_element = MovingOrBlocked2Element(xx, yy);
13846     else
13847       continue;                 // center and border element do not touch
13848
13849     border_element_old[i] = border_element;
13850   }
13851
13852   for (i = 0; i < NUM_DIRECTIONS; i++)
13853   {
13854     int xx = x + xy[i].x;
13855     int yy = y + xy[i].y;
13856     int center_side = trigger_sides[i][0];
13857     int border_element = border_element_old[i];
13858
13859     if (border_element == -1)
13860       continue;
13861
13862     // check for change of border element
13863     CheckElementChangeBySide(xx, yy, border_element, center_element,
13864                              CE_TOUCHING_X, center_side);
13865
13866     // (center element cannot be player, so we don't have to check this here)
13867   }
13868
13869   for (i = 0; i < NUM_DIRECTIONS; i++)
13870   {
13871     int xx = x + xy[i].x;
13872     int yy = y + xy[i].y;
13873     int border_side = trigger_sides[i][1];
13874     int border_element = border_element_old[i];
13875
13876     if (border_element == -1)
13877       continue;
13878
13879     // check for change of center element (but change it only once)
13880     if (!change_center_element)
13881       change_center_element =
13882         CheckElementChangeBySide(x, y, center_element, border_element,
13883                                  CE_TOUCHING_X, border_side);
13884
13885     if (IS_PLAYER(xx, yy))
13886     {
13887       /* use player element that is initially defined in the level playfield,
13888          not the player element that corresponds to the runtime player number
13889          (example: a level that contains EL_PLAYER_3 as the only player would
13890          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13891       int player_element = PLAYERINFO(xx, yy)->initial_element;
13892
13893       // as element "X" is the player here, check opposite (border) side
13894       CheckElementChangeBySide(x, y, center_element, player_element,
13895                                CE_TOUCHING_X, border_side);
13896     }
13897   }
13898 }
13899
13900 void TestIfElementHitsCustomElement(int x, int y, int direction)
13901 {
13902   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13903   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13904   int hitx = x + dx, hity = y + dy;
13905   int hitting_element = Tile[x][y];
13906   int touched_element;
13907
13908   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13909     return;
13910
13911   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13912                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13913
13914   if (IN_LEV_FIELD(hitx, hity))
13915   {
13916     int opposite_direction = MV_DIR_OPPOSITE(direction);
13917     int hitting_side = direction;
13918     int touched_side = opposite_direction;
13919     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13920                           MovDir[hitx][hity] != direction ||
13921                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13922
13923     object_hit = TRUE;
13924
13925     if (object_hit)
13926     {
13927       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13928                                CE_HITTING_X, touched_side);
13929
13930       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13931                                CE_HIT_BY_X, hitting_side);
13932
13933       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13934                                CE_HIT_BY_SOMETHING, opposite_direction);
13935
13936       if (IS_PLAYER(hitx, hity))
13937       {
13938         /* use player element that is initially defined in the level playfield,
13939            not the player element that corresponds to the runtime player number
13940            (example: a level that contains EL_PLAYER_3 as the only player would
13941            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13942         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13943
13944         CheckElementChangeBySide(x, y, hitting_element, player_element,
13945                                  CE_HITTING_X, touched_side);
13946       }
13947     }
13948   }
13949
13950   // "hitting something" is also true when hitting the playfield border
13951   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13952                            CE_HITTING_SOMETHING, direction);
13953 }
13954
13955 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13956 {
13957   int i, kill_x = -1, kill_y = -1;
13958
13959   int bad_element = -1;
13960   struct XY *test_xy = xy_topdown;
13961   static int test_dir[4] =
13962   {
13963     MV_UP,
13964     MV_LEFT,
13965     MV_RIGHT,
13966     MV_DOWN
13967   };
13968
13969   for (i = 0; i < NUM_DIRECTIONS; i++)
13970   {
13971     int test_x, test_y, test_move_dir, test_element;
13972
13973     test_x = good_x + test_xy[i].x;
13974     test_y = good_y + test_xy[i].y;
13975
13976     if (!IN_LEV_FIELD(test_x, test_y))
13977       continue;
13978
13979     test_move_dir =
13980       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13981
13982     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13983
13984     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13985        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13986     */
13987     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13988         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13989     {
13990       kill_x = test_x;
13991       kill_y = test_y;
13992       bad_element = test_element;
13993
13994       break;
13995     }
13996   }
13997
13998   if (kill_x != -1 || kill_y != -1)
13999   {
14000     if (IS_PLAYER(good_x, good_y))
14001     {
14002       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14003
14004       if (player->shield_deadly_time_left > 0 &&
14005           !IS_INDESTRUCTIBLE(bad_element))
14006         Bang(kill_x, kill_y);
14007       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14008         KillPlayer(player);
14009     }
14010     else
14011       Bang(good_x, good_y);
14012   }
14013 }
14014
14015 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14016 {
14017   int i, kill_x = -1, kill_y = -1;
14018   int bad_element = Tile[bad_x][bad_y];
14019   struct XY *test_xy = xy_topdown;
14020   static int touch_dir[4] =
14021   {
14022     MV_LEFT | MV_RIGHT,
14023     MV_UP   | MV_DOWN,
14024     MV_UP   | MV_DOWN,
14025     MV_LEFT | MV_RIGHT
14026   };
14027   static int test_dir[4] =
14028   {
14029     MV_UP,
14030     MV_LEFT,
14031     MV_RIGHT,
14032     MV_DOWN
14033   };
14034
14035   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14036     return;
14037
14038   for (i = 0; i < NUM_DIRECTIONS; i++)
14039   {
14040     int test_x, test_y, test_move_dir, test_element;
14041
14042     test_x = bad_x + test_xy[i].x;
14043     test_y = bad_y + test_xy[i].y;
14044
14045     if (!IN_LEV_FIELD(test_x, test_y))
14046       continue;
14047
14048     test_move_dir =
14049       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14050
14051     test_element = Tile[test_x][test_y];
14052
14053     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14054        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14055     */
14056     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14057         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14058     {
14059       // good thing is player or penguin that does not move away
14060       if (IS_PLAYER(test_x, test_y))
14061       {
14062         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14063
14064         if (bad_element == EL_ROBOT && player->is_moving)
14065           continue;     // robot does not kill player if he is moving
14066
14067         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14068         {
14069           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14070             continue;           // center and border element do not touch
14071         }
14072
14073         kill_x = test_x;
14074         kill_y = test_y;
14075
14076         break;
14077       }
14078       else if (test_element == EL_PENGUIN)
14079       {
14080         kill_x = test_x;
14081         kill_y = test_y;
14082
14083         break;
14084       }
14085     }
14086   }
14087
14088   if (kill_x != -1 || kill_y != -1)
14089   {
14090     if (IS_PLAYER(kill_x, kill_y))
14091     {
14092       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14093
14094       if (player->shield_deadly_time_left > 0 &&
14095           !IS_INDESTRUCTIBLE(bad_element))
14096         Bang(bad_x, bad_y);
14097       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14098         KillPlayer(player);
14099     }
14100     else
14101       Bang(kill_x, kill_y);
14102   }
14103 }
14104
14105 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14106 {
14107   int bad_element = Tile[bad_x][bad_y];
14108   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14109   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14110   int test_x = bad_x + dx, test_y = bad_y + dy;
14111   int test_move_dir, test_element;
14112   int kill_x = -1, kill_y = -1;
14113
14114   if (!IN_LEV_FIELD(test_x, test_y))
14115     return;
14116
14117   test_move_dir =
14118     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14119
14120   test_element = Tile[test_x][test_y];
14121
14122   if (test_move_dir != bad_move_dir)
14123   {
14124     // good thing can be player or penguin that does not move away
14125     if (IS_PLAYER(test_x, test_y))
14126     {
14127       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14128
14129       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14130          player as being hit when he is moving towards the bad thing, because
14131          the "get hit by" condition would be lost after the player stops) */
14132       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14133         return;         // player moves away from bad thing
14134
14135       kill_x = test_x;
14136       kill_y = test_y;
14137     }
14138     else if (test_element == EL_PENGUIN)
14139     {
14140       kill_x = test_x;
14141       kill_y = test_y;
14142     }
14143   }
14144
14145   if (kill_x != -1 || kill_y != -1)
14146   {
14147     if (IS_PLAYER(kill_x, kill_y))
14148     {
14149       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14150
14151       if (player->shield_deadly_time_left > 0 &&
14152           !IS_INDESTRUCTIBLE(bad_element))
14153         Bang(bad_x, bad_y);
14154       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14155         KillPlayer(player);
14156     }
14157     else
14158       Bang(kill_x, kill_y);
14159   }
14160 }
14161
14162 void TestIfPlayerTouchesBadThing(int x, int y)
14163 {
14164   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14165 }
14166
14167 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14168 {
14169   TestIfGoodThingHitsBadThing(x, y, move_dir);
14170 }
14171
14172 void TestIfBadThingTouchesPlayer(int x, int y)
14173 {
14174   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14175 }
14176
14177 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14178 {
14179   TestIfBadThingHitsGoodThing(x, y, move_dir);
14180 }
14181
14182 void TestIfFriendTouchesBadThing(int x, int y)
14183 {
14184   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14185 }
14186
14187 void TestIfBadThingTouchesFriend(int x, int y)
14188 {
14189   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14190 }
14191
14192 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14193 {
14194   int i, kill_x = bad_x, kill_y = bad_y;
14195   struct XY *xy = xy_topdown;
14196
14197   for (i = 0; i < NUM_DIRECTIONS; i++)
14198   {
14199     int x, y, element;
14200
14201     x = bad_x + xy[i].x;
14202     y = bad_y + xy[i].y;
14203     if (!IN_LEV_FIELD(x, y))
14204       continue;
14205
14206     element = Tile[x][y];
14207     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14208         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14209     {
14210       kill_x = x;
14211       kill_y = y;
14212       break;
14213     }
14214   }
14215
14216   if (kill_x != bad_x || kill_y != bad_y)
14217     Bang(bad_x, bad_y);
14218 }
14219
14220 void KillPlayer(struct PlayerInfo *player)
14221 {
14222   int jx = player->jx, jy = player->jy;
14223
14224   if (!player->active)
14225     return;
14226
14227 #if 0
14228   Debug("game:playing:KillPlayer",
14229         "0: killed == %d, active == %d, reanimated == %d",
14230         player->killed, player->active, player->reanimated);
14231 #endif
14232
14233   /* the following code was introduced to prevent an infinite loop when calling
14234      -> Bang()
14235      -> CheckTriggeredElementChangeExt()
14236      -> ExecuteCustomElementAction()
14237      -> KillPlayer()
14238      -> (infinitely repeating the above sequence of function calls)
14239      which occurs when killing the player while having a CE with the setting
14240      "kill player X when explosion of <player X>"; the solution using a new
14241      field "player->killed" was chosen for backwards compatibility, although
14242      clever use of the fields "player->active" etc. would probably also work */
14243 #if 1
14244   if (player->killed)
14245     return;
14246 #endif
14247
14248   player->killed = TRUE;
14249
14250   // remove accessible field at the player's position
14251   RemoveField(jx, jy);
14252
14253   // deactivate shield (else Bang()/Explode() would not work right)
14254   player->shield_normal_time_left = 0;
14255   player->shield_deadly_time_left = 0;
14256
14257 #if 0
14258   Debug("game:playing:KillPlayer",
14259         "1: killed == %d, active == %d, reanimated == %d",
14260         player->killed, player->active, player->reanimated);
14261 #endif
14262
14263   Bang(jx, jy);
14264
14265 #if 0
14266   Debug("game:playing:KillPlayer",
14267         "2: killed == %d, active == %d, reanimated == %d",
14268         player->killed, player->active, player->reanimated);
14269 #endif
14270
14271   if (player->reanimated)       // killed player may have been reanimated
14272     player->killed = player->reanimated = FALSE;
14273   else
14274     BuryPlayer(player);
14275 }
14276
14277 static void KillPlayerUnlessEnemyProtected(int x, int y)
14278 {
14279   if (!PLAYER_ENEMY_PROTECTED(x, y))
14280     KillPlayer(PLAYERINFO(x, y));
14281 }
14282
14283 static void KillPlayerUnlessExplosionProtected(int x, int y)
14284 {
14285   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14286     KillPlayer(PLAYERINFO(x, y));
14287 }
14288
14289 void BuryPlayer(struct PlayerInfo *player)
14290 {
14291   int jx = player->jx, jy = player->jy;
14292
14293   if (!player->active)
14294     return;
14295
14296   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14297
14298   RemovePlayer(player);
14299
14300   player->buried = TRUE;
14301
14302   if (game.all_players_gone)
14303     game.GameOver = TRUE;
14304 }
14305
14306 void RemovePlayer(struct PlayerInfo *player)
14307 {
14308   int jx = player->jx, jy = player->jy;
14309   int i, found = FALSE;
14310
14311   player->present = FALSE;
14312   player->active = FALSE;
14313
14314   // required for some CE actions (even if the player is not active anymore)
14315   player->MovPos = 0;
14316
14317   if (!ExplodeField[jx][jy])
14318     StorePlayer[jx][jy] = 0;
14319
14320   if (player->is_moving)
14321     TEST_DrawLevelField(player->last_jx, player->last_jy);
14322
14323   for (i = 0; i < MAX_PLAYERS; i++)
14324     if (stored_player[i].active)
14325       found = TRUE;
14326
14327   if (!found)
14328   {
14329     game.all_players_gone = TRUE;
14330     game.GameOver = TRUE;
14331   }
14332
14333   game.exit_x = game.robot_wheel_x = jx;
14334   game.exit_y = game.robot_wheel_y = jy;
14335 }
14336
14337 void ExitPlayer(struct PlayerInfo *player)
14338 {
14339   DrawPlayer(player);   // needed here only to cleanup last field
14340   RemovePlayer(player);
14341
14342   if (game.players_still_needed > 0)
14343     game.players_still_needed--;
14344 }
14345
14346 static void SetFieldForSnapping(int x, int y, int element, int direction,
14347                                 int player_index_bit)
14348 {
14349   struct ElementInfo *ei = &element_info[element];
14350   int direction_bit = MV_DIR_TO_BIT(direction);
14351   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14352   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14353                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14354
14355   Tile[x][y] = EL_ELEMENT_SNAPPING;
14356   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14357   MovDir[x][y] = direction;
14358   Store[x][y] = element;
14359   Store2[x][y] = player_index_bit;
14360
14361   ResetGfxAnimation(x, y);
14362
14363   GfxElement[x][y] = element;
14364   GfxAction[x][y] = action;
14365   GfxDir[x][y] = direction;
14366   GfxFrame[x][y] = -1;
14367 }
14368
14369 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14370                                    int player_index_bit)
14371 {
14372   TestIfElementTouchesCustomElement(x, y);      // for empty space
14373
14374   if (level.finish_dig_collect)
14375   {
14376     int dig_side = MV_DIR_OPPOSITE(direction);
14377     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14378                         CE_PLAYER_COLLECTS_X);
14379
14380     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14381                                         player_index_bit, dig_side);
14382     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14383                                         player_index_bit, dig_side);
14384   }
14385 }
14386
14387 /*
14388   =============================================================================
14389   checkDiagonalPushing()
14390   -----------------------------------------------------------------------------
14391   check if diagonal input device direction results in pushing of object
14392   (by checking if the alternative direction is walkable, diggable, ...)
14393   =============================================================================
14394 */
14395
14396 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14397                                     int x, int y, int real_dx, int real_dy)
14398 {
14399   int jx, jy, dx, dy, xx, yy;
14400
14401   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14402     return TRUE;
14403
14404   // diagonal direction: check alternative direction
14405   jx = player->jx;
14406   jy = player->jy;
14407   dx = x - jx;
14408   dy = y - jy;
14409   xx = jx + (dx == 0 ? real_dx : 0);
14410   yy = jy + (dy == 0 ? real_dy : 0);
14411
14412   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14413 }
14414
14415 /*
14416   =============================================================================
14417   DigField()
14418   -----------------------------------------------------------------------------
14419   x, y:                 field next to player (non-diagonal) to try to dig to
14420   real_dx, real_dy:     direction as read from input device (can be diagonal)
14421   =============================================================================
14422 */
14423
14424 static int DigField(struct PlayerInfo *player,
14425                     int oldx, int oldy, int x, int y,
14426                     int real_dx, int real_dy, int mode)
14427 {
14428   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14429   boolean player_was_pushing = player->is_pushing;
14430   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14431   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14432   int jx = oldx, jy = oldy;
14433   int dx = x - jx, dy = y - jy;
14434   int nextx = x + dx, nexty = y + dy;
14435   int move_direction = (dx == -1 ? MV_LEFT  :
14436                         dx == +1 ? MV_RIGHT :
14437                         dy == -1 ? MV_UP    :
14438                         dy == +1 ? MV_DOWN  : MV_NONE);
14439   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14440   int dig_side = MV_DIR_OPPOSITE(move_direction);
14441   int old_element = Tile[jx][jy];
14442   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14443   int collect_count;
14444
14445   if (is_player)                // function can also be called by EL_PENGUIN
14446   {
14447     if (player->MovPos == 0)
14448     {
14449       player->is_digging = FALSE;
14450       player->is_collecting = FALSE;
14451     }
14452
14453     if (player->MovPos == 0)    // last pushing move finished
14454       player->is_pushing = FALSE;
14455
14456     if (mode == DF_NO_PUSH)     // player just stopped pushing
14457     {
14458       player->is_switching = FALSE;
14459       player->push_delay = -1;
14460
14461       return MP_NO_ACTION;
14462     }
14463   }
14464   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14465     old_element = Back[jx][jy];
14466
14467   // in case of element dropped at player position, check background
14468   else if (Back[jx][jy] != EL_EMPTY &&
14469            game.engine_version >= VERSION_IDENT(2,2,0,0))
14470     old_element = Back[jx][jy];
14471
14472   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14473     return MP_NO_ACTION;        // field has no opening in this direction
14474
14475   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14476     return MP_NO_ACTION;        // field has no opening in this direction
14477
14478   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14479   {
14480     SplashAcid(x, y);
14481
14482     Tile[jx][jy] = player->artwork_element;
14483     InitMovingField(jx, jy, MV_DOWN);
14484     Store[jx][jy] = EL_ACID;
14485     ContinueMoving(jx, jy);
14486     BuryPlayer(player);
14487
14488     return MP_DONT_RUN_INTO;
14489   }
14490
14491   if (player_can_move && DONT_RUN_INTO(element))
14492   {
14493     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14494
14495     return MP_DONT_RUN_INTO;
14496   }
14497
14498   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14499     return MP_NO_ACTION;
14500
14501   collect_count = element_info[element].collect_count_initial;
14502
14503   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14504     return MP_NO_ACTION;
14505
14506   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14507     player_can_move = player_can_move_or_snap;
14508
14509   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14510       game.engine_version >= VERSION_IDENT(2,2,0,0))
14511   {
14512     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14513                                player->index_bit, dig_side);
14514     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14515                                         player->index_bit, dig_side);
14516
14517     if (element == EL_DC_LANDMINE)
14518       Bang(x, y);
14519
14520     if (Tile[x][y] != element)          // field changed by snapping
14521       return MP_ACTION;
14522
14523     return MP_NO_ACTION;
14524   }
14525
14526   if (player->gravity && is_player && !player->is_auto_moving &&
14527       canFallDown(player) && move_direction != MV_DOWN &&
14528       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14529     return MP_NO_ACTION;        // player cannot walk here due to gravity
14530
14531   if (player_can_move &&
14532       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14533   {
14534     int sound_element = SND_ELEMENT(element);
14535     int sound_action = ACTION_WALKING;
14536
14537     if (IS_RND_GATE(element))
14538     {
14539       if (!player->key[RND_GATE_NR(element)])
14540         return MP_NO_ACTION;
14541     }
14542     else if (IS_RND_GATE_GRAY(element))
14543     {
14544       if (!player->key[RND_GATE_GRAY_NR(element)])
14545         return MP_NO_ACTION;
14546     }
14547     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14548     {
14549       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14550         return MP_NO_ACTION;
14551     }
14552     else if (element == EL_EXIT_OPEN ||
14553              element == EL_EM_EXIT_OPEN ||
14554              element == EL_EM_EXIT_OPENING ||
14555              element == EL_STEEL_EXIT_OPEN ||
14556              element == EL_EM_STEEL_EXIT_OPEN ||
14557              element == EL_EM_STEEL_EXIT_OPENING ||
14558              element == EL_SP_EXIT_OPEN ||
14559              element == EL_SP_EXIT_OPENING)
14560     {
14561       sound_action = ACTION_PASSING;    // player is passing exit
14562     }
14563     else if (element == EL_EMPTY)
14564     {
14565       sound_action = ACTION_MOVING;             // nothing to walk on
14566     }
14567
14568     // play sound from background or player, whatever is available
14569     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14570       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14571     else
14572       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14573   }
14574   else if (player_can_move &&
14575            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14576   {
14577     if (!ACCESS_FROM(element, opposite_direction))
14578       return MP_NO_ACTION;      // field not accessible from this direction
14579
14580     if (CAN_MOVE(element))      // only fixed elements can be passed!
14581       return MP_NO_ACTION;
14582
14583     if (IS_EM_GATE(element))
14584     {
14585       if (!player->key[EM_GATE_NR(element)])
14586         return MP_NO_ACTION;
14587     }
14588     else if (IS_EM_GATE_GRAY(element))
14589     {
14590       if (!player->key[EM_GATE_GRAY_NR(element)])
14591         return MP_NO_ACTION;
14592     }
14593     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14594     {
14595       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14596         return MP_NO_ACTION;
14597     }
14598     else if (IS_EMC_GATE(element))
14599     {
14600       if (!player->key[EMC_GATE_NR(element)])
14601         return MP_NO_ACTION;
14602     }
14603     else if (IS_EMC_GATE_GRAY(element))
14604     {
14605       if (!player->key[EMC_GATE_GRAY_NR(element)])
14606         return MP_NO_ACTION;
14607     }
14608     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14609     {
14610       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14611         return MP_NO_ACTION;
14612     }
14613     else if (element == EL_DC_GATE_WHITE ||
14614              element == EL_DC_GATE_WHITE_GRAY ||
14615              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14616     {
14617       if (player->num_white_keys == 0)
14618         return MP_NO_ACTION;
14619
14620       player->num_white_keys--;
14621     }
14622     else if (IS_SP_PORT(element))
14623     {
14624       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14625           element == EL_SP_GRAVITY_PORT_RIGHT ||
14626           element == EL_SP_GRAVITY_PORT_UP ||
14627           element == EL_SP_GRAVITY_PORT_DOWN)
14628         player->gravity = !player->gravity;
14629       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14630                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14631                element == EL_SP_GRAVITY_ON_PORT_UP ||
14632                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14633         player->gravity = TRUE;
14634       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14635                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14636                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14637                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14638         player->gravity = FALSE;
14639     }
14640
14641     // automatically move to the next field with double speed
14642     player->programmed_action = move_direction;
14643
14644     if (player->move_delay_reset_counter == 0)
14645     {
14646       player->move_delay_reset_counter = 2;     // two double speed steps
14647
14648       DOUBLE_PLAYER_SPEED(player);
14649     }
14650
14651     PlayLevelSoundAction(x, y, ACTION_PASSING);
14652   }
14653   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14654   {
14655     RemoveField(x, y);
14656
14657     if (mode != DF_SNAP)
14658     {
14659       GfxElement[x][y] = GFX_ELEMENT(element);
14660       player->is_digging = TRUE;
14661     }
14662
14663     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14664
14665     // use old behaviour for old levels (digging)
14666     if (!level.finish_dig_collect)
14667     {
14668       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14669                                           player->index_bit, dig_side);
14670
14671       // if digging triggered player relocation, finish digging tile
14672       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14673         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14674     }
14675
14676     if (mode == DF_SNAP)
14677     {
14678       if (level.block_snap_field)
14679         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14680       else
14681         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14682
14683       // use old behaviour for old levels (snapping)
14684       if (!level.finish_dig_collect)
14685         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14686                                             player->index_bit, dig_side);
14687     }
14688   }
14689   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14690   {
14691     RemoveField(x, y);
14692
14693     if (is_player && mode != DF_SNAP)
14694     {
14695       GfxElement[x][y] = element;
14696       player->is_collecting = TRUE;
14697     }
14698
14699     if (element == EL_SPEED_PILL)
14700     {
14701       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14702     }
14703     else if (element == EL_EXTRA_TIME && level.time > 0)
14704     {
14705       TimeLeft += level.extra_time;
14706
14707       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14708
14709       DisplayGameControlValues();
14710     }
14711     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14712     {
14713       int shield_time = (element == EL_SHIELD_DEADLY ?
14714                          level.shield_deadly_time :
14715                          level.shield_normal_time);
14716
14717       player->shield_normal_time_left += shield_time;
14718       if (element == EL_SHIELD_DEADLY)
14719         player->shield_deadly_time_left += shield_time;
14720     }
14721     else if (element == EL_DYNAMITE ||
14722              element == EL_EM_DYNAMITE ||
14723              element == EL_SP_DISK_RED)
14724     {
14725       if (player->inventory_size < MAX_INVENTORY_SIZE)
14726         player->inventory_element[player->inventory_size++] = element;
14727
14728       DrawGameDoorValues();
14729     }
14730     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14731     {
14732       player->dynabomb_count++;
14733       player->dynabombs_left++;
14734     }
14735     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14736     {
14737       player->dynabomb_size++;
14738     }
14739     else if (element == EL_DYNABOMB_INCREASE_POWER)
14740     {
14741       player->dynabomb_xl = TRUE;
14742     }
14743     else if (IS_KEY(element))
14744     {
14745       player->key[KEY_NR(element)] = TRUE;
14746
14747       DrawGameDoorValues();
14748     }
14749     else if (element == EL_DC_KEY_WHITE)
14750     {
14751       player->num_white_keys++;
14752
14753       // display white keys?
14754       // DrawGameDoorValues();
14755     }
14756     else if (IS_ENVELOPE(element))
14757     {
14758       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14759
14760       if (!wait_for_snapping)
14761         player->show_envelope = element;
14762     }
14763     else if (element == EL_EMC_LENSES)
14764     {
14765       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14766
14767       RedrawAllInvisibleElementsForLenses();
14768     }
14769     else if (element == EL_EMC_MAGNIFIER)
14770     {
14771       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14772
14773       RedrawAllInvisibleElementsForMagnifier();
14774     }
14775     else if (IS_DROPPABLE(element) ||
14776              IS_THROWABLE(element))     // can be collected and dropped
14777     {
14778       int i;
14779
14780       if (collect_count == 0)
14781         player->inventory_infinite_element = element;
14782       else
14783         for (i = 0; i < collect_count; i++)
14784           if (player->inventory_size < MAX_INVENTORY_SIZE)
14785             player->inventory_element[player->inventory_size++] = element;
14786
14787       DrawGameDoorValues();
14788     }
14789     else if (collect_count > 0)
14790     {
14791       game.gems_still_needed -= collect_count;
14792       if (game.gems_still_needed < 0)
14793         game.gems_still_needed = 0;
14794
14795       game.snapshot.collected_item = TRUE;
14796
14797       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14798
14799       DisplayGameControlValues();
14800     }
14801
14802     RaiseScoreElement(element);
14803     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14804
14805     // use old behaviour for old levels (collecting)
14806     if (!level.finish_dig_collect && is_player)
14807     {
14808       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14809                                           player->index_bit, dig_side);
14810
14811       // if collecting triggered player relocation, finish collecting tile
14812       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14813         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14814     }
14815
14816     if (mode == DF_SNAP)
14817     {
14818       if (level.block_snap_field)
14819         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14820       else
14821         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14822
14823       // use old behaviour for old levels (snapping)
14824       if (!level.finish_dig_collect)
14825         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14826                                             player->index_bit, dig_side);
14827     }
14828   }
14829   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14830   {
14831     if (mode == DF_SNAP && element != EL_BD_ROCK)
14832       return MP_NO_ACTION;
14833
14834     if (CAN_FALL(element) && dy)
14835       return MP_NO_ACTION;
14836
14837     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14838         !(element == EL_SPRING && level.use_spring_bug))
14839       return MP_NO_ACTION;
14840
14841     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14842         ((move_direction & MV_VERTICAL &&
14843           ((element_info[element].move_pattern & MV_LEFT &&
14844             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14845            (element_info[element].move_pattern & MV_RIGHT &&
14846             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14847          (move_direction & MV_HORIZONTAL &&
14848           ((element_info[element].move_pattern & MV_UP &&
14849             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14850            (element_info[element].move_pattern & MV_DOWN &&
14851             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14852       return MP_NO_ACTION;
14853
14854     // do not push elements already moving away faster than player
14855     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14856         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14857       return MP_NO_ACTION;
14858
14859     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14860     {
14861       if (player->push_delay_value == -1 || !player_was_pushing)
14862         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14863     }
14864     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14865     {
14866       if (player->push_delay_value == -1)
14867         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14868     }
14869     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14870     {
14871       if (!player->is_pushing)
14872         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14873     }
14874
14875     player->is_pushing = TRUE;
14876     player->is_active = TRUE;
14877
14878     if (!(IN_LEV_FIELD(nextx, nexty) &&
14879           (IS_FREE(nextx, nexty) ||
14880            (IS_SB_ELEMENT(element) &&
14881             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14882            (IS_CUSTOM_ELEMENT(element) &&
14883             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14884       return MP_NO_ACTION;
14885
14886     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14887       return MP_NO_ACTION;
14888
14889     if (player->push_delay == -1)       // new pushing; restart delay
14890       player->push_delay = 0;
14891
14892     if (player->push_delay < player->push_delay_value &&
14893         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14894         element != EL_SPRING && element != EL_BALLOON)
14895     {
14896       // make sure that there is no move delay before next try to push
14897       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14898         player->move_delay = 0;
14899
14900       return MP_NO_ACTION;
14901     }
14902
14903     if (IS_CUSTOM_ELEMENT(element) &&
14904         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14905     {
14906       if (!DigFieldByCE(nextx, nexty, element))
14907         return MP_NO_ACTION;
14908     }
14909
14910     if (IS_SB_ELEMENT(element))
14911     {
14912       boolean sokoban_task_solved = FALSE;
14913
14914       if (element == EL_SOKOBAN_FIELD_FULL)
14915       {
14916         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14917
14918         IncrementSokobanFieldsNeeded();
14919         IncrementSokobanObjectsNeeded();
14920       }
14921
14922       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14923       {
14924         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14925
14926         DecrementSokobanFieldsNeeded();
14927         DecrementSokobanObjectsNeeded();
14928
14929         // sokoban object was pushed from empty field to sokoban field
14930         if (Back[x][y] == EL_EMPTY)
14931           sokoban_task_solved = TRUE;
14932       }
14933
14934       Tile[x][y] = EL_SOKOBAN_OBJECT;
14935
14936       if (Back[x][y] == Back[nextx][nexty])
14937         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14938       else if (Back[x][y] != 0)
14939         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14940                                     ACTION_EMPTYING);
14941       else
14942         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14943                                     ACTION_FILLING);
14944
14945       if (sokoban_task_solved &&
14946           game.sokoban_fields_still_needed == 0 &&
14947           game.sokoban_objects_still_needed == 0 &&
14948           level.auto_exit_sokoban)
14949       {
14950         game.players_still_needed = 0;
14951
14952         LevelSolved();
14953
14954         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14955       }
14956     }
14957     else
14958       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14959
14960     InitMovingField(x, y, move_direction);
14961     GfxAction[x][y] = ACTION_PUSHING;
14962
14963     if (mode == DF_SNAP)
14964       ContinueMoving(x, y);
14965     else
14966       MovPos[x][y] = (dx != 0 ? dx : dy);
14967
14968     Pushed[x][y] = TRUE;
14969     Pushed[nextx][nexty] = TRUE;
14970
14971     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14972       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14973     else
14974       player->push_delay_value = -1;    // get new value later
14975
14976     // check for element change _after_ element has been pushed
14977     if (game.use_change_when_pushing_bug)
14978     {
14979       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14980                                  player->index_bit, dig_side);
14981       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14982                                           player->index_bit, dig_side);
14983     }
14984   }
14985   else if (IS_SWITCHABLE(element))
14986   {
14987     if (PLAYER_SWITCHING(player, x, y))
14988     {
14989       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14990                                           player->index_bit, dig_side);
14991
14992       return MP_ACTION;
14993     }
14994
14995     player->is_switching = TRUE;
14996     player->switch_x = x;
14997     player->switch_y = y;
14998
14999     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15000
15001     if (element == EL_ROBOT_WHEEL)
15002     {
15003       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15004
15005       game.robot_wheel_x = x;
15006       game.robot_wheel_y = y;
15007       game.robot_wheel_active = TRUE;
15008
15009       TEST_DrawLevelField(x, y);
15010     }
15011     else if (element == EL_SP_TERMINAL)
15012     {
15013       int xx, yy;
15014
15015       SCAN_PLAYFIELD(xx, yy)
15016       {
15017         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
15018         {
15019           Bang(xx, yy);
15020         }
15021         else if (Tile[xx][yy] == EL_SP_TERMINAL)
15022         {
15023           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15024
15025           ResetGfxAnimation(xx, yy);
15026           TEST_DrawLevelField(xx, yy);
15027         }
15028       }
15029     }
15030     else if (IS_BELT_SWITCH(element))
15031     {
15032       ToggleBeltSwitch(x, y);
15033     }
15034     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15035              element == EL_SWITCHGATE_SWITCH_DOWN ||
15036              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15037              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15038     {
15039       ToggleSwitchgateSwitch();
15040     }
15041     else if (element == EL_LIGHT_SWITCH ||
15042              element == EL_LIGHT_SWITCH_ACTIVE)
15043     {
15044       ToggleLightSwitch(x, y);
15045     }
15046     else if (element == EL_TIMEGATE_SWITCH ||
15047              element == EL_DC_TIMEGATE_SWITCH)
15048     {
15049       ActivateTimegateSwitch(x, y);
15050     }
15051     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15052              element == EL_BALLOON_SWITCH_RIGHT ||
15053              element == EL_BALLOON_SWITCH_UP    ||
15054              element == EL_BALLOON_SWITCH_DOWN  ||
15055              element == EL_BALLOON_SWITCH_NONE  ||
15056              element == EL_BALLOON_SWITCH_ANY)
15057     {
15058       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15059                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15060                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15061                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15062                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15063                              move_direction);
15064     }
15065     else if (element == EL_LAMP)
15066     {
15067       Tile[x][y] = EL_LAMP_ACTIVE;
15068       game.lights_still_needed--;
15069
15070       ResetGfxAnimation(x, y);
15071       TEST_DrawLevelField(x, y);
15072     }
15073     else if (element == EL_TIME_ORB_FULL)
15074     {
15075       Tile[x][y] = EL_TIME_ORB_EMPTY;
15076
15077       if (level.time > 0 || level.use_time_orb_bug)
15078       {
15079         TimeLeft += level.time_orb_time;
15080         game.no_level_time_limit = FALSE;
15081
15082         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15083
15084         DisplayGameControlValues();
15085       }
15086
15087       ResetGfxAnimation(x, y);
15088       TEST_DrawLevelField(x, y);
15089     }
15090     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15091              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15092     {
15093       int xx, yy;
15094
15095       game.ball_active = !game.ball_active;
15096
15097       SCAN_PLAYFIELD(xx, yy)
15098       {
15099         int e = Tile[xx][yy];
15100
15101         if (game.ball_active)
15102         {
15103           if (e == EL_EMC_MAGIC_BALL)
15104             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15105           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15106             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15107         }
15108         else
15109         {
15110           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15111             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15112           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15113             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15114         }
15115       }
15116     }
15117
15118     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15119                                         player->index_bit, dig_side);
15120
15121     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15122                                         player->index_bit, dig_side);
15123
15124     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15125                                         player->index_bit, dig_side);
15126
15127     return MP_ACTION;
15128   }
15129   else
15130   {
15131     if (!PLAYER_SWITCHING(player, x, y))
15132     {
15133       player->is_switching = TRUE;
15134       player->switch_x = x;
15135       player->switch_y = y;
15136
15137       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15138                                  player->index_bit, dig_side);
15139       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15140                                           player->index_bit, dig_side);
15141
15142       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15143                                  player->index_bit, dig_side);
15144       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15145                                           player->index_bit, dig_side);
15146     }
15147
15148     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15149                                player->index_bit, dig_side);
15150     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15151                                         player->index_bit, dig_side);
15152
15153     return MP_NO_ACTION;
15154   }
15155
15156   player->push_delay = -1;
15157
15158   if (is_player)                // function can also be called by EL_PENGUIN
15159   {
15160     if (Tile[x][y] != element)          // really digged/collected something
15161     {
15162       player->is_collecting = !player->is_digging;
15163       player->is_active = TRUE;
15164
15165       player->last_removed_element = element;
15166     }
15167   }
15168
15169   return MP_MOVING;
15170 }
15171
15172 static boolean DigFieldByCE(int x, int y, int digging_element)
15173 {
15174   int element = Tile[x][y];
15175
15176   if (!IS_FREE(x, y))
15177   {
15178     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15179                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15180                   ACTION_BREAKING);
15181
15182     // no element can dig solid indestructible elements
15183     if (IS_INDESTRUCTIBLE(element) &&
15184         !IS_DIGGABLE(element) &&
15185         !IS_COLLECTIBLE(element))
15186       return FALSE;
15187
15188     if (AmoebaNr[x][y] &&
15189         (element == EL_AMOEBA_FULL ||
15190          element == EL_BD_AMOEBA ||
15191          element == EL_AMOEBA_GROWING))
15192     {
15193       AmoebaCnt[AmoebaNr[x][y]]--;
15194       AmoebaCnt2[AmoebaNr[x][y]]--;
15195     }
15196
15197     if (IS_MOVING(x, y))
15198       RemoveMovingField(x, y);
15199     else
15200     {
15201       RemoveField(x, y);
15202       TEST_DrawLevelField(x, y);
15203     }
15204
15205     // if digged element was about to explode, prevent the explosion
15206     ExplodeField[x][y] = EX_TYPE_NONE;
15207
15208     PlayLevelSoundAction(x, y, action);
15209   }
15210
15211   Store[x][y] = EL_EMPTY;
15212
15213   // this makes it possible to leave the removed element again
15214   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15215     Store[x][y] = element;
15216
15217   return TRUE;
15218 }
15219
15220 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15221 {
15222   int jx = player->jx, jy = player->jy;
15223   int x = jx + dx, y = jy + dy;
15224   int snap_direction = (dx == -1 ? MV_LEFT  :
15225                         dx == +1 ? MV_RIGHT :
15226                         dy == -1 ? MV_UP    :
15227                         dy == +1 ? MV_DOWN  : MV_NONE);
15228   boolean can_continue_snapping = (level.continuous_snapping &&
15229                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15230
15231   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15232     return FALSE;
15233
15234   if (!player->active || !IN_LEV_FIELD(x, y))
15235     return FALSE;
15236
15237   if (dx && dy)
15238     return FALSE;
15239
15240   if (!dx && !dy)
15241   {
15242     if (player->MovPos == 0)
15243       player->is_pushing = FALSE;
15244
15245     player->is_snapping = FALSE;
15246
15247     if (player->MovPos == 0)
15248     {
15249       player->is_moving = FALSE;
15250       player->is_digging = FALSE;
15251       player->is_collecting = FALSE;
15252     }
15253
15254     return FALSE;
15255   }
15256
15257   // prevent snapping with already pressed snap key when not allowed
15258   if (player->is_snapping && !can_continue_snapping)
15259     return FALSE;
15260
15261   player->MovDir = snap_direction;
15262
15263   if (player->MovPos == 0)
15264   {
15265     player->is_moving = FALSE;
15266     player->is_digging = FALSE;
15267     player->is_collecting = FALSE;
15268   }
15269
15270   player->is_dropping = FALSE;
15271   player->is_dropping_pressed = FALSE;
15272   player->drop_pressed_delay = 0;
15273
15274   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15275     return FALSE;
15276
15277   player->is_snapping = TRUE;
15278   player->is_active = TRUE;
15279
15280   if (player->MovPos == 0)
15281   {
15282     player->is_moving = FALSE;
15283     player->is_digging = FALSE;
15284     player->is_collecting = FALSE;
15285   }
15286
15287   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15288     TEST_DrawLevelField(player->last_jx, player->last_jy);
15289
15290   TEST_DrawLevelField(x, y);
15291
15292   return TRUE;
15293 }
15294
15295 static boolean DropElement(struct PlayerInfo *player)
15296 {
15297   int old_element, new_element;
15298   int dropx = player->jx, dropy = player->jy;
15299   int drop_direction = player->MovDir;
15300   int drop_side = drop_direction;
15301   int drop_element = get_next_dropped_element(player);
15302
15303   /* do not drop an element on top of another element; when holding drop key
15304      pressed without moving, dropped element must move away before the next
15305      element can be dropped (this is especially important if the next element
15306      is dynamite, which can be placed on background for historical reasons) */
15307   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15308     return MP_ACTION;
15309
15310   if (IS_THROWABLE(drop_element))
15311   {
15312     dropx += GET_DX_FROM_DIR(drop_direction);
15313     dropy += GET_DY_FROM_DIR(drop_direction);
15314
15315     if (!IN_LEV_FIELD(dropx, dropy))
15316       return FALSE;
15317   }
15318
15319   old_element = Tile[dropx][dropy];     // old element at dropping position
15320   new_element = drop_element;           // default: no change when dropping
15321
15322   // check if player is active, not moving and ready to drop
15323   if (!player->active || player->MovPos || player->drop_delay > 0)
15324     return FALSE;
15325
15326   // check if player has anything that can be dropped
15327   if (new_element == EL_UNDEFINED)
15328     return FALSE;
15329
15330   // only set if player has anything that can be dropped
15331   player->is_dropping_pressed = TRUE;
15332
15333   // check if drop key was pressed long enough for EM style dynamite
15334   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15335     return FALSE;
15336
15337   // check if anything can be dropped at the current position
15338   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15339     return FALSE;
15340
15341   // collected custom elements can only be dropped on empty fields
15342   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15343     return FALSE;
15344
15345   if (old_element != EL_EMPTY)
15346     Back[dropx][dropy] = old_element;   // store old element on this field
15347
15348   ResetGfxAnimation(dropx, dropy);
15349   ResetRandomAnimationValue(dropx, dropy);
15350
15351   if (player->inventory_size > 0 ||
15352       player->inventory_infinite_element != EL_UNDEFINED)
15353   {
15354     if (player->inventory_size > 0)
15355     {
15356       player->inventory_size--;
15357
15358       DrawGameDoorValues();
15359
15360       if (new_element == EL_DYNAMITE)
15361         new_element = EL_DYNAMITE_ACTIVE;
15362       else if (new_element == EL_EM_DYNAMITE)
15363         new_element = EL_EM_DYNAMITE_ACTIVE;
15364       else if (new_element == EL_SP_DISK_RED)
15365         new_element = EL_SP_DISK_RED_ACTIVE;
15366     }
15367
15368     Tile[dropx][dropy] = new_element;
15369
15370     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15371       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15372                           el2img(Tile[dropx][dropy]), 0);
15373
15374     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15375
15376     // needed if previous element just changed to "empty" in the last frame
15377     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15378
15379     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15380                                player->index_bit, drop_side);
15381     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15382                                         CE_PLAYER_DROPS_X,
15383                                         player->index_bit, drop_side);
15384
15385     TestIfElementTouchesCustomElement(dropx, dropy);
15386   }
15387   else          // player is dropping a dyna bomb
15388   {
15389     player->dynabombs_left--;
15390
15391     Tile[dropx][dropy] = new_element;
15392
15393     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15394       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15395                           el2img(Tile[dropx][dropy]), 0);
15396
15397     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15398   }
15399
15400   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15401     InitField_WithBug1(dropx, dropy, FALSE);
15402
15403   new_element = Tile[dropx][dropy];     // element might have changed
15404
15405   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15406       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15407   {
15408     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15409       MovDir[dropx][dropy] = drop_direction;
15410
15411     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15412
15413     // do not cause impact style collision by dropping elements that can fall
15414     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15415   }
15416
15417   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15418   player->is_dropping = TRUE;
15419
15420   player->drop_pressed_delay = 0;
15421   player->is_dropping_pressed = FALSE;
15422
15423   player->drop_x = dropx;
15424   player->drop_y = dropy;
15425
15426   return TRUE;
15427 }
15428
15429 // ----------------------------------------------------------------------------
15430 // game sound playing functions
15431 // ----------------------------------------------------------------------------
15432
15433 static int *loop_sound_frame = NULL;
15434 static int *loop_sound_volume = NULL;
15435
15436 void InitPlayLevelSound(void)
15437 {
15438   int num_sounds = getSoundListSize();
15439
15440   checked_free(loop_sound_frame);
15441   checked_free(loop_sound_volume);
15442
15443   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15444   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15445 }
15446
15447 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15448 {
15449   int sx = SCREENX(x), sy = SCREENY(y);
15450   int volume, stereo_position;
15451   int max_distance = 8;
15452   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15453
15454   if ((!setup.sound_simple && !is_loop_sound) ||
15455       (!setup.sound_loops && is_loop_sound))
15456     return;
15457
15458   if (!IN_LEV_FIELD(x, y) ||
15459       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15460       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15461     return;
15462
15463   volume = SOUND_MAX_VOLUME;
15464
15465   if (!IN_SCR_FIELD(sx, sy))
15466   {
15467     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15468     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15469
15470     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15471   }
15472
15473   stereo_position = (SOUND_MAX_LEFT +
15474                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15475                      (SCR_FIELDX + 2 * max_distance));
15476
15477   if (is_loop_sound)
15478   {
15479     /* This assures that quieter loop sounds do not overwrite louder ones,
15480        while restarting sound volume comparison with each new game frame. */
15481
15482     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15483       return;
15484
15485     loop_sound_volume[nr] = volume;
15486     loop_sound_frame[nr] = FrameCounter;
15487   }
15488
15489   PlaySoundExt(nr, volume, stereo_position, type);
15490 }
15491
15492 static void PlayLevelSound(int x, int y, int nr)
15493 {
15494   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15495 }
15496
15497 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15498 {
15499   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15500                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15501                  y < LEVELY(BY1) ? LEVELY(BY1) :
15502                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15503                  sound_action);
15504 }
15505
15506 static void PlayLevelSoundAction(int x, int y, int action)
15507 {
15508   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15509 }
15510
15511 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15512 {
15513   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15514
15515   if (sound_effect != SND_UNDEFINED)
15516     PlayLevelSound(x, y, sound_effect);
15517 }
15518
15519 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15520                                               int action)
15521 {
15522   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15523
15524   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15525     PlayLevelSound(x, y, sound_effect);
15526 }
15527
15528 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15529 {
15530   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15531
15532   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15533     PlayLevelSound(x, y, sound_effect);
15534 }
15535
15536 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15537 {
15538   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15539
15540   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15541     StopSound(sound_effect);
15542 }
15543
15544 static int getLevelMusicNr(void)
15545 {
15546   int level_pos = level_nr - leveldir_current->first_level;
15547
15548   if (levelset.music[level_nr] != MUS_UNDEFINED)
15549     return levelset.music[level_nr];            // from config file
15550   else
15551     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15552 }
15553
15554 static void FadeLevelSounds(void)
15555 {
15556   FadeSounds();
15557 }
15558
15559 static void FadeLevelMusic(void)
15560 {
15561   int music_nr = getLevelMusicNr();
15562   char *curr_music = getCurrentlyPlayingMusicFilename();
15563   char *next_music = getMusicInfoEntryFilename(music_nr);
15564
15565   if (!strEqual(curr_music, next_music))
15566     FadeMusic();
15567 }
15568
15569 void FadeLevelSoundsAndMusic(void)
15570 {
15571   FadeLevelSounds();
15572   FadeLevelMusic();
15573 }
15574
15575 static void PlayLevelMusic(void)
15576 {
15577   int music_nr = getLevelMusicNr();
15578   char *curr_music = getCurrentlyPlayingMusicFilename();
15579   char *next_music = getMusicInfoEntryFilename(music_nr);
15580
15581   if (!strEqual(curr_music, next_music))
15582     PlayMusicLoop(music_nr);
15583 }
15584
15585 static int getSoundAction_BD(int sample)
15586 {
15587   switch (sample)
15588   {
15589     case GD_S_STONE_PUSHING:
15590     case GD_S_MEGA_STONE_PUSHING:
15591     case GD_S_FLYING_STONE_PUSHING:
15592     case GD_S_WAITING_STONE_PUSHING:
15593     case GD_S_CHASING_STONE_PUSHING:
15594     case GD_S_NUT_PUSHING:
15595     case GD_S_NITRO_PACK_PUSHING:
15596     case GD_S_BLADDER_PUSHING:
15597     case GD_S_BOX_PUSHING:
15598       return ACTION_PUSHING;
15599
15600     case GD_S_STONE_FALLING:
15601     case GD_S_MEGA_STONE_FALLING:
15602     case GD_S_FLYING_STONE_FALLING:
15603     case GD_S_NUT_FALLING:
15604     case GD_S_DIRT_BALL_FALLING:
15605     case GD_S_DIRT_LOOSE_FALLING:
15606     case GD_S_NITRO_PACK_FALLING:
15607     case GD_S_FALLING_WALL_FALLING:
15608       return ACTION_FALLING;
15609
15610     case GD_S_STONE_IMPACT:
15611     case GD_S_MEGA_STONE_IMPACT:
15612     case GD_S_FLYING_STONE_IMPACT:
15613     case GD_S_NUT_IMPACT:
15614     case GD_S_DIRT_BALL_IMPACT:
15615     case GD_S_DIRT_LOOSE_IMPACT:
15616     case GD_S_NITRO_PACK_IMPACT:
15617     case GD_S_FALLING_WALL_IMPACT:
15618       return ACTION_IMPACT;
15619
15620     case GD_S_NUT_CRACKING:
15621       return ACTION_BREAKING;
15622
15623     case GD_S_EXPANDING_WALL:
15624     case GD_S_WALL_REAPPEARING:
15625     case GD_S_SLIME:
15626     case GD_S_LAVA:
15627     case GD_S_ACID_SPREADING:
15628       return ACTION_GROWING;
15629
15630     case GD_S_DIAMOND_COLLECTING:
15631     case GD_S_FLYING_DIAMOND_COLLECTING:
15632     case GD_S_SKELETON_COLLECTING:
15633     case GD_S_PNEUMATIC_COLLECTING:
15634     case GD_S_BOMB_COLLECTING:
15635     case GD_S_CLOCK_COLLECTING:
15636     case GD_S_SWEET_COLLECTING:
15637     case GD_S_KEY_COLLECTING:
15638     case GD_S_DIAMOND_KEY_COLLECTING:
15639       return ACTION_COLLECTING;
15640
15641     case GD_S_BOMB_PLACING:
15642     case GD_S_REPLICATOR:
15643       return ACTION_DROPPING;
15644
15645     case GD_S_BLADDER_MOVING:
15646       return ACTION_MOVING;
15647
15648     case GD_S_BLADDER_SPENDER:
15649     case GD_S_BLADDER_CONVERTING:
15650     case GD_S_GRAVITY_CHANGING:
15651       return ACTION_CHANGING;
15652
15653     case GD_S_BITER_EATING:
15654       return ACTION_EATING;
15655
15656     case GD_S_DOOR_OPENING:
15657     case GD_S_CRACKING:
15658       return ACTION_OPENING;
15659
15660     case GD_S_DIRT_WALKING:
15661       return ACTION_DIGGING;
15662
15663     case GD_S_EMPTY_WALKING:
15664       return ACTION_WALKING;
15665
15666     case GD_S_SWITCH_BITER:
15667     case GD_S_SWITCH_CREATURES:
15668     case GD_S_SWITCH_GRAVITY:
15669     case GD_S_SWITCH_EXPANDING:
15670     case GD_S_SWITCH_CONVEYOR:
15671     case GD_S_SWITCH_REPLICATOR:
15672     case GD_S_STIRRING:
15673       return ACTION_ACTIVATING;
15674
15675     case GD_S_TELEPORTER:
15676       return ACTION_PASSING;
15677
15678     case GD_S_EXPLODING:
15679     case GD_S_BOMB_EXPLODING:
15680     case GD_S_GHOST_EXPLODING:
15681     case GD_S_VOODOO_EXPLODING:
15682     case GD_S_NITRO_PACK_EXPLODING:
15683       return ACTION_EXPLODING;
15684
15685     case GD_S_COVERING:
15686     case GD_S_AMOEBA:
15687     case GD_S_MAGIC_WALL:
15688     case GD_S_PNEUMATIC_HAMMER:
15689     case GD_S_WATER:
15690       return ACTION_ACTIVE;
15691
15692     case GD_S_DIAMOND_FALLING_RANDOM:
15693     case GD_S_DIAMOND_FALLING_1:
15694     case GD_S_DIAMOND_FALLING_2:
15695     case GD_S_DIAMOND_FALLING_3:
15696     case GD_S_DIAMOND_FALLING_4:
15697     case GD_S_DIAMOND_FALLING_5:
15698     case GD_S_DIAMOND_FALLING_6:
15699     case GD_S_DIAMOND_FALLING_7:
15700     case GD_S_DIAMOND_FALLING_8:
15701     case GD_S_DIAMOND_IMPACT_RANDOM:
15702     case GD_S_DIAMOND_IMPACT_1:
15703     case GD_S_DIAMOND_IMPACT_2:
15704     case GD_S_DIAMOND_IMPACT_3:
15705     case GD_S_DIAMOND_IMPACT_4:
15706     case GD_S_DIAMOND_IMPACT_5:
15707     case GD_S_DIAMOND_IMPACT_6:
15708     case GD_S_DIAMOND_IMPACT_7:
15709     case GD_S_DIAMOND_IMPACT_8:
15710     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15711     case GD_S_FLYING_DIAMOND_FALLING_1:
15712     case GD_S_FLYING_DIAMOND_FALLING_2:
15713     case GD_S_FLYING_DIAMOND_FALLING_3:
15714     case GD_S_FLYING_DIAMOND_FALLING_4:
15715     case GD_S_FLYING_DIAMOND_FALLING_5:
15716     case GD_S_FLYING_DIAMOND_FALLING_6:
15717     case GD_S_FLYING_DIAMOND_FALLING_7:
15718     case GD_S_FLYING_DIAMOND_FALLING_8:
15719     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15720     case GD_S_FLYING_DIAMOND_IMPACT_1:
15721     case GD_S_FLYING_DIAMOND_IMPACT_2:
15722     case GD_S_FLYING_DIAMOND_IMPACT_3:
15723     case GD_S_FLYING_DIAMOND_IMPACT_4:
15724     case GD_S_FLYING_DIAMOND_IMPACT_5:
15725     case GD_S_FLYING_DIAMOND_IMPACT_6:
15726     case GD_S_FLYING_DIAMOND_IMPACT_7:
15727     case GD_S_FLYING_DIAMOND_IMPACT_8:
15728     case GD_S_TIMEOUT_0:
15729     case GD_S_TIMEOUT_1:
15730     case GD_S_TIMEOUT_2:
15731     case GD_S_TIMEOUT_3:
15732     case GD_S_TIMEOUT_4:
15733     case GD_S_TIMEOUT_5:
15734     case GD_S_TIMEOUT_6:
15735     case GD_S_TIMEOUT_7:
15736     case GD_S_TIMEOUT_8:
15737     case GD_S_TIMEOUT_9:
15738     case GD_S_TIMEOUT_10:
15739     case GD_S_BONUS_LIFE:
15740       // trigger special post-processing (and force sound to be non-looping)
15741       return ACTION_OTHER;
15742
15743     case GD_S_AMOEBA_MAGIC:
15744     case GD_S_FINISHED:
15745       // trigger special post-processing (and force sound to be looping)
15746       return ACTION_DEFAULT;
15747
15748     default:
15749       return ACTION_DEFAULT;
15750   }
15751 }
15752
15753 static int getSoundEffect_BD(int element_bd, int sample)
15754 {
15755   int sound_action = getSoundAction_BD(sample);
15756   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15757   int nr;
15758
15759   // standard sounds
15760   if (sound_action != ACTION_OTHER &&
15761       sound_action != ACTION_DEFAULT)
15762     return sound_effect;
15763
15764   // special post-processing for some sounds
15765   switch (sample)
15766   {
15767     case GD_S_DIAMOND_FALLING_RANDOM:
15768     case GD_S_DIAMOND_FALLING_1:
15769     case GD_S_DIAMOND_FALLING_2:
15770     case GD_S_DIAMOND_FALLING_3:
15771     case GD_S_DIAMOND_FALLING_4:
15772     case GD_S_DIAMOND_FALLING_5:
15773     case GD_S_DIAMOND_FALLING_6:
15774     case GD_S_DIAMOND_FALLING_7:
15775     case GD_S_DIAMOND_FALLING_8:
15776       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15777             sample - GD_S_DIAMOND_FALLING_1);
15778       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15779
15780       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15781         sound_effect = SND_BD_DIAMOND_FALLING;
15782       break;
15783
15784     case GD_S_DIAMOND_IMPACT_RANDOM:
15785     case GD_S_DIAMOND_IMPACT_1:
15786     case GD_S_DIAMOND_IMPACT_2:
15787     case GD_S_DIAMOND_IMPACT_3:
15788     case GD_S_DIAMOND_IMPACT_4:
15789     case GD_S_DIAMOND_IMPACT_5:
15790     case GD_S_DIAMOND_IMPACT_6:
15791     case GD_S_DIAMOND_IMPACT_7:
15792     case GD_S_DIAMOND_IMPACT_8:
15793       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15794             sample - GD_S_DIAMOND_IMPACT_1);
15795       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15796
15797       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15798         sound_effect = SND_BD_DIAMOND_IMPACT;
15799       break;
15800
15801     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15802     case GD_S_FLYING_DIAMOND_FALLING_1:
15803     case GD_S_FLYING_DIAMOND_FALLING_2:
15804     case GD_S_FLYING_DIAMOND_FALLING_3:
15805     case GD_S_FLYING_DIAMOND_FALLING_4:
15806     case GD_S_FLYING_DIAMOND_FALLING_5:
15807     case GD_S_FLYING_DIAMOND_FALLING_6:
15808     case GD_S_FLYING_DIAMOND_FALLING_7:
15809     case GD_S_FLYING_DIAMOND_FALLING_8:
15810       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15811             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15812       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15813
15814       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15815         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15816       break;
15817
15818     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15819     case GD_S_FLYING_DIAMOND_IMPACT_1:
15820     case GD_S_FLYING_DIAMOND_IMPACT_2:
15821     case GD_S_FLYING_DIAMOND_IMPACT_3:
15822     case GD_S_FLYING_DIAMOND_IMPACT_4:
15823     case GD_S_FLYING_DIAMOND_IMPACT_5:
15824     case GD_S_FLYING_DIAMOND_IMPACT_6:
15825     case GD_S_FLYING_DIAMOND_IMPACT_7:
15826     case GD_S_FLYING_DIAMOND_IMPACT_8:
15827       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15828             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15829       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15830
15831       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15832         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15833       break;
15834
15835     case GD_S_TIMEOUT_0:
15836     case GD_S_TIMEOUT_1:
15837     case GD_S_TIMEOUT_2:
15838     case GD_S_TIMEOUT_3:
15839     case GD_S_TIMEOUT_4:
15840     case GD_S_TIMEOUT_5:
15841     case GD_S_TIMEOUT_6:
15842     case GD_S_TIMEOUT_7:
15843     case GD_S_TIMEOUT_8:
15844     case GD_S_TIMEOUT_9:
15845     case GD_S_TIMEOUT_10:
15846       nr = sample - GD_S_TIMEOUT_0;
15847       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15848
15849       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15850         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15851       break;
15852
15853     case GD_S_BONUS_LIFE:
15854       sound_effect = SND_GAME_HEALTH_BONUS;
15855       break;
15856
15857     case GD_S_AMOEBA_MAGIC:
15858       sound_effect = SND_BD_AMOEBA_OTHER;
15859       break;
15860
15861     case GD_S_FINISHED:
15862       sound_effect = SND_GAME_LEVELTIME_BONUS;
15863       break;
15864
15865     default:
15866       sound_effect = SND_UNDEFINED;
15867       break;
15868   }
15869
15870   return sound_effect;
15871 }
15872
15873 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15874 {
15875   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15876   int sound_effect = getSoundEffect_BD(element, sample);
15877   int sound_action = getSoundAction_BD(sample);
15878   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15879   int offset = 0;
15880   int x = xx - offset;
15881   int y = yy - offset;
15882
15883   // some sound actions are always looping in native BD game engine
15884   if (sound_action == ACTION_DEFAULT)
15885     is_loop_sound = TRUE;
15886
15887   // some sound actions are always non-looping in native BD game engine
15888   if (sound_action == ACTION_FALLING ||
15889       sound_action == ACTION_MOVING ||
15890       sound_action == ACTION_OTHER)
15891     is_loop_sound = FALSE;
15892
15893   if (sound_effect != SND_UNDEFINED)
15894     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15895 }
15896
15897 void StopSound_BD(int element_bd, int sample)
15898 {
15899   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15900   int sound_effect = getSoundEffect_BD(element, sample);
15901
15902   if (sound_effect != SND_UNDEFINED)
15903     StopSound(sound_effect);
15904 }
15905
15906 boolean isSoundPlaying_BD(int element_bd, int sample)
15907 {
15908   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15909   int sound_effect = getSoundEffect_BD(element, sample);
15910
15911   if (sound_effect != SND_UNDEFINED)
15912     return isSoundPlaying(sound_effect);
15913
15914   return FALSE;
15915 }
15916
15917 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15918 {
15919   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15920   int offset = 0;
15921   int x = xx - offset;
15922   int y = yy - offset;
15923
15924   switch (sample)
15925   {
15926     case SOUND_blank:
15927       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15928       break;
15929
15930     case SOUND_roll:
15931       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15932       break;
15933
15934     case SOUND_stone:
15935       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15936       break;
15937
15938     case SOUND_nut:
15939       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15940       break;
15941
15942     case SOUND_crack:
15943       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15944       break;
15945
15946     case SOUND_bug:
15947       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15948       break;
15949
15950     case SOUND_tank:
15951       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15952       break;
15953
15954     case SOUND_android_clone:
15955       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15956       break;
15957
15958     case SOUND_android_move:
15959       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15960       break;
15961
15962     case SOUND_spring:
15963       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15964       break;
15965
15966     case SOUND_slurp:
15967       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15968       break;
15969
15970     case SOUND_eater:
15971       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15972       break;
15973
15974     case SOUND_eater_eat:
15975       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15976       break;
15977
15978     case SOUND_alien:
15979       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15980       break;
15981
15982     case SOUND_collect:
15983       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15984       break;
15985
15986     case SOUND_diamond:
15987       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15988       break;
15989
15990     case SOUND_squash:
15991       // !!! CHECK THIS !!!
15992 #if 1
15993       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15994 #else
15995       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15996 #endif
15997       break;
15998
15999     case SOUND_wonderfall:
16000       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16001       break;
16002
16003     case SOUND_drip:
16004       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16005       break;
16006
16007     case SOUND_push:
16008       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16009       break;
16010
16011     case SOUND_dirt:
16012       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16013       break;
16014
16015     case SOUND_acid:
16016       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16017       break;
16018
16019     case SOUND_ball:
16020       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16021       break;
16022
16023     case SOUND_slide:
16024       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16025       break;
16026
16027     case SOUND_wonder:
16028       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16029       break;
16030
16031     case SOUND_door:
16032       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16033       break;
16034
16035     case SOUND_exit_open:
16036       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16037       break;
16038
16039     case SOUND_exit_leave:
16040       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16041       break;
16042
16043     case SOUND_dynamite:
16044       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16045       break;
16046
16047     case SOUND_tick:
16048       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16049       break;
16050
16051     case SOUND_press:
16052       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16053       break;
16054
16055     case SOUND_wheel:
16056       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16057       break;
16058
16059     case SOUND_boom:
16060       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16061       break;
16062
16063     case SOUND_die:
16064       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16065       break;
16066
16067     case SOUND_time:
16068       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16069       break;
16070
16071     default:
16072       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16073       break;
16074   }
16075 }
16076
16077 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16078 {
16079   int element = map_element_SP_to_RND(element_sp);
16080   int action = map_action_SP_to_RND(action_sp);
16081   int offset = (setup.sp_show_border_elements ? 0 : 1);
16082   int x = xx - offset;
16083   int y = yy - offset;
16084
16085   PlayLevelSoundElementAction(x, y, element, action);
16086 }
16087
16088 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16089 {
16090   int element = map_element_MM_to_RND(element_mm);
16091   int action = map_action_MM_to_RND(action_mm);
16092   int offset = 0;
16093   int x = xx - offset;
16094   int y = yy - offset;
16095
16096   if (!IS_MM_ELEMENT(element))
16097     element = EL_MM_DEFAULT;
16098
16099   PlayLevelSoundElementAction(x, y, element, action);
16100 }
16101
16102 void PlaySound_MM(int sound_mm)
16103 {
16104   int sound = map_sound_MM_to_RND(sound_mm);
16105
16106   if (sound == SND_UNDEFINED)
16107     return;
16108
16109   PlaySound(sound);
16110 }
16111
16112 void PlaySoundLoop_MM(int sound_mm)
16113 {
16114   int sound = map_sound_MM_to_RND(sound_mm);
16115
16116   if (sound == SND_UNDEFINED)
16117     return;
16118
16119   PlaySoundLoop(sound);
16120 }
16121
16122 void StopSound_MM(int sound_mm)
16123 {
16124   int sound = map_sound_MM_to_RND(sound_mm);
16125
16126   if (sound == SND_UNDEFINED)
16127     return;
16128
16129   StopSound(sound);
16130 }
16131
16132 void RaiseScore(int value)
16133 {
16134   game.score += value;
16135
16136   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16137
16138   DisplayGameControlValues();
16139 }
16140
16141 void RaiseScoreElement(int element)
16142 {
16143   switch (element)
16144   {
16145     case EL_EMERALD:
16146     case EL_BD_DIAMOND:
16147     case EL_EMERALD_YELLOW:
16148     case EL_EMERALD_RED:
16149     case EL_EMERALD_PURPLE:
16150     case EL_SP_INFOTRON:
16151       RaiseScore(level.score[SC_EMERALD]);
16152       break;
16153     case EL_DIAMOND:
16154       RaiseScore(level.score[SC_DIAMOND]);
16155       break;
16156     case EL_CRYSTAL:
16157       RaiseScore(level.score[SC_CRYSTAL]);
16158       break;
16159     case EL_PEARL:
16160       RaiseScore(level.score[SC_PEARL]);
16161       break;
16162     case EL_BUG:
16163     case EL_BD_BUTTERFLY:
16164     case EL_SP_ELECTRON:
16165       RaiseScore(level.score[SC_BUG]);
16166       break;
16167     case EL_SPACESHIP:
16168     case EL_BD_FIREFLY:
16169     case EL_SP_SNIKSNAK:
16170       RaiseScore(level.score[SC_SPACESHIP]);
16171       break;
16172     case EL_YAMYAM:
16173     case EL_DARK_YAMYAM:
16174       RaiseScore(level.score[SC_YAMYAM]);
16175       break;
16176     case EL_ROBOT:
16177       RaiseScore(level.score[SC_ROBOT]);
16178       break;
16179     case EL_PACMAN:
16180       RaiseScore(level.score[SC_PACMAN]);
16181       break;
16182     case EL_NUT:
16183       RaiseScore(level.score[SC_NUT]);
16184       break;
16185     case EL_DYNAMITE:
16186     case EL_EM_DYNAMITE:
16187     case EL_SP_DISK_RED:
16188     case EL_DYNABOMB_INCREASE_NUMBER:
16189     case EL_DYNABOMB_INCREASE_SIZE:
16190     case EL_DYNABOMB_INCREASE_POWER:
16191       RaiseScore(level.score[SC_DYNAMITE]);
16192       break;
16193     case EL_SHIELD_NORMAL:
16194     case EL_SHIELD_DEADLY:
16195       RaiseScore(level.score[SC_SHIELD]);
16196       break;
16197     case EL_EXTRA_TIME:
16198       RaiseScore(level.extra_time_score);
16199       break;
16200     case EL_KEY_1:
16201     case EL_KEY_2:
16202     case EL_KEY_3:
16203     case EL_KEY_4:
16204     case EL_EM_KEY_1:
16205     case EL_EM_KEY_2:
16206     case EL_EM_KEY_3:
16207     case EL_EM_KEY_4:
16208     case EL_EMC_KEY_5:
16209     case EL_EMC_KEY_6:
16210     case EL_EMC_KEY_7:
16211     case EL_EMC_KEY_8:
16212     case EL_DC_KEY_WHITE:
16213       RaiseScore(level.score[SC_KEY]);
16214       break;
16215     default:
16216       RaiseScore(element_info[element].collect_score);
16217       break;
16218   }
16219 }
16220
16221 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16222 {
16223   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16224   {
16225     if (!quick_quit)
16226     {
16227       // prevent short reactivation of overlay buttons while closing door
16228       SetOverlayActive(FALSE);
16229       UnmapGameButtons();
16230
16231       // door may still be open due to skipped or envelope style request
16232       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16233     }
16234
16235     if (network.enabled)
16236     {
16237       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16238     }
16239     else
16240     {
16241       // when using BD game engine, cover screen before fading out
16242       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16243         game_bd.cover_screen = TRUE;
16244
16245       if (quick_quit)
16246         FadeSkipNextFadeIn();
16247
16248       SetGameStatus(GAME_MODE_MAIN);
16249
16250       DrawMainMenu();
16251     }
16252   }
16253   else          // continue playing the game
16254   {
16255     if (tape.playing && tape.deactivate_display)
16256       TapeDeactivateDisplayOff(TRUE);
16257
16258     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16259
16260     if (tape.playing && tape.deactivate_display)
16261       TapeDeactivateDisplayOn();
16262   }
16263 }
16264
16265 void RequestQuitGame(boolean escape_key_pressed)
16266 {
16267   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16268   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16269                         level_editor_test_game);
16270   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16271                           quick_quit || score_info_tape_play);
16272
16273   RequestQuitGameExt(skip_request, quick_quit,
16274                      "Do you really want to quit the game?");
16275 }
16276
16277 static char *getRestartGameMessage(void)
16278 {
16279   boolean play_again = hasStartedNetworkGame();
16280   static char message[MAX_OUTPUT_LINESIZE];
16281   char *game_over_text = "Game over!";
16282   char *play_again_text = " Play it again?";
16283
16284   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16285       game_mm.game_over_message != NULL)
16286     game_over_text = game_mm.game_over_message;
16287
16288   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16289            (play_again ? play_again_text : ""));
16290
16291   return message;
16292 }
16293
16294 static void RequestRestartGame(void)
16295 {
16296   char *message = getRestartGameMessage();
16297   boolean has_started_game = hasStartedNetworkGame();
16298   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16299   int door_state = DOOR_CLOSE_1;
16300
16301   boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game);
16302
16303   // if no restart wanted, continue with next level for BD style intermission levels
16304   if (!restart_wanted && !level_editor_test_game && level.bd_intermission)
16305   {
16306     boolean success = AdvanceToNextLevel();
16307
16308     restart_wanted = (success && setup.auto_play_next_level);
16309   }
16310
16311   if (restart_wanted)
16312   {
16313     CloseDoor(door_state);
16314
16315     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16316   }
16317   else
16318   {
16319     // if game was invoked from level editor, also close tape recorder door
16320     if (level_editor_test_game)
16321       door_state = DOOR_CLOSE_ALL;
16322
16323     CloseDoor(door_state);
16324
16325     SetGameStatus(GAME_MODE_MAIN);
16326
16327     DrawMainMenu();
16328   }
16329 }
16330
16331 boolean CheckRestartGame(void)
16332 {
16333   static int game_over_delay = 0;
16334   int game_over_delay_value = 50;
16335   boolean game_over = checkGameFailed();
16336
16337   if (!game_over)
16338   {
16339     game_over_delay = game_over_delay_value;
16340
16341     return FALSE;
16342   }
16343
16344   if (game_over_delay > 0)
16345   {
16346     if (game_over_delay == game_over_delay_value / 2)
16347       PlaySound(SND_GAME_LOSING);
16348
16349     game_over_delay--;
16350
16351     return FALSE;
16352   }
16353
16354   // do not ask to play again if request dialog is already active
16355   if (checkRequestActive())
16356     return FALSE;
16357
16358   // do not ask to play again if request dialog already handled
16359   if (game.RestartGameRequested)
16360     return FALSE;
16361
16362   // do not ask to play again if game was never actually played
16363   if (!game.GamePlayed)
16364     return FALSE;
16365
16366   // do not ask to play again if this was disabled in setup menu
16367   if (!setup.ask_on_game_over)
16368     return FALSE;
16369
16370   game.RestartGameRequested = TRUE;
16371
16372   RequestRestartGame();
16373
16374   return TRUE;
16375 }
16376
16377 boolean checkGameRunning(void)
16378 {
16379   if (game_status != GAME_MODE_PLAYING)
16380     return FALSE;
16381
16382   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16383     return FALSE;
16384
16385   return TRUE;
16386 }
16387
16388 boolean checkGamePlaying(void)
16389 {
16390   if (game_status != GAME_MODE_PLAYING)
16391     return FALSE;
16392
16393   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16394     return FALSE;
16395
16396   return TRUE;
16397 }
16398
16399 boolean checkGameSolved(void)
16400 {
16401   // set for all game engines if level was solved
16402   return game.LevelSolved_GameEnd;
16403 }
16404
16405 boolean checkGameFailed(void)
16406 {
16407   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16408     return (game_bd.game_over && !game_bd.level_solved);
16409   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16410     return (game_em.game_over && !game_em.level_solved);
16411   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16412     return (game_sp.game_over && !game_sp.level_solved);
16413   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16414     return (game_mm.game_over && !game_mm.level_solved);
16415   else                          // GAME_ENGINE_TYPE_RND
16416     return (game.GameOver && !game.LevelSolved);
16417 }
16418
16419 boolean checkGameEnded(void)
16420 {
16421   return (checkGameSolved() || checkGameFailed());
16422 }
16423
16424 boolean checkRequestActive(void)
16425 {
16426   return (game.request_active || game.envelope_active || game.any_door_active);
16427 }
16428
16429
16430 // ----------------------------------------------------------------------------
16431 // random generator functions
16432 // ----------------------------------------------------------------------------
16433
16434 unsigned int InitEngineRandom_RND(int seed)
16435 {
16436   game.num_random_calls = 0;
16437
16438   return InitEngineRandom(seed);
16439 }
16440
16441 unsigned int RND(int max)
16442 {
16443   if (max > 0)
16444   {
16445     game.num_random_calls++;
16446
16447     return GetEngineRandom(max);
16448   }
16449
16450   return 0;
16451 }
16452
16453
16454 // ----------------------------------------------------------------------------
16455 // game engine snapshot handling functions
16456 // ----------------------------------------------------------------------------
16457
16458 struct EngineSnapshotInfo
16459 {
16460   // runtime values for custom element collect score
16461   int collect_score[NUM_CUSTOM_ELEMENTS];
16462
16463   // runtime values for group element choice position
16464   int choice_pos[NUM_GROUP_ELEMENTS];
16465
16466   // runtime values for belt position animations
16467   int belt_graphic[4][NUM_BELT_PARTS];
16468   int belt_anim_mode[4][NUM_BELT_PARTS];
16469 };
16470
16471 static struct EngineSnapshotInfo engine_snapshot_rnd;
16472 static char *snapshot_level_identifier = NULL;
16473 static int snapshot_level_nr = -1;
16474
16475 static void SaveEngineSnapshotValues_RND(void)
16476 {
16477   static int belt_base_active_element[4] =
16478   {
16479     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16480     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16481     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16482     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16483   };
16484   int i, j;
16485
16486   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16487   {
16488     int element = EL_CUSTOM_START + i;
16489
16490     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16491   }
16492
16493   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16494   {
16495     int element = EL_GROUP_START + i;
16496
16497     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16498   }
16499
16500   for (i = 0; i < 4; i++)
16501   {
16502     for (j = 0; j < NUM_BELT_PARTS; j++)
16503     {
16504       int element = belt_base_active_element[i] + j;
16505       int graphic = el2img(element);
16506       int anim_mode = graphic_info[graphic].anim_mode;
16507
16508       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16509       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16510     }
16511   }
16512 }
16513
16514 static void LoadEngineSnapshotValues_RND(void)
16515 {
16516   unsigned int num_random_calls = game.num_random_calls;
16517   int i, j;
16518
16519   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16520   {
16521     int element = EL_CUSTOM_START + i;
16522
16523     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16524   }
16525
16526   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16527   {
16528     int element = EL_GROUP_START + i;
16529
16530     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16531   }
16532
16533   for (i = 0; i < 4; i++)
16534   {
16535     for (j = 0; j < NUM_BELT_PARTS; j++)
16536     {
16537       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16538       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16539
16540       graphic_info[graphic].anim_mode = anim_mode;
16541     }
16542   }
16543
16544   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16545   {
16546     InitRND(tape.random_seed);
16547     for (i = 0; i < num_random_calls; i++)
16548       RND(1);
16549   }
16550
16551   if (game.num_random_calls != num_random_calls)
16552   {
16553     Error("number of random calls out of sync");
16554     Error("number of random calls should be %d", num_random_calls);
16555     Error("number of random calls is %d", game.num_random_calls);
16556
16557     Fail("this should not happen -- please debug");
16558   }
16559 }
16560
16561 void FreeEngineSnapshotSingle(void)
16562 {
16563   FreeSnapshotSingle();
16564
16565   setString(&snapshot_level_identifier, NULL);
16566   snapshot_level_nr = -1;
16567 }
16568
16569 void FreeEngineSnapshotList(void)
16570 {
16571   FreeSnapshotList();
16572 }
16573
16574 static ListNode *SaveEngineSnapshotBuffers(void)
16575 {
16576   ListNode *buffers = NULL;
16577
16578   // copy some special values to a structure better suited for the snapshot
16579
16580   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16581     SaveEngineSnapshotValues_RND();
16582   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16583     SaveEngineSnapshotValues_EM();
16584   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16585     SaveEngineSnapshotValues_SP(&buffers);
16586   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16587     SaveEngineSnapshotValues_MM();
16588
16589   // save values stored in special snapshot structure
16590
16591   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16592     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16593   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16594     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16595   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16596     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16597   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16598     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16599
16600   // save further RND engine values
16601
16602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16605
16606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16607   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16612
16613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16616
16617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16618
16619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16621
16622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16624   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16625   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16626   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16627   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16628   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16629   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16630   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16631   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16632   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16633   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16634   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16635   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16636   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16638   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16640
16641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16642   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16643
16644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16647
16648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16650
16651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16657
16658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16660
16661 #if 0
16662   ListNode *node = engine_snapshot_list_rnd;
16663   int num_bytes = 0;
16664
16665   while (node != NULL)
16666   {
16667     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16668
16669     node = node->next;
16670   }
16671
16672   Debug("game:playing:SaveEngineSnapshotBuffers",
16673         "size of engine snapshot: %d bytes", num_bytes);
16674 #endif
16675
16676   return buffers;
16677 }
16678
16679 void SaveEngineSnapshotSingle(void)
16680 {
16681   ListNode *buffers = SaveEngineSnapshotBuffers();
16682
16683   // finally save all snapshot buffers to single snapshot
16684   SaveSnapshotSingle(buffers);
16685
16686   // save level identification information
16687   setString(&snapshot_level_identifier, leveldir_current->identifier);
16688   snapshot_level_nr = level_nr;
16689 }
16690
16691 boolean CheckSaveEngineSnapshotToList(void)
16692 {
16693   boolean save_snapshot =
16694     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16695      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16696       game.snapshot.changed_action) ||
16697      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16698       game.snapshot.collected_item));
16699
16700   game.snapshot.changed_action = FALSE;
16701   game.snapshot.collected_item = FALSE;
16702   game.snapshot.save_snapshot = save_snapshot;
16703
16704   return save_snapshot;
16705 }
16706
16707 void SaveEngineSnapshotToList(void)
16708 {
16709   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16710       tape.quick_resume)
16711     return;
16712
16713   ListNode *buffers = SaveEngineSnapshotBuffers();
16714
16715   // finally save all snapshot buffers to snapshot list
16716   SaveSnapshotToList(buffers);
16717 }
16718
16719 void SaveEngineSnapshotToListInitial(void)
16720 {
16721   FreeEngineSnapshotList();
16722
16723   SaveEngineSnapshotToList();
16724 }
16725
16726 static void LoadEngineSnapshotValues(void)
16727 {
16728   // restore special values from snapshot structure
16729
16730   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16731     LoadEngineSnapshotValues_RND();
16732   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16733     LoadEngineSnapshotValues_EM();
16734   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16735     LoadEngineSnapshotValues_SP();
16736   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16737     LoadEngineSnapshotValues_MM();
16738 }
16739
16740 void LoadEngineSnapshotSingle(void)
16741 {
16742   LoadSnapshotSingle();
16743
16744   LoadEngineSnapshotValues();
16745 }
16746
16747 static void LoadEngineSnapshot_Undo(int steps)
16748 {
16749   LoadSnapshotFromList_Older(steps);
16750
16751   LoadEngineSnapshotValues();
16752 }
16753
16754 static void LoadEngineSnapshot_Redo(int steps)
16755 {
16756   LoadSnapshotFromList_Newer(steps);
16757
16758   LoadEngineSnapshotValues();
16759 }
16760
16761 boolean CheckEngineSnapshotSingle(void)
16762 {
16763   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16764           snapshot_level_nr == level_nr);
16765 }
16766
16767 boolean CheckEngineSnapshotList(void)
16768 {
16769   return CheckSnapshotList();
16770 }
16771
16772
16773 // ---------- new game button stuff -------------------------------------------
16774
16775 static struct
16776 {
16777   int graphic;
16778   struct XY *pos;
16779   int gadget_id;
16780   boolean *setup_value;
16781   boolean allowed_on_tape;
16782   boolean is_touch_button;
16783   char *infotext;
16784 } gamebutton_info[NUM_GAME_BUTTONS] =
16785 {
16786   {
16787     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16788     GAME_CTRL_ID_STOP,                          NULL,
16789     TRUE, FALSE,                                "stop game"
16790   },
16791   {
16792     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16793     GAME_CTRL_ID_PAUSE,                         NULL,
16794     TRUE, FALSE,                                "pause game"
16795   },
16796   {
16797     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16798     GAME_CTRL_ID_PLAY,                          NULL,
16799     TRUE, FALSE,                                "play game"
16800   },
16801   {
16802     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16803     GAME_CTRL_ID_UNDO,                          NULL,
16804     TRUE, FALSE,                                "undo step"
16805   },
16806   {
16807     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16808     GAME_CTRL_ID_REDO,                          NULL,
16809     TRUE, FALSE,                                "redo step"
16810   },
16811   {
16812     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16813     GAME_CTRL_ID_SAVE,                          NULL,
16814     TRUE, FALSE,                                "save game"
16815   },
16816   {
16817     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16818     GAME_CTRL_ID_PAUSE2,                        NULL,
16819     TRUE, FALSE,                                "pause game"
16820   },
16821   {
16822     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16823     GAME_CTRL_ID_LOAD,                          NULL,
16824     TRUE, FALSE,                                "load game"
16825   },
16826   {
16827     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16828     GAME_CTRL_ID_RESTART,                       NULL,
16829     TRUE, FALSE,                                "restart game"
16830   },
16831   {
16832     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16833     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16834     FALSE, FALSE,                               "stop game"
16835   },
16836   {
16837     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16838     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16839     FALSE, FALSE,                               "pause game"
16840   },
16841   {
16842     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16843     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16844     FALSE, FALSE,                               "play game"
16845   },
16846   {
16847     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16848     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16849     FALSE, FALSE,                               "restart game"
16850   },
16851   {
16852     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16853     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16854     FALSE, TRUE,                                "stop game"
16855   },
16856   {
16857     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16858     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16859     FALSE, TRUE,                                "pause game"
16860   },
16861   {
16862     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16863     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16864     FALSE, TRUE,                                "restart game"
16865   },
16866   {
16867     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16868     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16869     TRUE, FALSE,                                "background music on/off"
16870   },
16871   {
16872     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16873     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16874     TRUE, FALSE,                                "sound loops on/off"
16875   },
16876   {
16877     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16878     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16879     TRUE, FALSE,                                "normal sounds on/off"
16880   },
16881   {
16882     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16883     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16884     FALSE, FALSE,                               "background music on/off"
16885   },
16886   {
16887     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16888     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16889     FALSE, FALSE,                               "sound loops on/off"
16890   },
16891   {
16892     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16893     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16894     FALSE, FALSE,                               "normal sounds on/off"
16895   }
16896 };
16897
16898 void CreateGameButtons(void)
16899 {
16900   int i;
16901
16902   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16903   {
16904     int graphic = gamebutton_info[i].graphic;
16905     struct GraphicInfo *gfx = &graphic_info[graphic];
16906     struct XY *pos = gamebutton_info[i].pos;
16907     struct GadgetInfo *gi;
16908     int button_type;
16909     boolean checked;
16910     unsigned int event_mask;
16911     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16912     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16913     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16914     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16915     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16916     int gd_x   = gfx->src_x;
16917     int gd_y   = gfx->src_y;
16918     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16919     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16920     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16921     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16922     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16923     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16924     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16925     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16926     int id = i;
16927
16928     // do not use touch buttons if overlay touch buttons are disabled
16929     if (is_touch_button && !setup.touch.overlay_buttons)
16930       continue;
16931
16932     if (gfx->bitmap == NULL)
16933     {
16934       game_gadget[id] = NULL;
16935
16936       continue;
16937     }
16938
16939     if (id == GAME_CTRL_ID_STOP ||
16940         id == GAME_CTRL_ID_PANEL_STOP ||
16941         id == GAME_CTRL_ID_TOUCH_STOP ||
16942         id == GAME_CTRL_ID_PLAY ||
16943         id == GAME_CTRL_ID_PANEL_PLAY ||
16944         id == GAME_CTRL_ID_SAVE ||
16945         id == GAME_CTRL_ID_LOAD ||
16946         id == GAME_CTRL_ID_RESTART ||
16947         id == GAME_CTRL_ID_PANEL_RESTART ||
16948         id == GAME_CTRL_ID_TOUCH_RESTART)
16949     {
16950       button_type = GD_TYPE_NORMAL_BUTTON;
16951       checked = FALSE;
16952       event_mask = GD_EVENT_RELEASED;
16953     }
16954     else if (id == GAME_CTRL_ID_UNDO ||
16955              id == GAME_CTRL_ID_REDO)
16956     {
16957       button_type = GD_TYPE_NORMAL_BUTTON;
16958       checked = FALSE;
16959       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16960     }
16961     else
16962     {
16963       button_type = GD_TYPE_CHECK_BUTTON;
16964       checked = (gamebutton_info[i].setup_value != NULL ?
16965                  *gamebutton_info[i].setup_value : FALSE);
16966       event_mask = GD_EVENT_PRESSED;
16967     }
16968
16969     gi = CreateGadget(GDI_CUSTOM_ID, id,
16970                       GDI_IMAGE_ID, graphic,
16971                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16972                       GDI_X, base_x + x,
16973                       GDI_Y, base_y + y,
16974                       GDI_WIDTH, gfx->width,
16975                       GDI_HEIGHT, gfx->height,
16976                       GDI_TYPE, button_type,
16977                       GDI_STATE, GD_BUTTON_UNPRESSED,
16978                       GDI_CHECKED, checked,
16979                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16980                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16981                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16982                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16983                       GDI_DIRECT_DRAW, FALSE,
16984                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16985                       GDI_EVENT_MASK, event_mask,
16986                       GDI_CALLBACK_ACTION, HandleGameButtons,
16987                       GDI_END);
16988
16989     if (gi == NULL)
16990       Fail("cannot create gadget");
16991
16992     game_gadget[id] = gi;
16993   }
16994 }
16995
16996 void FreeGameButtons(void)
16997 {
16998   int i;
16999
17000   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17001     FreeGadget(game_gadget[i]);
17002 }
17003
17004 static void UnmapGameButtonsAtSamePosition(int id)
17005 {
17006   int i;
17007
17008   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17009     if (i != id &&
17010         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
17011         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
17012       UnmapGadget(game_gadget[i]);
17013 }
17014
17015 static void UnmapGameButtonsAtSamePosition_All(void)
17016 {
17017   if (setup.show_load_save_buttons)
17018   {
17019     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17020     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17021     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17022   }
17023   else if (setup.show_undo_redo_buttons)
17024   {
17025     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17026     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17027     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17028   }
17029   else
17030   {
17031     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
17032     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
17033     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
17034
17035     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
17036     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
17037     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
17038   }
17039 }
17040
17041 void MapLoadSaveButtons(void)
17042 {
17043   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17044   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17045
17046   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17047   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17048 }
17049
17050 void MapUndoRedoButtons(void)
17051 {
17052   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17053   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17054
17055   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17056   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17057 }
17058
17059 void ModifyPauseButtons(void)
17060 {
17061   static int ids[] =
17062   {
17063     GAME_CTRL_ID_PAUSE,
17064     GAME_CTRL_ID_PAUSE2,
17065     GAME_CTRL_ID_PANEL_PAUSE,
17066     GAME_CTRL_ID_TOUCH_PAUSE,
17067     -1
17068   };
17069   int i;
17070
17071   // do not redraw pause button on closed door (may happen when restarting game)
17072   if (!(GetDoorState() & DOOR_OPEN_1))
17073     return;
17074
17075   for (i = 0; ids[i] > -1; i++)
17076     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17077 }
17078
17079 static void MapGameButtonsExt(boolean on_tape)
17080 {
17081   int i;
17082
17083   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17084   {
17085     if ((i == GAME_CTRL_ID_UNDO ||
17086          i == GAME_CTRL_ID_REDO) &&
17087         game_status != GAME_MODE_PLAYING)
17088       continue;
17089
17090     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17091       MapGadget(game_gadget[i]);
17092   }
17093
17094   UnmapGameButtonsAtSamePosition_All();
17095
17096   RedrawGameButtons();
17097 }
17098
17099 static void UnmapGameButtonsExt(boolean on_tape)
17100 {
17101   int i;
17102
17103   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17104     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17105       UnmapGadget(game_gadget[i]);
17106 }
17107
17108 static void RedrawGameButtonsExt(boolean on_tape)
17109 {
17110   int i;
17111
17112   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17113     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17114       RedrawGadget(game_gadget[i]);
17115 }
17116
17117 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17118 {
17119   if (gi == NULL)
17120     return;
17121
17122   gi->checked = state;
17123 }
17124
17125 static void RedrawSoundButtonGadget(int id)
17126 {
17127   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17128              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17129              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17130              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17131              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17132              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17133              id);
17134
17135   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17136   RedrawGadget(game_gadget[id2]);
17137 }
17138
17139 void MapGameButtons(void)
17140 {
17141   MapGameButtonsExt(FALSE);
17142 }
17143
17144 void UnmapGameButtons(void)
17145 {
17146   UnmapGameButtonsExt(FALSE);
17147 }
17148
17149 void RedrawGameButtons(void)
17150 {
17151   RedrawGameButtonsExt(FALSE);
17152 }
17153
17154 void MapGameButtonsOnTape(void)
17155 {
17156   MapGameButtonsExt(TRUE);
17157 }
17158
17159 void UnmapGameButtonsOnTape(void)
17160 {
17161   UnmapGameButtonsExt(TRUE);
17162 }
17163
17164 void RedrawGameButtonsOnTape(void)
17165 {
17166   RedrawGameButtonsExt(TRUE);
17167 }
17168
17169 static void GameUndoRedoExt(void)
17170 {
17171   ClearPlayerAction();
17172
17173   tape.pausing = TRUE;
17174
17175   RedrawPlayfield();
17176   UpdateAndDisplayGameControlValues();
17177
17178   DrawCompleteVideoDisplay();
17179   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17180   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17181   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17182
17183   ModifyPauseButtons();
17184
17185   BackToFront();
17186 }
17187
17188 static void GameUndo(int steps)
17189 {
17190   if (!CheckEngineSnapshotList())
17191     return;
17192
17193   int tape_property_bits = tape.property_bits;
17194
17195   LoadEngineSnapshot_Undo(steps);
17196
17197   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17198
17199   GameUndoRedoExt();
17200 }
17201
17202 static void GameRedo(int steps)
17203 {
17204   if (!CheckEngineSnapshotList())
17205     return;
17206
17207   int tape_property_bits = tape.property_bits;
17208
17209   LoadEngineSnapshot_Redo(steps);
17210
17211   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17212
17213   GameUndoRedoExt();
17214 }
17215
17216 static void HandleGameButtonsExt(int id, int button)
17217 {
17218   static boolean game_undo_executed = FALSE;
17219   int steps = BUTTON_STEPSIZE(button);
17220   boolean handle_game_buttons =
17221     (game_status == GAME_MODE_PLAYING ||
17222      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17223
17224   if (!handle_game_buttons)
17225     return;
17226
17227   switch (id)
17228   {
17229     case GAME_CTRL_ID_STOP:
17230     case GAME_CTRL_ID_PANEL_STOP:
17231     case GAME_CTRL_ID_TOUCH_STOP:
17232       TapeStopGame();
17233
17234       break;
17235
17236     case GAME_CTRL_ID_PAUSE:
17237     case GAME_CTRL_ID_PAUSE2:
17238     case GAME_CTRL_ID_PANEL_PAUSE:
17239     case GAME_CTRL_ID_TOUCH_PAUSE:
17240       if (network.enabled && game_status == GAME_MODE_PLAYING)
17241       {
17242         if (tape.pausing)
17243           SendToServer_ContinuePlaying();
17244         else
17245           SendToServer_PausePlaying();
17246       }
17247       else
17248         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17249
17250       game_undo_executed = FALSE;
17251
17252       break;
17253
17254     case GAME_CTRL_ID_PLAY:
17255     case GAME_CTRL_ID_PANEL_PLAY:
17256       if (game_status == GAME_MODE_MAIN)
17257       {
17258         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17259       }
17260       else if (tape.pausing)
17261       {
17262         if (network.enabled)
17263           SendToServer_ContinuePlaying();
17264         else
17265           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17266       }
17267       break;
17268
17269     case GAME_CTRL_ID_UNDO:
17270       // Important: When using "save snapshot when collecting an item" mode,
17271       // load last (current) snapshot for first "undo" after pressing "pause"
17272       // (else the last-but-one snapshot would be loaded, because the snapshot
17273       // pointer already points to the last snapshot when pressing "pause",
17274       // which is fine for "every step/move" mode, but not for "every collect")
17275       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17276           !game_undo_executed)
17277         steps--;
17278
17279       game_undo_executed = TRUE;
17280
17281       GameUndo(steps);
17282       break;
17283
17284     case GAME_CTRL_ID_REDO:
17285       GameRedo(steps);
17286       break;
17287
17288     case GAME_CTRL_ID_SAVE:
17289       TapeQuickSave();
17290       break;
17291
17292     case GAME_CTRL_ID_LOAD:
17293       TapeQuickLoad();
17294       break;
17295
17296     case GAME_CTRL_ID_RESTART:
17297     case GAME_CTRL_ID_PANEL_RESTART:
17298     case GAME_CTRL_ID_TOUCH_RESTART:
17299       TapeRestartGame();
17300
17301       break;
17302
17303     case SOUND_CTRL_ID_MUSIC:
17304     case SOUND_CTRL_ID_PANEL_MUSIC:
17305       if (setup.sound_music)
17306       { 
17307         setup.sound_music = FALSE;
17308
17309         FadeMusic();
17310       }
17311       else if (audio.music_available)
17312       { 
17313         setup.sound = setup.sound_music = TRUE;
17314
17315         SetAudioMode(setup.sound);
17316
17317         if (game_status == GAME_MODE_PLAYING)
17318           PlayLevelMusic();
17319       }
17320
17321       RedrawSoundButtonGadget(id);
17322
17323       break;
17324
17325     case SOUND_CTRL_ID_LOOPS:
17326     case SOUND_CTRL_ID_PANEL_LOOPS:
17327       if (setup.sound_loops)
17328         setup.sound_loops = FALSE;
17329       else if (audio.loops_available)
17330       {
17331         setup.sound = setup.sound_loops = TRUE;
17332
17333         SetAudioMode(setup.sound);
17334       }
17335
17336       RedrawSoundButtonGadget(id);
17337
17338       break;
17339
17340     case SOUND_CTRL_ID_SIMPLE:
17341     case SOUND_CTRL_ID_PANEL_SIMPLE:
17342       if (setup.sound_simple)
17343         setup.sound_simple = FALSE;
17344       else if (audio.sound_available)
17345       {
17346         setup.sound = setup.sound_simple = TRUE;
17347
17348         SetAudioMode(setup.sound);
17349       }
17350
17351       RedrawSoundButtonGadget(id);
17352
17353       break;
17354
17355     default:
17356       break;
17357   }
17358 }
17359
17360 static void HandleGameButtons(struct GadgetInfo *gi)
17361 {
17362   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17363 }
17364
17365 void HandleSoundButtonKeys(Key key)
17366 {
17367   if (key == setup.shortcut.sound_simple)
17368     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17369   else if (key == setup.shortcut.sound_loops)
17370     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17371   else if (key == setup.shortcut.sound_music)
17372     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17373 }