fixed potential crash bug in 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 InitField(int x, int y, boolean init_game)
1839 {
1840   int element = Tile[x][y];
1841
1842   switch (element)
1843   {
1844     case EL_SP_MURPHY:
1845     case EL_PLAYER_1:
1846     case EL_PLAYER_2:
1847     case EL_PLAYER_3:
1848     case EL_PLAYER_4:
1849       InitPlayerField(x, y, element, init_game);
1850       break;
1851
1852     case EL_SOKOBAN_FIELD_PLAYER:
1853       element = Tile[x][y] = EL_PLAYER_1;
1854       InitField(x, y, init_game);
1855
1856       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1857       InitField(x, y, init_game);
1858       break;
1859
1860     case EL_SOKOBAN_FIELD_EMPTY:
1861       IncrementSokobanFieldsNeeded();
1862       break;
1863
1864     case EL_SOKOBAN_OBJECT:
1865       IncrementSokobanObjectsNeeded();
1866       break;
1867
1868     case EL_STONEBLOCK:
1869       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1870         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1871       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1872         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1873       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1874         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1875       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1876         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1877       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1878         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1879       break;
1880
1881     case EL_BUG:
1882     case EL_BUG_RIGHT:
1883     case EL_BUG_UP:
1884     case EL_BUG_LEFT:
1885     case EL_BUG_DOWN:
1886     case EL_SPACESHIP:
1887     case EL_SPACESHIP_RIGHT:
1888     case EL_SPACESHIP_UP:
1889     case EL_SPACESHIP_LEFT:
1890     case EL_SPACESHIP_DOWN:
1891     case EL_BD_BUTTERFLY:
1892     case EL_BD_BUTTERFLY_RIGHT:
1893     case EL_BD_BUTTERFLY_UP:
1894     case EL_BD_BUTTERFLY_LEFT:
1895     case EL_BD_BUTTERFLY_DOWN:
1896     case EL_BD_FIREFLY:
1897     case EL_BD_FIREFLY_RIGHT:
1898     case EL_BD_FIREFLY_UP:
1899     case EL_BD_FIREFLY_LEFT:
1900     case EL_BD_FIREFLY_DOWN:
1901     case EL_PACMAN_RIGHT:
1902     case EL_PACMAN_UP:
1903     case EL_PACMAN_LEFT:
1904     case EL_PACMAN_DOWN:
1905     case EL_YAMYAM:
1906     case EL_YAMYAM_LEFT:
1907     case EL_YAMYAM_RIGHT:
1908     case EL_YAMYAM_UP:
1909     case EL_YAMYAM_DOWN:
1910     case EL_DARK_YAMYAM:
1911     case EL_ROBOT:
1912     case EL_PACMAN:
1913     case EL_SP_SNIKSNAK:
1914     case EL_SP_ELECTRON:
1915     case EL_MOLE:
1916     case EL_MOLE_LEFT:
1917     case EL_MOLE_RIGHT:
1918     case EL_MOLE_UP:
1919     case EL_MOLE_DOWN:
1920     case EL_SPRING_LEFT:
1921     case EL_SPRING_RIGHT:
1922       InitMovDir(x, y);
1923       break;
1924
1925     case EL_AMOEBA_FULL:
1926     case EL_BD_AMOEBA:
1927       InitAmoebaNr(x, y);
1928       break;
1929
1930     case EL_AMOEBA_DROP:
1931       if (y == lev_fieldy - 1)
1932       {
1933         Tile[x][y] = EL_AMOEBA_GROWING;
1934         Store[x][y] = EL_AMOEBA_WET;
1935       }
1936       break;
1937
1938     case EL_DYNAMITE_ACTIVE:
1939     case EL_SP_DISK_RED_ACTIVE:
1940     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1941     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1942     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1943     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1944       MovDelay[x][y] = 96;
1945       break;
1946
1947     case EL_EM_DYNAMITE_ACTIVE:
1948       MovDelay[x][y] = 32;
1949       break;
1950
1951     case EL_LAMP:
1952       game.lights_still_needed++;
1953       break;
1954
1955     case EL_PENGUIN:
1956       game.friends_still_needed++;
1957       break;
1958
1959     case EL_PIG:
1960     case EL_DRAGON:
1961       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1962       break;
1963
1964     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1965     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1966     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1967     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1968     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1969     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1970     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1971     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1972     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1973     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1974     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1975     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1976       if (init_game)
1977       {
1978         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1979         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1980         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1981
1982         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1983         {
1984           game.belt_dir[belt_nr] = belt_dir;
1985           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1986         }
1987         else    // more than one switch -- set it like the first switch
1988         {
1989           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1990         }
1991       }
1992       break;
1993
1994     case EL_LIGHT_SWITCH_ACTIVE:
1995       if (init_game)
1996         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1997       break;
1998
1999     case EL_INVISIBLE_STEELWALL:
2000     case EL_INVISIBLE_WALL:
2001     case EL_INVISIBLE_SAND:
2002       if (game.light_time_left > 0 ||
2003           game.lenses_time_left > 0)
2004         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2005       break;
2006
2007     case EL_EMC_MAGIC_BALL:
2008       if (game.ball_active)
2009         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2010       break;
2011
2012     case EL_EMC_MAGIC_BALL_SWITCH:
2013       if (game.ball_active)
2014         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2015       break;
2016
2017     case EL_TRIGGER_PLAYER:
2018     case EL_TRIGGER_ELEMENT:
2019     case EL_TRIGGER_CE_VALUE:
2020     case EL_TRIGGER_CE_SCORE:
2021     case EL_SELF:
2022     case EL_ANY_ELEMENT:
2023     case EL_CURRENT_CE_VALUE:
2024     case EL_CURRENT_CE_SCORE:
2025     case EL_PREV_CE_1:
2026     case EL_PREV_CE_2:
2027     case EL_PREV_CE_3:
2028     case EL_PREV_CE_4:
2029     case EL_PREV_CE_5:
2030     case EL_PREV_CE_6:
2031     case EL_PREV_CE_7:
2032     case EL_PREV_CE_8:
2033     case EL_NEXT_CE_1:
2034     case EL_NEXT_CE_2:
2035     case EL_NEXT_CE_3:
2036     case EL_NEXT_CE_4:
2037     case EL_NEXT_CE_5:
2038     case EL_NEXT_CE_6:
2039     case EL_NEXT_CE_7:
2040     case EL_NEXT_CE_8:
2041       // reference elements should not be used on the playfield
2042       Tile[x][y] = EL_EMPTY;
2043       break;
2044
2045     default:
2046       if (IS_CUSTOM_ELEMENT(element))
2047       {
2048         if (CAN_MOVE(element))
2049           InitMovDir(x, y);
2050
2051         if (!element_info[element].use_last_ce_value || init_game)
2052           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2053       }
2054       else if (IS_GROUP_ELEMENT(element))
2055       {
2056         Tile[x][y] = GetElementFromGroupElement(element);
2057
2058         InitField(x, y, init_game);
2059       }
2060       else if (IS_EMPTY_ELEMENT(element))
2061       {
2062         GfxElementEmpty[x][y] = element;
2063         Tile[x][y] = EL_EMPTY;
2064
2065         if (element_info[element].use_gfx_element)
2066           game.use_masked_elements = TRUE;
2067       }
2068
2069       break;
2070   }
2071
2072   if (!init_game)
2073     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2074 }
2075
2076 static void InitField_WithBug1(int x, int y, boolean init_game)
2077 {
2078   InitField(x, y, init_game);
2079
2080   // not needed to call InitMovDir() -- already done by InitField()!
2081   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2082       CAN_MOVE(Tile[x][y]))
2083     InitMovDir(x, y);
2084 }
2085
2086 static void InitField_WithBug2(int x, int y, boolean init_game)
2087 {
2088   int old_element = Tile[x][y];
2089
2090   InitField(x, y, init_game);
2091
2092   // not needed to call InitMovDir() -- already done by InitField()!
2093   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2094       CAN_MOVE(old_element) &&
2095       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2096     InitMovDir(x, y);
2097
2098   /* this case is in fact a combination of not less than three bugs:
2099      first, it calls InitMovDir() for elements that can move, although this is
2100      already done by InitField(); then, it checks the element that was at this
2101      field _before_ the call to InitField() (which can change it); lastly, it
2102      was not called for "mole with direction" elements, which were treated as
2103      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2104   */
2105 }
2106
2107 static int get_key_element_from_nr(int key_nr)
2108 {
2109   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2110                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2111                           EL_EM_KEY_1 : EL_KEY_1);
2112
2113   return key_base_element + key_nr;
2114 }
2115
2116 static int get_next_dropped_element(struct PlayerInfo *player)
2117 {
2118   return (player->inventory_size > 0 ?
2119           player->inventory_element[player->inventory_size - 1] :
2120           player->inventory_infinite_element != EL_UNDEFINED ?
2121           player->inventory_infinite_element :
2122           player->dynabombs_left > 0 ?
2123           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2124           EL_UNDEFINED);
2125 }
2126
2127 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2128 {
2129   // pos >= 0: get element from bottom of the stack;
2130   // pos <  0: get element from top of the stack
2131
2132   if (pos < 0)
2133   {
2134     int min_inventory_size = -pos;
2135     int inventory_pos = player->inventory_size - min_inventory_size;
2136     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2137
2138     return (player->inventory_size >= min_inventory_size ?
2139             player->inventory_element[inventory_pos] :
2140             player->inventory_infinite_element != EL_UNDEFINED ?
2141             player->inventory_infinite_element :
2142             player->dynabombs_left >= min_dynabombs_left ?
2143             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2144             EL_UNDEFINED);
2145   }
2146   else
2147   {
2148     int min_dynabombs_left = pos + 1;
2149     int min_inventory_size = pos + 1 - player->dynabombs_left;
2150     int inventory_pos = pos - player->dynabombs_left;
2151
2152     return (player->inventory_infinite_element != EL_UNDEFINED ?
2153             player->inventory_infinite_element :
2154             player->dynabombs_left >= min_dynabombs_left ?
2155             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2156             player->inventory_size >= min_inventory_size ?
2157             player->inventory_element[inventory_pos] :
2158             EL_UNDEFINED);
2159   }
2160 }
2161
2162 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2163 {
2164   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2165   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2166   int compare_result;
2167
2168   if (gpo1->sort_priority != gpo2->sort_priority)
2169     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2170   else
2171     compare_result = gpo1->nr - gpo2->nr;
2172
2173   return compare_result;
2174 }
2175
2176 int getPlayerInventorySize(int player_nr)
2177 {
2178   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179     return game_em.ply[player_nr]->dynamite;
2180   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2181     return game_sp.red_disk_count;
2182   else
2183     return stored_player[player_nr].inventory_size;
2184 }
2185
2186 static void InitGameControlValues(void)
2187 {
2188   int i;
2189
2190   for (i = 0; game_panel_controls[i].nr != -1; i++)
2191   {
2192     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2193     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2194     struct TextPosInfo *pos = gpc->pos;
2195     int nr = gpc->nr;
2196     int type = gpc->type;
2197
2198     if (nr != i)
2199     {
2200       Error("'game_panel_controls' structure corrupted at %d", i);
2201
2202       Fail("this should not happen -- please debug");
2203     }
2204
2205     // force update of game controls after initialization
2206     gpc->value = gpc->last_value = -1;
2207     gpc->frame = gpc->last_frame = -1;
2208     gpc->gfx_frame = -1;
2209
2210     // determine panel value width for later calculation of alignment
2211     if (type == TYPE_INTEGER || type == TYPE_STRING)
2212     {
2213       pos->width = pos->size * getFontWidth(pos->font);
2214       pos->height = getFontHeight(pos->font);
2215     }
2216     else if (type == TYPE_ELEMENT)
2217     {
2218       pos->width = pos->size;
2219       pos->height = pos->size;
2220     }
2221
2222     // fill structure for game panel draw order
2223     gpo->nr = gpc->nr;
2224     gpo->sort_priority = pos->sort_priority;
2225   }
2226
2227   // sort game panel controls according to sort_priority and control number
2228   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2229         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2230 }
2231
2232 static void UpdatePlayfieldElementCount(void)
2233 {
2234   boolean use_element_count = FALSE;
2235   int i, j, x, y;
2236
2237   // first check if it is needed at all to calculate playfield element count
2238   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2239     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2240       use_element_count = TRUE;
2241
2242   if (!use_element_count)
2243     return;
2244
2245   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2246     element_info[i].element_count = 0;
2247
2248   SCAN_PLAYFIELD(x, y)
2249   {
2250     element_info[Tile[x][y]].element_count++;
2251   }
2252
2253   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2254     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2255       if (IS_IN_GROUP(j, i))
2256         element_info[EL_GROUP_START + i].element_count +=
2257           element_info[j].element_count;
2258 }
2259
2260 static void UpdateGameControlValues(void)
2261 {
2262   int i, k;
2263   int time = (game.LevelSolved ?
2264               game.LevelSolved_CountingTime :
2265               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2266               game_bd.time_played :
2267               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268               game_em.lev->time :
2269               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270               game_sp.time_played :
2271               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272               game_mm.energy_left :
2273               game.no_level_time_limit ? TimePlayed : TimeLeft);
2274   int score = (game.LevelSolved ?
2275                game.LevelSolved_CountingScore :
2276                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2277                game_bd.score :
2278                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2279                game_em.lev->score :
2280                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2281                game_sp.score :
2282                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2283                game_mm.score :
2284                game.score);
2285   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2286               game_bd.gems_still_needed :
2287               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2288               game_em.lev->gems_needed :
2289               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2290               game_sp.infotrons_still_needed :
2291               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2292               game_mm.kettles_still_needed :
2293               game.gems_still_needed);
2294   int gems_needed = level.gems_needed;
2295   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2296                         game_bd.game->cave->diamonds_collected :
2297                         gems_needed - gems);
2298   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2299                     game_bd.game->cave->diamond_value :
2300                     level.score[SC_EMERALD]);
2301   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2302                      game_bd.gems_still_needed > 0 :
2303                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2304                      game_em.lev->gems_needed > 0 :
2305                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2306                      game_sp.infotrons_still_needed > 0 :
2307                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2308                      game_mm.kettles_still_needed > 0 ||
2309                      game_mm.lights_still_needed > 0 :
2310                      game.gems_still_needed > 0 ||
2311                      game.sokoban_fields_still_needed > 0 ||
2312                      game.sokoban_objects_still_needed > 0 ||
2313                      game.lights_still_needed > 0);
2314   int health = (game.LevelSolved ?
2315                 game.LevelSolved_CountingHealth :
2316                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2317                 MM_HEALTH(game_mm.laser_overload_value) :
2318                 game.health);
2319   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2320
2321   UpdatePlayfieldElementCount();
2322
2323   // update game panel control values
2324
2325   // used instead of "level_nr" (for network games)
2326   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2327   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2328   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2329   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2330   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2331
2332   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2333   for (i = 0; i < MAX_NUM_KEYS; i++)
2334     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2335   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2336   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2337
2338   if (game.centered_player_nr == -1)
2339   {
2340     for (i = 0; i < MAX_PLAYERS; i++)
2341     {
2342       // only one player in Supaplex game engine
2343       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2344         break;
2345
2346       for (k = 0; k < MAX_NUM_KEYS; k++)
2347       {
2348         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2349         {
2350           if (game_em.ply[i]->keys & (1 << k))
2351             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2352               get_key_element_from_nr(k);
2353         }
2354         else if (stored_player[i].key[k])
2355           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2356             get_key_element_from_nr(k);
2357       }
2358
2359       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2360         getPlayerInventorySize(i);
2361
2362       if (stored_player[i].num_white_keys > 0)
2363         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2364           EL_DC_KEY_WHITE;
2365
2366       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2367         stored_player[i].num_white_keys;
2368     }
2369   }
2370   else
2371   {
2372     int player_nr = game.centered_player_nr;
2373
2374     for (k = 0; k < MAX_NUM_KEYS; k++)
2375     {
2376       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2377       {
2378         if (game_em.ply[player_nr]->keys & (1 << k))
2379           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2380             get_key_element_from_nr(k);
2381       }
2382       else if (stored_player[player_nr].key[k])
2383         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2384           get_key_element_from_nr(k);
2385     }
2386
2387     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2388       getPlayerInventorySize(player_nr);
2389
2390     if (stored_player[player_nr].num_white_keys > 0)
2391       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2392
2393     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2394       stored_player[player_nr].num_white_keys;
2395   }
2396
2397   // re-arrange keys on game panel, if needed or if defined by style settings
2398   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2399   {
2400     int nr = GAME_PANEL_KEY_1 + i;
2401     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2402     struct TextPosInfo *pos = gpc->pos;
2403
2404     // skip check if key is not in the player's inventory
2405     if (gpc->value == EL_EMPTY)
2406       continue;
2407
2408     // check if keys should be arranged on panel from left to right
2409     if (pos->style == STYLE_LEFTMOST_POSITION)
2410     {
2411       // check previous key positions (left from current key)
2412       for (k = 0; k < i; k++)
2413       {
2414         int nr_new = GAME_PANEL_KEY_1 + k;
2415
2416         if (game_panel_controls[nr_new].value == EL_EMPTY)
2417         {
2418           game_panel_controls[nr_new].value = gpc->value;
2419           gpc->value = EL_EMPTY;
2420
2421           break;
2422         }
2423       }
2424     }
2425
2426     // check if "undefined" keys can be placed at some other position
2427     if (pos->x == -1 && pos->y == -1)
2428     {
2429       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2430
2431       // 1st try: display key at the same position as normal or EM keys
2432       if (game_panel_controls[nr_new].value == EL_EMPTY)
2433       {
2434         game_panel_controls[nr_new].value = gpc->value;
2435       }
2436       else
2437       {
2438         // 2nd try: display key at the next free position in the key panel
2439         for (k = 0; k < STD_NUM_KEYS; k++)
2440         {
2441           nr_new = GAME_PANEL_KEY_1 + k;
2442
2443           if (game_panel_controls[nr_new].value == EL_EMPTY)
2444           {
2445             game_panel_controls[nr_new].value = gpc->value;
2446
2447             break;
2448           }
2449         }
2450       }
2451     }
2452   }
2453
2454   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2455   {
2456     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2457       get_inventory_element_from_pos(local_player, i);
2458     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2459       get_inventory_element_from_pos(local_player, -i - 1);
2460   }
2461
2462   game_panel_controls[GAME_PANEL_SCORE].value = score;
2463   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2464
2465   game_panel_controls[GAME_PANEL_TIME].value = time;
2466
2467   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2468   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2469   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2470
2471   if (level.time == 0)
2472     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2473   else
2474     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2475
2476   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2477   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2478
2479   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2480
2481   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2482     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2483      EL_EMPTY);
2484   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2485     local_player->shield_normal_time_left;
2486   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2487     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2488      EL_EMPTY);
2489   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2490     local_player->shield_deadly_time_left;
2491
2492   game_panel_controls[GAME_PANEL_EXIT].value =
2493     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2494
2495   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2496     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2497   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2498     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2499      EL_EMC_MAGIC_BALL_SWITCH);
2500
2501   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2502     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2503   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2504     game.light_time_left;
2505
2506   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2507     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2508   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2509     game.timegate_time_left;
2510
2511   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2512     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2513
2514   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2515     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2516   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2517     game.lenses_time_left;
2518
2519   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2520     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2521   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2522     game.magnify_time_left;
2523
2524   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2525     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2526      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2527      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2528      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2529      EL_BALLOON_SWITCH_NONE);
2530
2531   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2532     local_player->dynabomb_count;
2533   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2534     local_player->dynabomb_size;
2535   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2536     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2537
2538   game_panel_controls[GAME_PANEL_PENGUINS].value =
2539     game.friends_still_needed;
2540
2541   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2542     game.sokoban_objects_still_needed;
2543   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2544     game.sokoban_fields_still_needed;
2545
2546   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2547     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2548
2549   for (i = 0; i < NUM_BELTS; i++)
2550   {
2551     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2552       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2553        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2554     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2555       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2556   }
2557
2558   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2559     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2560   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2561     game.magic_wall_time_left;
2562
2563   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2564     local_player->gravity;
2565
2566   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2567     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2568
2569   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2570     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2571       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2572        game.panel.element[i].id : EL_UNDEFINED);
2573
2574   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2575     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2576       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2577        element_info[game.panel.element_count[i].id].element_count : 0);
2578
2579   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2580     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2581       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2582        element_info[game.panel.ce_score[i].id].collect_score : 0);
2583
2584   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2585     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2586       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2587        element_info[game.panel.ce_score_element[i].id].collect_score :
2588        EL_UNDEFINED);
2589
2590   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2591   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2592   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2593
2594   // update game panel control frames
2595
2596   for (i = 0; game_panel_controls[i].nr != -1; i++)
2597   {
2598     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2599
2600     if (gpc->type == TYPE_ELEMENT)
2601     {
2602       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2603       {
2604         int last_anim_random_frame = gfx.anim_random_frame;
2605         int element = gpc->value;
2606         int graphic = el2panelimg(element);
2607         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2608                                sync_random_frame :
2609                                graphic_info[graphic].anim_global_anim_sync ?
2610                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2611
2612         if (gpc->value != gpc->last_value)
2613         {
2614           gpc->gfx_frame = 0;
2615           gpc->gfx_random = init_gfx_random;
2616         }
2617         else
2618         {
2619           gpc->gfx_frame++;
2620
2621           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2622               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2623             gpc->gfx_random = init_gfx_random;
2624         }
2625
2626         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2627           gfx.anim_random_frame = gpc->gfx_random;
2628
2629         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2630           gpc->gfx_frame = element_info[element].collect_score;
2631
2632         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2633
2634         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2635           gfx.anim_random_frame = last_anim_random_frame;
2636       }
2637     }
2638     else if (gpc->type == TYPE_GRAPHIC)
2639     {
2640       if (gpc->graphic != IMG_UNDEFINED)
2641       {
2642         int last_anim_random_frame = gfx.anim_random_frame;
2643         int graphic = gpc->graphic;
2644         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2645                                sync_random_frame :
2646                                graphic_info[graphic].anim_global_anim_sync ?
2647                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2648
2649         if (gpc->value != gpc->last_value)
2650         {
2651           gpc->gfx_frame = 0;
2652           gpc->gfx_random = init_gfx_random;
2653         }
2654         else
2655         {
2656           gpc->gfx_frame++;
2657
2658           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2659               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2660             gpc->gfx_random = init_gfx_random;
2661         }
2662
2663         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2664           gfx.anim_random_frame = gpc->gfx_random;
2665
2666         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2667
2668         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2669           gfx.anim_random_frame = last_anim_random_frame;
2670       }
2671     }
2672   }
2673 }
2674
2675 static void DisplayGameControlValues(void)
2676 {
2677   boolean redraw_panel = FALSE;
2678   int i;
2679
2680   for (i = 0; game_panel_controls[i].nr != -1; i++)
2681   {
2682     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2683
2684     if (PANEL_DEACTIVATED(gpc->pos))
2685       continue;
2686
2687     if (gpc->value == gpc->last_value &&
2688         gpc->frame == gpc->last_frame)
2689       continue;
2690
2691     redraw_panel = TRUE;
2692   }
2693
2694   if (!redraw_panel)
2695     return;
2696
2697   // copy default game door content to main double buffer
2698
2699   // !!! CHECK AGAIN !!!
2700   SetPanelBackground();
2701   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2702   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2703
2704   // redraw game control buttons
2705   RedrawGameButtons();
2706
2707   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2708
2709   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2710   {
2711     int nr = game_panel_order[i].nr;
2712     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2713     struct TextPosInfo *pos = gpc->pos;
2714     int type = gpc->type;
2715     int value = gpc->value;
2716     int frame = gpc->frame;
2717     int size = pos->size;
2718     int font = pos->font;
2719     boolean draw_masked = pos->draw_masked;
2720     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2721
2722     if (PANEL_DEACTIVATED(pos))
2723       continue;
2724
2725     if (pos->class == get_hash_from_string("extra_panel_items") &&
2726         !setup.prefer_extra_panel_items)
2727       continue;
2728
2729     gpc->last_value = value;
2730     gpc->last_frame = frame;
2731
2732     if (type == TYPE_INTEGER)
2733     {
2734       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2735           nr == GAME_PANEL_INVENTORY_COUNT ||
2736           nr == GAME_PANEL_SCORE ||
2737           nr == GAME_PANEL_HIGHSCORE ||
2738           nr == GAME_PANEL_TIME)
2739       {
2740         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2741
2742         if (use_dynamic_size)           // use dynamic number of digits
2743         {
2744           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2745                               nr == GAME_PANEL_INVENTORY_COUNT ||
2746                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2747           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2748                           nr == GAME_PANEL_INVENTORY_COUNT ||
2749                           nr == GAME_PANEL_TIME ? 1 : 2);
2750           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2751                        nr == GAME_PANEL_INVENTORY_COUNT ||
2752                        nr == GAME_PANEL_TIME ? 3 : 5);
2753           int size2 = size1 + size_add;
2754           int font1 = pos->font;
2755           int font2 = pos->font_alt;
2756
2757           size = (value < value_change ? size1 : size2);
2758           font = (value < value_change ? font1 : font2);
2759         }
2760       }
2761
2762       // correct text size if "digits" is zero or less
2763       if (size <= 0)
2764         size = strlen(int2str(value, size));
2765
2766       // dynamically correct text alignment
2767       pos->width = size * getFontWidth(font);
2768
2769       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2770                   int2str(value, size), font, mask_mode);
2771     }
2772     else if (type == TYPE_ELEMENT)
2773     {
2774       int element, graphic;
2775       Bitmap *src_bitmap;
2776       int src_x, src_y;
2777       int width, height;
2778       int dst_x = PANEL_XPOS(pos);
2779       int dst_y = PANEL_YPOS(pos);
2780
2781       if (value != EL_UNDEFINED && value != EL_EMPTY)
2782       {
2783         element = value;
2784         graphic = el2panelimg(value);
2785
2786 #if 0
2787         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2788               element, EL_NAME(element), size);
2789 #endif
2790
2791         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2792           size = TILESIZE;
2793
2794         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2795                               &src_x, &src_y);
2796
2797         width  = graphic_info[graphic].width  * size / TILESIZE;
2798         height = graphic_info[graphic].height * size / TILESIZE;
2799
2800         if (draw_masked)
2801           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2802                            dst_x, dst_y);
2803         else
2804           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2805                      dst_x, dst_y);
2806       }
2807     }
2808     else if (type == TYPE_GRAPHIC)
2809     {
2810       int graphic        = gpc->graphic;
2811       int graphic_active = gpc->graphic_active;
2812       Bitmap *src_bitmap;
2813       int src_x, src_y;
2814       int width, height;
2815       int dst_x = PANEL_XPOS(pos);
2816       int dst_y = PANEL_YPOS(pos);
2817       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2818                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2819
2820       if (graphic != IMG_UNDEFINED && !skip)
2821       {
2822         if (pos->style == STYLE_REVERSE)
2823           value = 100 - value;
2824
2825         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2826
2827         if (pos->direction & MV_HORIZONTAL)
2828         {
2829           width  = graphic_info[graphic_active].width * value / 100;
2830           height = graphic_info[graphic_active].height;
2831
2832           if (pos->direction == MV_LEFT)
2833           {
2834             src_x += graphic_info[graphic_active].width - width;
2835             dst_x += graphic_info[graphic_active].width - width;
2836           }
2837         }
2838         else
2839         {
2840           width  = graphic_info[graphic_active].width;
2841           height = graphic_info[graphic_active].height * value / 100;
2842
2843           if (pos->direction == MV_UP)
2844           {
2845             src_y += graphic_info[graphic_active].height - height;
2846             dst_y += graphic_info[graphic_active].height - height;
2847           }
2848         }
2849
2850         if (draw_masked)
2851           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2852                            dst_x, dst_y);
2853         else
2854           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2855                      dst_x, dst_y);
2856
2857         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2858
2859         if (pos->direction & MV_HORIZONTAL)
2860         {
2861           if (pos->direction == MV_RIGHT)
2862           {
2863             src_x += width;
2864             dst_x += width;
2865           }
2866           else
2867           {
2868             dst_x = PANEL_XPOS(pos);
2869           }
2870
2871           width = graphic_info[graphic].width - width;
2872         }
2873         else
2874         {
2875           if (pos->direction == MV_DOWN)
2876           {
2877             src_y += height;
2878             dst_y += height;
2879           }
2880           else
2881           {
2882             dst_y = PANEL_YPOS(pos);
2883           }
2884
2885           height = graphic_info[graphic].height - height;
2886         }
2887
2888         if (draw_masked)
2889           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2890                            dst_x, dst_y);
2891         else
2892           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2893                      dst_x, dst_y);
2894       }
2895     }
2896     else if (type == TYPE_STRING)
2897     {
2898       boolean active = (value != 0);
2899       char *state_normal = "off";
2900       char *state_active = "on";
2901       char *state = (active ? state_active : state_normal);
2902       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2903                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2904                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2905                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2906
2907       if (nr == GAME_PANEL_GRAVITY_STATE)
2908       {
2909         int font1 = pos->font;          // (used for normal state)
2910         int font2 = pos->font_alt;      // (used for active state)
2911
2912         font = (active ? font2 : font1);
2913       }
2914
2915       if (s != NULL)
2916       {
2917         char *s_cut;
2918
2919         if (size <= 0)
2920         {
2921           // don't truncate output if "chars" is zero or less
2922           size = strlen(s);
2923
2924           // dynamically correct text alignment
2925           pos->width = size * getFontWidth(font);
2926         }
2927
2928         s_cut = getStringCopyN(s, size);
2929
2930         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2931                     s_cut, font, mask_mode);
2932
2933         free(s_cut);
2934       }
2935     }
2936
2937     redraw_mask |= REDRAW_DOOR_1;
2938   }
2939
2940   SetGameStatus(GAME_MODE_PLAYING);
2941 }
2942
2943 void UpdateAndDisplayGameControlValues(void)
2944 {
2945   if (tape.deactivate_display)
2946     return;
2947
2948   UpdateGameControlValues();
2949   DisplayGameControlValues();
2950 }
2951
2952 void UpdateGameDoorValues(void)
2953 {
2954   UpdateGameControlValues();
2955 }
2956
2957 void DrawGameDoorValues(void)
2958 {
2959   DisplayGameControlValues();
2960 }
2961
2962
2963 // ============================================================================
2964 // InitGameEngine()
2965 // ----------------------------------------------------------------------------
2966 // initialize game engine due to level / tape version number
2967 // ============================================================================
2968
2969 static void InitGameEngine(void)
2970 {
2971   int i, j, k, l, x, y;
2972
2973   // set game engine from tape file when re-playing, else from level file
2974   game.engine_version = (tape.playing ? tape.engine_version :
2975                          level.game_version);
2976
2977   // set single or multi-player game mode (needed for re-playing tapes)
2978   game.team_mode = setup.team_mode;
2979
2980   if (tape.playing)
2981   {
2982     int num_players = 0;
2983
2984     for (i = 0; i < MAX_PLAYERS; i++)
2985       if (tape.player_participates[i])
2986         num_players++;
2987
2988     // multi-player tapes contain input data for more than one player
2989     game.team_mode = (num_players > 1);
2990   }
2991
2992 #if 0
2993   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2994         level.game_version);
2995   Debug("game:init:level", "          tape.file_version   == %06d",
2996         tape.file_version);
2997   Debug("game:init:level", "          tape.game_version   == %06d",
2998         tape.game_version);
2999   Debug("game:init:level", "          tape.engine_version == %06d",
3000         tape.engine_version);
3001   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3002         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3003 #endif
3004
3005   // --------------------------------------------------------------------------
3006   // set flags for bugs and changes according to active game engine version
3007   // --------------------------------------------------------------------------
3008
3009   /*
3010     Summary of bugfix:
3011     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3012
3013     Bug was introduced in version:
3014     2.0.1
3015
3016     Bug was fixed in version:
3017     4.2.0.0
3018
3019     Description:
3020     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3021     but the property "can fall" was missing, which caused some levels to be
3022     unsolvable. This was fixed in version 4.2.0.0.
3023
3024     Affected levels/tapes:
3025     An example for a tape that was fixed by this bugfix is tape 029 from the
3026     level set "rnd_sam_bateman".
3027     The wrong behaviour will still be used for all levels or tapes that were
3028     created/recorded with it. An example for this is tape 023 from the level
3029     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3030   */
3031
3032   boolean use_amoeba_dropping_cannot_fall_bug =
3033     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3034       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3035      (tape.playing &&
3036       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3037       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3038
3039   /*
3040     Summary of bugfix/change:
3041     Fixed move speed of elements entering or leaving magic wall.
3042
3043     Fixed/changed in version:
3044     2.0.1
3045
3046     Description:
3047     Before 2.0.1, move speed of elements entering or leaving magic wall was
3048     twice as fast as it is now.
3049     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3050
3051     Affected levels/tapes:
3052     The first condition is generally needed for all levels/tapes before version
3053     2.0.1, which might use the old behaviour before it was changed; known tapes
3054     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3055     The second condition is an exception from the above case and is needed for
3056     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3057     above, but before it was known that this change would break tapes like the
3058     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3059     although the engine version while recording maybe was before 2.0.1. There
3060     are a lot of tapes that are affected by this exception, like tape 006 from
3061     the level set "rnd_conor_mancone".
3062   */
3063
3064   boolean use_old_move_stepsize_for_magic_wall =
3065     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3066      !(tape.playing &&
3067        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3068        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3069
3070   /*
3071     Summary of bugfix/change:
3072     Fixed handling for custom elements that change when pushed by the player.
3073
3074     Fixed/changed in version:
3075     3.1.0
3076
3077     Description:
3078     Before 3.1.0, custom elements that "change when pushing" changed directly
3079     after the player started pushing them (until then handled in "DigField()").
3080     Since 3.1.0, these custom elements are not changed until the "pushing"
3081     move of the element is finished (now handled in "ContinueMoving()").
3082
3083     Affected levels/tapes:
3084     The first condition is generally needed for all levels/tapes before version
3085     3.1.0, which might use the old behaviour before it was changed; known tapes
3086     that are affected are some tapes from the level set "Walpurgis Gardens" by
3087     Jamie Cullen.
3088     The second condition is an exception from the above case and is needed for
3089     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3090     above (including some development versions of 3.1.0), but before it was
3091     known that this change would break tapes like the above and was fixed in
3092     3.1.1, so that the changed behaviour was active although the engine version
3093     while recording maybe was before 3.1.0. There is at least one tape that is
3094     affected by this exception, which is the tape for the one-level set "Bug
3095     Machine" by Juergen Bonhagen.
3096   */
3097
3098   game.use_change_when_pushing_bug =
3099     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3100      !(tape.playing &&
3101        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3102        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3103
3104   /*
3105     Summary of bugfix/change:
3106     Fixed handling for blocking the field the player leaves when moving.
3107
3108     Fixed/changed in version:
3109     3.1.1
3110
3111     Description:
3112     Before 3.1.1, when "block last field when moving" was enabled, the field
3113     the player is leaving when moving was blocked for the time of the move,
3114     and was directly unblocked afterwards. This resulted in the last field
3115     being blocked for exactly one less than the number of frames of one player
3116     move. Additionally, even when blocking was disabled, the last field was
3117     blocked for exactly one frame.
3118     Since 3.1.1, due to changes in player movement handling, the last field
3119     is not blocked at all when blocking is disabled. When blocking is enabled,
3120     the last field is blocked for exactly the number of frames of one player
3121     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3122     last field is blocked for exactly one more than the number of frames of
3123     one player move.
3124
3125     Affected levels/tapes:
3126     (!!! yet to be determined -- probably many !!!)
3127   */
3128
3129   game.use_block_last_field_bug =
3130     (game.engine_version < VERSION_IDENT(3,1,1,0));
3131
3132   /* various special flags and settings for native Emerald Mine game engine */
3133
3134   game_em.use_single_button =
3135     (game.engine_version > VERSION_IDENT(4,0,0,2));
3136
3137   game_em.use_push_delay =
3138     (game.engine_version > VERSION_IDENT(4,3,7,1));
3139
3140   game_em.use_snap_key_bug =
3141     (game.engine_version < VERSION_IDENT(4,0,1,0));
3142
3143   game_em.use_random_bug =
3144     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3145
3146   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3147
3148   game_em.use_old_explosions            = use_old_em_engine;
3149   game_em.use_old_android               = use_old_em_engine;
3150   game_em.use_old_push_elements         = use_old_em_engine;
3151   game_em.use_old_push_into_acid        = use_old_em_engine;
3152
3153   game_em.use_wrap_around               = !use_old_em_engine;
3154
3155   // --------------------------------------------------------------------------
3156
3157   // set maximal allowed number of custom element changes per game frame
3158   game.max_num_changes_per_frame = 1;
3159
3160   // default scan direction: scan playfield from top/left to bottom/right
3161   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3162
3163   // dynamically adjust element properties according to game engine version
3164   InitElementPropertiesEngine(game.engine_version);
3165
3166   // ---------- initialize special element properties -------------------------
3167
3168   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3169   if (use_amoeba_dropping_cannot_fall_bug)
3170     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3171
3172   // ---------- initialize player's initial move delay ------------------------
3173
3174   // dynamically adjust player properties according to level information
3175   for (i = 0; i < MAX_PLAYERS; i++)
3176     game.initial_move_delay_value[i] =
3177       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3178
3179   // dynamically adjust player properties according to game engine version
3180   for (i = 0; i < MAX_PLAYERS; i++)
3181     game.initial_move_delay[i] =
3182       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3183        game.initial_move_delay_value[i] : 0);
3184
3185   // ---------- initialize player's initial push delay ------------------------
3186
3187   // dynamically adjust player properties according to game engine version
3188   game.initial_push_delay_value =
3189     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3190
3191   // ---------- initialize changing elements ----------------------------------
3192
3193   // initialize changing elements information
3194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3195   {
3196     struct ElementInfo *ei = &element_info[i];
3197
3198     // this pointer might have been changed in the level editor
3199     ei->change = &ei->change_page[0];
3200
3201     if (!IS_CUSTOM_ELEMENT(i))
3202     {
3203       ei->change->target_element = EL_EMPTY_SPACE;
3204       ei->change->delay_fixed = 0;
3205       ei->change->delay_random = 0;
3206       ei->change->delay_frames = 1;
3207     }
3208
3209     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3210     {
3211       ei->has_change_event[j] = FALSE;
3212
3213       ei->event_page_nr[j] = 0;
3214       ei->event_page[j] = &ei->change_page[0];
3215     }
3216   }
3217
3218   // add changing elements from pre-defined list
3219   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3220   {
3221     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3222     struct ElementInfo *ei = &element_info[ch_delay->element];
3223
3224     ei->change->target_element       = ch_delay->target_element;
3225     ei->change->delay_fixed          = ch_delay->change_delay;
3226
3227     ei->change->pre_change_function  = ch_delay->pre_change_function;
3228     ei->change->change_function      = ch_delay->change_function;
3229     ei->change->post_change_function = ch_delay->post_change_function;
3230
3231     ei->change->can_change = TRUE;
3232     ei->change->can_change_or_has_action = TRUE;
3233
3234     ei->has_change_event[CE_DELAY] = TRUE;
3235
3236     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3237     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3238   }
3239
3240   // ---------- initialize if element can trigger global animations -----------
3241
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243   {
3244     struct ElementInfo *ei = &element_info[i];
3245
3246     ei->has_anim_event = FALSE;
3247   }
3248
3249   InitGlobalAnimEventsForCustomElements();
3250
3251   // ---------- initialize internal run-time variables ------------------------
3252
3253   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3254   {
3255     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       ei->change_page[j].can_change_or_has_action =
3260         (ei->change_page[j].can_change |
3261          ei->change_page[j].has_action);
3262     }
3263   }
3264
3265   // add change events from custom element configuration
3266   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3267   {
3268     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3269
3270     for (j = 0; j < ei->num_change_pages; j++)
3271     {
3272       if (!ei->change_page[j].can_change_or_has_action)
3273         continue;
3274
3275       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3276       {
3277         // only add event page for the first page found with this event
3278         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3279         {
3280           ei->has_change_event[k] = TRUE;
3281
3282           ei->event_page_nr[k] = j;
3283           ei->event_page[k] = &ei->change_page[j];
3284         }
3285       }
3286     }
3287   }
3288
3289   // ---------- initialize reference elements in change conditions ------------
3290
3291   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3292   {
3293     int element = EL_CUSTOM_START + i;
3294     struct ElementInfo *ei = &element_info[element];
3295
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       int trigger_element = ei->change_page[j].initial_trigger_element;
3299
3300       if (trigger_element >= EL_PREV_CE_8 &&
3301           trigger_element <= EL_NEXT_CE_8)
3302         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3303
3304       ei->change_page[j].trigger_element = trigger_element;
3305     }
3306   }
3307
3308   // ---------- initialize run-time trigger player and element ----------------
3309
3310   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3311   {
3312     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3313
3314     for (j = 0; j < ei->num_change_pages; j++)
3315     {
3316       struct ElementChangeInfo *change = &ei->change_page[j];
3317
3318       change->actual_trigger_element = EL_EMPTY;
3319       change->actual_trigger_player = EL_EMPTY;
3320       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3321       change->actual_trigger_side = CH_SIDE_NONE;
3322       change->actual_trigger_ce_value = 0;
3323       change->actual_trigger_ce_score = 0;
3324       change->actual_trigger_x = -1;
3325       change->actual_trigger_y = -1;
3326     }
3327   }
3328
3329   // ---------- initialize trigger events -------------------------------------
3330
3331   // initialize trigger events information
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3334       trigger_events[i][j] = FALSE;
3335
3336   // add trigger events from element change event properties
3337   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338   {
3339     struct ElementInfo *ei = &element_info[i];
3340
3341     for (j = 0; j < ei->num_change_pages; j++)
3342     {
3343       struct ElementChangeInfo *change = &ei->change_page[j];
3344
3345       if (!change->can_change_or_has_action)
3346         continue;
3347
3348       if (change->has_event[CE_BY_OTHER_ACTION])
3349       {
3350         int trigger_element = change->trigger_element;
3351
3352         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3353         {
3354           if (change->has_event[k])
3355           {
3356             if (IS_GROUP_ELEMENT(trigger_element))
3357             {
3358               struct ElementGroupInfo *group =
3359                 element_info[trigger_element].group;
3360
3361               for (l = 0; l < group->num_elements_resolved; l++)
3362                 trigger_events[group->element_resolved[l]][k] = TRUE;
3363             }
3364             else if (trigger_element == EL_ANY_ELEMENT)
3365               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3366                 trigger_events[l][k] = TRUE;
3367             else
3368               trigger_events[trigger_element][k] = TRUE;
3369           }
3370         }
3371       }
3372     }
3373   }
3374
3375   // ---------- initialize push delay -----------------------------------------
3376
3377   // initialize push delay values to default
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (!IS_CUSTOM_ELEMENT(i))
3381     {
3382       // set default push delay values (corrected since version 3.0.7-1)
3383       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3384       {
3385         element_info[i].push_delay_fixed = 2;
3386         element_info[i].push_delay_random = 8;
3387       }
3388       else
3389       {
3390         element_info[i].push_delay_fixed = 8;
3391         element_info[i].push_delay_random = 8;
3392       }
3393     }
3394   }
3395
3396   // set push delay value for certain elements from pre-defined list
3397   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3398   {
3399     int e = push_delay_list[i].element;
3400
3401     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3402     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3403   }
3404
3405   // set push delay value for Supaplex elements for newer engine versions
3406   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3407   {
3408     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3409     {
3410       if (IS_SP_ELEMENT(i))
3411       {
3412         // set SP push delay to just enough to push under a falling zonk
3413         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3414
3415         element_info[i].push_delay_fixed  = delay;
3416         element_info[i].push_delay_random = 0;
3417       }
3418     }
3419   }
3420
3421   // ---------- initialize move stepsize --------------------------------------
3422
3423   // initialize move stepsize values to default
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3427
3428   // set move stepsize value for certain elements from pre-defined list
3429   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3430   {
3431     int e = move_stepsize_list[i].element;
3432
3433     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3434
3435     // set move stepsize value for certain elements for older engine versions
3436     if (use_old_move_stepsize_for_magic_wall)
3437     {
3438       if (e == EL_MAGIC_WALL_FILLING ||
3439           e == EL_MAGIC_WALL_EMPTYING ||
3440           e == EL_BD_MAGIC_WALL_FILLING ||
3441           e == EL_BD_MAGIC_WALL_EMPTYING)
3442         element_info[e].move_stepsize *= 2;
3443     }
3444   }
3445
3446   // ---------- initialize collect score --------------------------------------
3447
3448   // initialize collect score values for custom elements from initial value
3449   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3450     if (IS_CUSTOM_ELEMENT(i))
3451       element_info[i].collect_score = element_info[i].collect_score_initial;
3452
3453   // ---------- initialize collect count --------------------------------------
3454
3455   // initialize collect count values for non-custom elements
3456   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457     if (!IS_CUSTOM_ELEMENT(i))
3458       element_info[i].collect_count_initial = 0;
3459
3460   // add collect count values for all elements from pre-defined list
3461   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3462     element_info[collect_count_list[i].element].collect_count_initial =
3463       collect_count_list[i].count;
3464
3465   // ---------- initialize access direction -----------------------------------
3466
3467   // initialize access direction values to default (access from every side)
3468   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3469     if (!IS_CUSTOM_ELEMENT(i))
3470       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3471
3472   // set access direction value for certain elements from pre-defined list
3473   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3474     element_info[access_direction_list[i].element].access_direction =
3475       access_direction_list[i].direction;
3476
3477   // ---------- initialize explosion content ----------------------------------
3478   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3479   {
3480     if (IS_CUSTOM_ELEMENT(i))
3481       continue;
3482
3483     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3484     {
3485       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3486
3487       element_info[i].content.e[x][y] =
3488         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3489          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3490          i == EL_PLAYER_3 ? EL_EMERALD :
3491          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3492          i == EL_MOLE ? EL_EMERALD_RED :
3493          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3494          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3495          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3496          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3497          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3498          i == EL_WALL_EMERALD ? EL_EMERALD :
3499          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3500          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3501          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3502          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3503          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3504          i == EL_WALL_PEARL ? EL_PEARL :
3505          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3506          EL_EMPTY);
3507     }
3508   }
3509
3510   // ---------- initialize recursion detection --------------------------------
3511   recursion_loop_depth = 0;
3512   recursion_loop_detected = FALSE;
3513   recursion_loop_element = EL_UNDEFINED;
3514
3515   // ---------- initialize graphics engine ------------------------------------
3516   game.scroll_delay_value =
3517     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3518      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3519      !setup.forced_scroll_delay           ? 0 :
3520      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3521   if (game.forced_scroll_delay_value == -1)
3522     game.scroll_delay_value =
3523       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3524
3525   // ---------- initialize game engine snapshots ------------------------------
3526   for (i = 0; i < MAX_PLAYERS; i++)
3527     game.snapshot.last_action[i] = 0;
3528   game.snapshot.changed_action = FALSE;
3529   game.snapshot.collected_item = FALSE;
3530   game.snapshot.mode =
3531     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3532      SNAPSHOT_MODE_EVERY_STEP :
3533      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3534      SNAPSHOT_MODE_EVERY_MOVE :
3535      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3536      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3537   game.snapshot.save_snapshot = FALSE;
3538
3539   // ---------- initialize level time for Supaplex engine ---------------------
3540   // Supaplex levels with time limit currently unsupported -- should be added
3541   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3542     level.time = 0;
3543
3544   // ---------- initialize flags for handling game actions --------------------
3545
3546   // set flags for game actions to default values
3547   game.use_key_actions = TRUE;
3548   game.use_mouse_actions = FALSE;
3549
3550   // when using Mirror Magic game engine, handle mouse events only
3551   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3552   {
3553     game.use_key_actions = FALSE;
3554     game.use_mouse_actions = TRUE;
3555   }
3556
3557   // check for custom elements with mouse click events
3558   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3559   {
3560     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3561     {
3562       int element = EL_CUSTOM_START + i;
3563
3564       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3565           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3566           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3567           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3568         game.use_mouse_actions = TRUE;
3569     }
3570   }
3571 }
3572
3573 static int get_num_special_action(int element, int action_first,
3574                                   int action_last)
3575 {
3576   int num_special_action = 0;
3577   int i, j;
3578
3579   for (i = action_first; i <= action_last; i++)
3580   {
3581     boolean found = FALSE;
3582
3583     for (j = 0; j < NUM_DIRECTIONS; j++)
3584       if (el_act_dir2img(element, i, j) !=
3585           el_act_dir2img(element, ACTION_DEFAULT, j))
3586         found = TRUE;
3587
3588     if (found)
3589       num_special_action++;
3590     else
3591       break;
3592   }
3593
3594   return num_special_action;
3595 }
3596
3597
3598 // ============================================================================
3599 // InitGame()
3600 // ----------------------------------------------------------------------------
3601 // initialize and start new game
3602 // ============================================================================
3603
3604 #if DEBUG_INIT_PLAYER
3605 static void DebugPrintPlayerStatus(char *message)
3606 {
3607   int i;
3608
3609   if (!options.debug)
3610     return;
3611
3612   Debug("game:init:player", "%s:", message);
3613
3614   for (i = 0; i < MAX_PLAYERS; i++)
3615   {
3616     struct PlayerInfo *player = &stored_player[i];
3617
3618     Debug("game:init:player",
3619           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3620           i + 1,
3621           player->present,
3622           player->connected,
3623           player->connected_locally,
3624           player->connected_network,
3625           player->active,
3626           (local_player == player ? " (local player)" : ""));
3627   }
3628 }
3629 #endif
3630
3631 void InitGame(void)
3632 {
3633   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3634   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3635   int fade_mask = REDRAW_FIELD;
3636   boolean restarting = (game_status == GAME_MODE_PLAYING);
3637   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3638   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3639   int initial_move_dir = MV_DOWN;
3640   int i, j, x, y;
3641
3642   // required here to update video display before fading (FIX THIS)
3643   DrawMaskedBorder(REDRAW_DOOR_2);
3644
3645   if (!game.restart_level)
3646     CloseDoor(DOOR_CLOSE_1);
3647
3648   if (restarting)
3649   {
3650     // force fading out global animations displayed during game play
3651     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3652   }
3653   else
3654   {
3655     SetGameStatus(GAME_MODE_PLAYING);
3656   }
3657
3658   if (level_editor_test_game)
3659     FadeSkipNextFadeOut();
3660   else
3661     FadeSetEnterScreen();
3662
3663   if (CheckFadeAll())
3664     fade_mask = REDRAW_ALL;
3665
3666   FadeLevelSoundsAndMusic();
3667
3668   ExpireSoundLoops(TRUE);
3669
3670   FadeOut(fade_mask);
3671
3672   if (restarting)
3673   {
3674     // force restarting global animations displayed during game play
3675     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3676
3677     // this is required for "transforming" fade modes like cross-fading
3678     // (else global animations will be stopped, but not restarted here)
3679     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3680
3681     SetGameStatus(GAME_MODE_PLAYING);
3682   }
3683
3684   if (level_editor_test_game)
3685     FadeSkipNextFadeIn();
3686
3687   // needed if different viewport properties defined for playing
3688   ChangeViewportPropertiesIfNeeded();
3689
3690   ClearField();
3691
3692   DrawCompleteVideoDisplay();
3693
3694   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3695
3696   InitGameEngine();
3697   InitGameControlValues();
3698
3699   if (tape.recording)
3700   {
3701     // initialize tape actions from game when recording tape
3702     tape.use_key_actions   = game.use_key_actions;
3703     tape.use_mouse_actions = game.use_mouse_actions;
3704
3705     // initialize visible playfield size when recording tape (for team mode)
3706     tape.scr_fieldx = SCR_FIELDX;
3707     tape.scr_fieldy = SCR_FIELDY;
3708   }
3709
3710   // don't play tapes over network
3711   network_playing = (network.enabled && !tape.playing);
3712
3713   for (i = 0; i < MAX_PLAYERS; i++)
3714   {
3715     struct PlayerInfo *player = &stored_player[i];
3716
3717     player->index_nr = i;
3718     player->index_bit = (1 << i);
3719     player->element_nr = EL_PLAYER_1 + i;
3720
3721     player->present = FALSE;
3722     player->active = FALSE;
3723     player->mapped = FALSE;
3724
3725     player->killed = FALSE;
3726     player->reanimated = FALSE;
3727     player->buried = FALSE;
3728
3729     player->action = 0;
3730     player->effective_action = 0;
3731     player->programmed_action = 0;
3732     player->snap_action = 0;
3733
3734     player->mouse_action.lx = 0;
3735     player->mouse_action.ly = 0;
3736     player->mouse_action.button = 0;
3737     player->mouse_action.button_hint = 0;
3738
3739     player->effective_mouse_action.lx = 0;
3740     player->effective_mouse_action.ly = 0;
3741     player->effective_mouse_action.button = 0;
3742     player->effective_mouse_action.button_hint = 0;
3743
3744     for (j = 0; j < MAX_NUM_KEYS; j++)
3745       player->key[j] = FALSE;
3746
3747     player->num_white_keys = 0;
3748
3749     player->dynabomb_count = 0;
3750     player->dynabomb_size = 1;
3751     player->dynabombs_left = 0;
3752     player->dynabomb_xl = FALSE;
3753
3754     player->MovDir = initial_move_dir;
3755     player->MovPos = 0;
3756     player->GfxPos = 0;
3757     player->GfxDir = initial_move_dir;
3758     player->GfxAction = ACTION_DEFAULT;
3759     player->Frame = 0;
3760     player->StepFrame = 0;
3761
3762     player->initial_element = player->element_nr;
3763     player->artwork_element =
3764       (level.use_artwork_element[i] ? level.artwork_element[i] :
3765        player->element_nr);
3766     player->use_murphy = FALSE;
3767
3768     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3769     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3770
3771     player->gravity = level.initial_player_gravity[i];
3772
3773     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3774
3775     player->actual_frame_counter.count = 0;
3776     player->actual_frame_counter.value = 1;
3777
3778     player->step_counter = 0;
3779
3780     player->last_move_dir = initial_move_dir;
3781
3782     player->is_active = FALSE;
3783
3784     player->is_waiting = FALSE;
3785     player->is_moving = FALSE;
3786     player->is_auto_moving = FALSE;
3787     player->is_digging = FALSE;
3788     player->is_snapping = FALSE;
3789     player->is_collecting = FALSE;
3790     player->is_pushing = FALSE;
3791     player->is_switching = FALSE;
3792     player->is_dropping = FALSE;
3793     player->is_dropping_pressed = FALSE;
3794
3795     player->is_bored = FALSE;
3796     player->is_sleeping = FALSE;
3797
3798     player->was_waiting = TRUE;
3799     player->was_moving = FALSE;
3800     player->was_snapping = FALSE;
3801     player->was_dropping = FALSE;
3802
3803     player->force_dropping = FALSE;
3804
3805     player->frame_counter_bored = -1;
3806     player->frame_counter_sleeping = -1;
3807
3808     player->anim_delay_counter = 0;
3809     player->post_delay_counter = 0;
3810
3811     player->dir_waiting = initial_move_dir;
3812     player->action_waiting = ACTION_DEFAULT;
3813     player->last_action_waiting = ACTION_DEFAULT;
3814     player->special_action_bored = ACTION_DEFAULT;
3815     player->special_action_sleeping = ACTION_DEFAULT;
3816
3817     player->switch_x = -1;
3818     player->switch_y = -1;
3819
3820     player->drop_x = -1;
3821     player->drop_y = -1;
3822
3823     player->show_envelope = 0;
3824
3825     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3826
3827     player->push_delay       = -1;      // initialized when pushing starts
3828     player->push_delay_value = game.initial_push_delay_value;
3829
3830     player->drop_delay = 0;
3831     player->drop_pressed_delay = 0;
3832
3833     player->last_jx = -1;
3834     player->last_jy = -1;
3835     player->jx = -1;
3836     player->jy = -1;
3837
3838     player->shield_normal_time_left = 0;
3839     player->shield_deadly_time_left = 0;
3840
3841     player->last_removed_element = EL_UNDEFINED;
3842
3843     player->inventory_infinite_element = EL_UNDEFINED;
3844     player->inventory_size = 0;
3845
3846     if (level.use_initial_inventory[i])
3847     {
3848       for (j = 0; j < level.initial_inventory_size[i]; j++)
3849       {
3850         int element = level.initial_inventory_content[i][j];
3851         int collect_count = element_info[element].collect_count_initial;
3852         int k;
3853
3854         if (!IS_CUSTOM_ELEMENT(element))
3855           collect_count = 1;
3856
3857         if (collect_count == 0)
3858           player->inventory_infinite_element = element;
3859         else
3860           for (k = 0; k < collect_count; k++)
3861             if (player->inventory_size < MAX_INVENTORY_SIZE)
3862               player->inventory_element[player->inventory_size++] = element;
3863       }
3864     }
3865
3866     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3867     SnapField(player, 0, 0);
3868
3869     map_player_action[i] = i;
3870   }
3871
3872   network_player_action_received = FALSE;
3873
3874   // initial null action
3875   if (network_playing)
3876     SendToServer_MovePlayer(MV_NONE);
3877
3878   FrameCounter = 0;
3879   TimeFrames = 0;
3880   TimePlayed = 0;
3881   TimeLeft = level.time;
3882
3883   TapeTimeFrames = 0;
3884   TapeTime = 0;
3885
3886   ScreenMovDir = MV_NONE;
3887   ScreenMovPos = 0;
3888   ScreenGfxPos = 0;
3889
3890   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3891
3892   game.robot_wheel_x = -1;
3893   game.robot_wheel_y = -1;
3894
3895   game.exit_x = -1;
3896   game.exit_y = -1;
3897
3898   game.all_players_gone = FALSE;
3899
3900   game.LevelSolved = FALSE;
3901   game.GameOver = FALSE;
3902
3903   game.GamePlayed = !tape.playing;
3904
3905   game.LevelSolved_GameWon = FALSE;
3906   game.LevelSolved_GameEnd = FALSE;
3907   game.LevelSolved_SaveTape = FALSE;
3908   game.LevelSolved_SaveScore = FALSE;
3909
3910   game.LevelSolved_CountingTime = 0;
3911   game.LevelSolved_CountingScore = 0;
3912   game.LevelSolved_CountingHealth = 0;
3913
3914   game.RestartGameRequested = FALSE;
3915
3916   game.panel.active = TRUE;
3917
3918   game.no_level_time_limit = (level.time == 0);
3919   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3920
3921   game.yamyam_content_nr = 0;
3922   game.robot_wheel_active = FALSE;
3923   game.magic_wall_active = FALSE;
3924   game.magic_wall_time_left = 0;
3925   game.light_time_left = 0;
3926   game.timegate_time_left = 0;
3927   game.switchgate_pos = 0;
3928   game.wind_direction = level.wind_direction_initial;
3929
3930   game.time_final = 0;
3931   game.score_time_final = 0;
3932
3933   game.score = 0;
3934   game.score_final = 0;
3935
3936   game.health = MAX_HEALTH;
3937   game.health_final = MAX_HEALTH;
3938
3939   game.gems_still_needed = level.gems_needed;
3940   game.sokoban_fields_still_needed = 0;
3941   game.sokoban_objects_still_needed = 0;
3942   game.lights_still_needed = 0;
3943   game.players_still_needed = 0;
3944   game.friends_still_needed = 0;
3945
3946   game.lenses_time_left = 0;
3947   game.magnify_time_left = 0;
3948
3949   game.ball_active = level.ball_active_initial;
3950   game.ball_content_nr = 0;
3951
3952   game.explosions_delayed = TRUE;
3953
3954   game.envelope_active = FALSE;
3955
3956   // special case: set custom artwork setting to initial value
3957   game.use_masked_elements = game.use_masked_elements_initial;
3958
3959   for (i = 0; i < NUM_BELTS; i++)
3960   {
3961     game.belt_dir[i] = MV_NONE;
3962     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3963   }
3964
3965   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3966     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3967
3968 #if DEBUG_INIT_PLAYER
3969   DebugPrintPlayerStatus("Player status at level initialization");
3970 #endif
3971
3972   SCAN_PLAYFIELD(x, y)
3973   {
3974     Tile[x][y] = Last[x][y] = level.field[x][y];
3975     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3976     ChangeDelay[x][y] = 0;
3977     ChangePage[x][y] = -1;
3978     CustomValue[x][y] = 0;              // initialized in InitField()
3979     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3980     AmoebaNr[x][y] = 0;
3981     WasJustMoving[x][y] = 0;
3982     WasJustFalling[x][y] = 0;
3983     CheckCollision[x][y] = 0;
3984     CheckImpact[x][y] = 0;
3985     Stop[x][y] = FALSE;
3986     Pushed[x][y] = FALSE;
3987
3988     ChangeCount[x][y] = 0;
3989     ChangeEvent[x][y] = -1;
3990
3991     ExplodePhase[x][y] = 0;
3992     ExplodeDelay[x][y] = 0;
3993     ExplodeField[x][y] = EX_TYPE_NONE;
3994
3995     RunnerVisit[x][y] = 0;
3996     PlayerVisit[x][y] = 0;
3997
3998     GfxFrame[x][y] = 0;
3999     GfxRandom[x][y] = INIT_GFX_RANDOM();
4000     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4001     GfxElement[x][y] = EL_UNDEFINED;
4002     GfxElementEmpty[x][y] = EL_EMPTY;
4003     GfxAction[x][y] = ACTION_DEFAULT;
4004     GfxDir[x][y] = MV_NONE;
4005     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4006   }
4007
4008   SCAN_PLAYFIELD(x, y)
4009   {
4010     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4011       emulate_bd = FALSE;
4012     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4013       emulate_sp = FALSE;
4014
4015     InitField(x, y, TRUE);
4016
4017     ResetGfxAnimation(x, y);
4018   }
4019
4020   InitBeltMovement();
4021
4022   // required if level does not contain any "empty space" element
4023   if (element_info[EL_EMPTY].use_gfx_element)
4024     game.use_masked_elements = TRUE;
4025
4026   for (i = 0; i < MAX_PLAYERS; i++)
4027   {
4028     struct PlayerInfo *player = &stored_player[i];
4029
4030     // set number of special actions for bored and sleeping animation
4031     player->num_special_action_bored =
4032       get_num_special_action(player->artwork_element,
4033                              ACTION_BORING_1, ACTION_BORING_LAST);
4034     player->num_special_action_sleeping =
4035       get_num_special_action(player->artwork_element,
4036                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4037   }
4038
4039   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4040                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4041
4042   // initialize type of slippery elements
4043   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4044   {
4045     if (!IS_CUSTOM_ELEMENT(i))
4046     {
4047       // default: elements slip down either to the left or right randomly
4048       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4049
4050       // SP style elements prefer to slip down on the left side
4051       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4052         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4053
4054       // BD style elements prefer to slip down on the left side
4055       if (game.emulation == EMU_BOULDERDASH)
4056         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4057     }
4058   }
4059
4060   // initialize explosion and ignition delay
4061   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4062   {
4063     if (!IS_CUSTOM_ELEMENT(i))
4064     {
4065       int num_phase = 8;
4066       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4067                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4068                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4069       int last_phase = (num_phase + 1) * delay;
4070       int half_phase = (num_phase / 2) * delay;
4071
4072       element_info[i].explosion_delay = last_phase - 1;
4073       element_info[i].ignition_delay = half_phase;
4074
4075       if (i == EL_BLACK_ORB)
4076         element_info[i].ignition_delay = 1;
4077     }
4078   }
4079
4080   // correct non-moving belts to start moving left
4081   for (i = 0; i < NUM_BELTS; i++)
4082     if (game.belt_dir[i] == MV_NONE)
4083       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4084
4085 #if USE_NEW_PLAYER_ASSIGNMENTS
4086   // use preferred player also in local single-player mode
4087   if (!network.enabled && !game.team_mode)
4088   {
4089     int new_index_nr = setup.network_player_nr;
4090
4091     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4092     {
4093       for (i = 0; i < MAX_PLAYERS; i++)
4094         stored_player[i].connected_locally = FALSE;
4095
4096       stored_player[new_index_nr].connected_locally = TRUE;
4097     }
4098   }
4099
4100   for (i = 0; i < MAX_PLAYERS; i++)
4101   {
4102     stored_player[i].connected = FALSE;
4103
4104     // in network game mode, the local player might not be the first player
4105     if (stored_player[i].connected_locally)
4106       local_player = &stored_player[i];
4107   }
4108
4109   if (!network.enabled)
4110     local_player->connected = TRUE;
4111
4112   if (tape.playing)
4113   {
4114     for (i = 0; i < MAX_PLAYERS; i++)
4115       stored_player[i].connected = tape.player_participates[i];
4116   }
4117   else if (network.enabled)
4118   {
4119     // add team mode players connected over the network (needed for correct
4120     // assignment of player figures from level to locally playing players)
4121
4122     for (i = 0; i < MAX_PLAYERS; i++)
4123       if (stored_player[i].connected_network)
4124         stored_player[i].connected = TRUE;
4125   }
4126   else if (game.team_mode)
4127   {
4128     // try to guess locally connected team mode players (needed for correct
4129     // assignment of player figures from level to locally playing players)
4130
4131     for (i = 0; i < MAX_PLAYERS; i++)
4132       if (setup.input[i].use_joystick ||
4133           setup.input[i].key.left != KSYM_UNDEFINED)
4134         stored_player[i].connected = TRUE;
4135   }
4136
4137 #if DEBUG_INIT_PLAYER
4138   DebugPrintPlayerStatus("Player status after level initialization");
4139 #endif
4140
4141 #if DEBUG_INIT_PLAYER
4142   Debug("game:init:player", "Reassigning players ...");
4143 #endif
4144
4145   // check if any connected player was not found in playfield
4146   for (i = 0; i < MAX_PLAYERS; i++)
4147   {
4148     struct PlayerInfo *player = &stored_player[i];
4149
4150     if (player->connected && !player->present)
4151     {
4152       struct PlayerInfo *field_player = NULL;
4153
4154 #if DEBUG_INIT_PLAYER
4155       Debug("game:init:player",
4156             "- looking for field player for player %d ...", i + 1);
4157 #endif
4158
4159       // assign first free player found that is present in the playfield
4160
4161       // first try: look for unmapped playfield player that is not connected
4162       for (j = 0; j < MAX_PLAYERS; j++)
4163         if (field_player == NULL &&
4164             stored_player[j].present &&
4165             !stored_player[j].mapped &&
4166             !stored_player[j].connected)
4167           field_player = &stored_player[j];
4168
4169       // second try: look for *any* unmapped playfield player
4170       for (j = 0; j < MAX_PLAYERS; j++)
4171         if (field_player == NULL &&
4172             stored_player[j].present &&
4173             !stored_player[j].mapped)
4174           field_player = &stored_player[j];
4175
4176       if (field_player != NULL)
4177       {
4178         int jx = field_player->jx, jy = field_player->jy;
4179
4180 #if DEBUG_INIT_PLAYER
4181         Debug("game:init:player", "- found player %d",
4182               field_player->index_nr + 1);
4183 #endif
4184
4185         player->present = FALSE;
4186         player->active = FALSE;
4187
4188         field_player->present = TRUE;
4189         field_player->active = TRUE;
4190
4191         /*
4192         player->initial_element = field_player->initial_element;
4193         player->artwork_element = field_player->artwork_element;
4194
4195         player->block_last_field       = field_player->block_last_field;
4196         player->block_delay_adjustment = field_player->block_delay_adjustment;
4197         */
4198
4199         StorePlayer[jx][jy] = field_player->element_nr;
4200
4201         field_player->jx = field_player->last_jx = jx;
4202         field_player->jy = field_player->last_jy = jy;
4203
4204         if (local_player == player)
4205           local_player = field_player;
4206
4207         map_player_action[field_player->index_nr] = i;
4208
4209         field_player->mapped = TRUE;
4210
4211 #if DEBUG_INIT_PLAYER
4212         Debug("game:init:player", "- map_player_action[%d] == %d",
4213               field_player->index_nr + 1, i + 1);
4214 #endif
4215       }
4216     }
4217
4218     if (player->connected && player->present)
4219       player->mapped = TRUE;
4220   }
4221
4222 #if DEBUG_INIT_PLAYER
4223   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4224 #endif
4225
4226 #else
4227
4228   // check if any connected player was not found in playfield
4229   for (i = 0; i < MAX_PLAYERS; i++)
4230   {
4231     struct PlayerInfo *player = &stored_player[i];
4232
4233     if (player->connected && !player->present)
4234     {
4235       for (j = 0; j < MAX_PLAYERS; j++)
4236       {
4237         struct PlayerInfo *field_player = &stored_player[j];
4238         int jx = field_player->jx, jy = field_player->jy;
4239
4240         // assign first free player found that is present in the playfield
4241         if (field_player->present && !field_player->connected)
4242         {
4243           player->present = TRUE;
4244           player->active = TRUE;
4245
4246           field_player->present = FALSE;
4247           field_player->active = FALSE;
4248
4249           player->initial_element = field_player->initial_element;
4250           player->artwork_element = field_player->artwork_element;
4251
4252           player->block_last_field       = field_player->block_last_field;
4253           player->block_delay_adjustment = field_player->block_delay_adjustment;
4254
4255           StorePlayer[jx][jy] = player->element_nr;
4256
4257           player->jx = player->last_jx = jx;
4258           player->jy = player->last_jy = jy;
4259
4260           break;
4261         }
4262       }
4263     }
4264   }
4265 #endif
4266
4267 #if 0
4268   Debug("game:init:player", "local_player->present == %d",
4269         local_player->present);
4270 #endif
4271
4272   // set focus to local player for network games, else to all players
4273   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4274   game.centered_player_nr_next = game.centered_player_nr;
4275   game.set_centered_player = FALSE;
4276   game.set_centered_player_wrap = FALSE;
4277
4278   if (network_playing && tape.recording)
4279   {
4280     // store client dependent player focus when recording network games
4281     tape.centered_player_nr_next = game.centered_player_nr_next;
4282     tape.set_centered_player = TRUE;
4283   }
4284
4285   if (tape.playing)
4286   {
4287     // when playing a tape, eliminate all players who do not participate
4288
4289 #if USE_NEW_PLAYER_ASSIGNMENTS
4290
4291     if (!game.team_mode)
4292     {
4293       for (i = 0; i < MAX_PLAYERS; i++)
4294       {
4295         if (stored_player[i].active &&
4296             !tape.player_participates[map_player_action[i]])
4297         {
4298           struct PlayerInfo *player = &stored_player[i];
4299           int jx = player->jx, jy = player->jy;
4300
4301 #if DEBUG_INIT_PLAYER
4302           Debug("game:init:player", "Removing player %d at (%d, %d)",
4303                 i + 1, jx, jy);
4304 #endif
4305
4306           player->active = FALSE;
4307           StorePlayer[jx][jy] = 0;
4308           Tile[jx][jy] = EL_EMPTY;
4309         }
4310       }
4311     }
4312
4313 #else
4314
4315     for (i = 0; i < MAX_PLAYERS; i++)
4316     {
4317       if (stored_player[i].active &&
4318           !tape.player_participates[i])
4319       {
4320         struct PlayerInfo *player = &stored_player[i];
4321         int jx = player->jx, jy = player->jy;
4322
4323         player->active = FALSE;
4324         StorePlayer[jx][jy] = 0;
4325         Tile[jx][jy] = EL_EMPTY;
4326       }
4327     }
4328 #endif
4329   }
4330   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4331   {
4332     // when in single player mode, eliminate all but the local player
4333
4334     for (i = 0; i < MAX_PLAYERS; i++)
4335     {
4336       struct PlayerInfo *player = &stored_player[i];
4337
4338       if (player->active && player != local_player)
4339       {
4340         int jx = player->jx, jy = player->jy;
4341
4342         player->active = FALSE;
4343         player->present = FALSE;
4344
4345         StorePlayer[jx][jy] = 0;
4346         Tile[jx][jy] = EL_EMPTY;
4347       }
4348     }
4349   }
4350
4351   for (i = 0; i < MAX_PLAYERS; i++)
4352     if (stored_player[i].active)
4353       game.players_still_needed++;
4354
4355   if (level.solved_by_one_player)
4356     game.players_still_needed = 1;
4357
4358   // when recording the game, store which players take part in the game
4359   if (tape.recording)
4360   {
4361 #if USE_NEW_PLAYER_ASSIGNMENTS
4362     for (i = 0; i < MAX_PLAYERS; i++)
4363       if (stored_player[i].connected)
4364         tape.player_participates[i] = TRUE;
4365 #else
4366     for (i = 0; i < MAX_PLAYERS; i++)
4367       if (stored_player[i].active)
4368         tape.player_participates[i] = TRUE;
4369 #endif
4370   }
4371
4372 #if DEBUG_INIT_PLAYER
4373   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4374 #endif
4375
4376   if (BorderElement == EL_EMPTY)
4377   {
4378     SBX_Left = 0;
4379     SBX_Right = lev_fieldx - SCR_FIELDX;
4380     SBY_Upper = 0;
4381     SBY_Lower = lev_fieldy - SCR_FIELDY;
4382   }
4383   else
4384   {
4385     SBX_Left = -1;
4386     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4387     SBY_Upper = -1;
4388     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4389   }
4390
4391   if (full_lev_fieldx <= SCR_FIELDX)
4392     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4393   if (full_lev_fieldy <= SCR_FIELDY)
4394     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4395
4396   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4397     SBX_Left--;
4398   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4399     SBY_Upper--;
4400
4401   // if local player not found, look for custom element that might create
4402   // the player (make some assumptions about the right custom element)
4403   if (!local_player->present)
4404   {
4405     int start_x = 0, start_y = 0;
4406     int found_rating = 0;
4407     int found_element = EL_UNDEFINED;
4408     int player_nr = local_player->index_nr;
4409
4410     SCAN_PLAYFIELD(x, y)
4411     {
4412       int element = Tile[x][y];
4413       int content;
4414       int xx, yy;
4415       boolean is_player;
4416
4417       if (level.use_start_element[player_nr] &&
4418           level.start_element[player_nr] == element &&
4419           found_rating < 4)
4420       {
4421         start_x = x;
4422         start_y = y;
4423
4424         found_rating = 4;
4425         found_element = element;
4426       }
4427
4428       if (!IS_CUSTOM_ELEMENT(element))
4429         continue;
4430
4431       if (CAN_CHANGE(element))
4432       {
4433         for (i = 0; i < element_info[element].num_change_pages; i++)
4434         {
4435           // check for player created from custom element as single target
4436           content = element_info[element].change_page[i].target_element;
4437           is_player = IS_PLAYER_ELEMENT(content);
4438
4439           if (is_player && (found_rating < 3 ||
4440                             (found_rating == 3 && element < found_element)))
4441           {
4442             start_x = x;
4443             start_y = y;
4444
4445             found_rating = 3;
4446             found_element = element;
4447           }
4448         }
4449       }
4450
4451       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4452       {
4453         // check for player created from custom element as explosion content
4454         content = element_info[element].content.e[xx][yy];
4455         is_player = IS_PLAYER_ELEMENT(content);
4456
4457         if (is_player && (found_rating < 2 ||
4458                           (found_rating == 2 && element < found_element)))
4459         {
4460           start_x = x + xx - 1;
4461           start_y = y + yy - 1;
4462
4463           found_rating = 2;
4464           found_element = element;
4465         }
4466
4467         if (!CAN_CHANGE(element))
4468           continue;
4469
4470         for (i = 0; i < element_info[element].num_change_pages; i++)
4471         {
4472           // check for player created from custom element as extended target
4473           content =
4474             element_info[element].change_page[i].target_content.e[xx][yy];
4475
4476           is_player = IS_PLAYER_ELEMENT(content);
4477
4478           if (is_player && (found_rating < 1 ||
4479                             (found_rating == 1 && element < found_element)))
4480           {
4481             start_x = x + xx - 1;
4482             start_y = y + yy - 1;
4483
4484             found_rating = 1;
4485             found_element = element;
4486           }
4487         }
4488       }
4489     }
4490
4491     scroll_x = SCROLL_POSITION_X(start_x);
4492     scroll_y = SCROLL_POSITION_Y(start_y);
4493   }
4494   else
4495   {
4496     scroll_x = SCROLL_POSITION_X(local_player->jx);
4497     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4498   }
4499
4500   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4501     scroll_x = game.forced_scroll_x;
4502   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4503     scroll_y = game.forced_scroll_y;
4504
4505   // !!! FIX THIS (START) !!!
4506   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4507   {
4508     InitGameEngine_BD();
4509   }
4510   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4511   {
4512     InitGameEngine_EM();
4513   }
4514   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4515   {
4516     InitGameEngine_SP();
4517   }
4518   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4519   {
4520     InitGameEngine_MM();
4521   }
4522   else
4523   {
4524     DrawLevel(REDRAW_FIELD);
4525     DrawAllPlayers();
4526
4527     // after drawing the level, correct some elements
4528     if (game.timegate_time_left == 0)
4529       CloseAllOpenTimegates();
4530   }
4531
4532   // blit playfield from scroll buffer to normal back buffer for fading in
4533   BlitScreenToBitmap(backbuffer);
4534   // !!! FIX THIS (END) !!!
4535
4536   DrawMaskedBorder(fade_mask);
4537
4538   FadeIn(fade_mask);
4539
4540 #if 1
4541   // full screen redraw is required at this point in the following cases:
4542   // - special editor door undrawn when game was started from level editor
4543   // - drawing area (playfield) was changed and has to be removed completely
4544   redraw_mask = REDRAW_ALL;
4545   BackToFront();
4546 #endif
4547
4548   if (!game.restart_level)
4549   {
4550     // copy default game door content to main double buffer
4551
4552     // !!! CHECK AGAIN !!!
4553     SetPanelBackground();
4554     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4555     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4556   }
4557
4558   SetPanelBackground();
4559   SetDrawBackgroundMask(REDRAW_DOOR_1);
4560
4561   UpdateAndDisplayGameControlValues();
4562
4563   if (!game.restart_level)
4564   {
4565     UnmapGameButtons();
4566     UnmapTapeButtons();
4567
4568     FreeGameButtons();
4569     CreateGameButtons();
4570
4571     MapGameButtons();
4572     MapTapeButtons();
4573
4574     // copy actual game door content to door double buffer for OpenDoor()
4575     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4576
4577     OpenDoor(DOOR_OPEN_ALL);
4578
4579     KeyboardAutoRepeatOffUnlessAutoplay();
4580
4581 #if DEBUG_INIT_PLAYER
4582     DebugPrintPlayerStatus("Player status (final)");
4583 #endif
4584   }
4585
4586   UnmapAllGadgets();
4587
4588   MapGameButtons();
4589   MapTapeButtons();
4590
4591   if (!game.restart_level && !tape.playing)
4592   {
4593     LevelStats_incPlayed(level_nr);
4594
4595     SaveLevelSetup_SeriesInfo();
4596   }
4597
4598   game.restart_level = FALSE;
4599   game.request_active = FALSE;
4600
4601   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4602     InitGameActions_MM();
4603
4604   SaveEngineSnapshotToListInitial();
4605
4606   if (!game.restart_level)
4607   {
4608     PlaySound(SND_GAME_STARTING);
4609
4610     if (setup.sound_music)
4611       PlayLevelMusic();
4612   }
4613
4614   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4615 }
4616
4617 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4618                         int actual_player_x, int actual_player_y)
4619 {
4620   // this is used for non-R'n'D game engines to update certain engine values
4621
4622   // needed to determine if sounds are played within the visible screen area
4623   scroll_x = actual_scroll_x;
4624   scroll_y = actual_scroll_y;
4625
4626   // needed to get player position for "follow finger" playing input method
4627   local_player->jx = actual_player_x;
4628   local_player->jy = actual_player_y;
4629 }
4630
4631 void InitMovDir(int x, int y)
4632 {
4633   int i, element = Tile[x][y];
4634   static int xy[4][2] =
4635   {
4636     {  0, +1 },
4637     { +1,  0 },
4638     {  0, -1 },
4639     { -1,  0 }
4640   };
4641   static int direction[3][4] =
4642   {
4643     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4644     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4645     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4646   };
4647
4648   switch (element)
4649   {
4650     case EL_BUG_RIGHT:
4651     case EL_BUG_UP:
4652     case EL_BUG_LEFT:
4653     case EL_BUG_DOWN:
4654       Tile[x][y] = EL_BUG;
4655       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4656       break;
4657
4658     case EL_SPACESHIP_RIGHT:
4659     case EL_SPACESHIP_UP:
4660     case EL_SPACESHIP_LEFT:
4661     case EL_SPACESHIP_DOWN:
4662       Tile[x][y] = EL_SPACESHIP;
4663       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4664       break;
4665
4666     case EL_BD_BUTTERFLY_RIGHT:
4667     case EL_BD_BUTTERFLY_UP:
4668     case EL_BD_BUTTERFLY_LEFT:
4669     case EL_BD_BUTTERFLY_DOWN:
4670       Tile[x][y] = EL_BD_BUTTERFLY;
4671       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4672       break;
4673
4674     case EL_BD_FIREFLY_RIGHT:
4675     case EL_BD_FIREFLY_UP:
4676     case EL_BD_FIREFLY_LEFT:
4677     case EL_BD_FIREFLY_DOWN:
4678       Tile[x][y] = EL_BD_FIREFLY;
4679       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4680       break;
4681
4682     case EL_PACMAN_RIGHT:
4683     case EL_PACMAN_UP:
4684     case EL_PACMAN_LEFT:
4685     case EL_PACMAN_DOWN:
4686       Tile[x][y] = EL_PACMAN;
4687       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4688       break;
4689
4690     case EL_YAMYAM_LEFT:
4691     case EL_YAMYAM_RIGHT:
4692     case EL_YAMYAM_UP:
4693     case EL_YAMYAM_DOWN:
4694       Tile[x][y] = EL_YAMYAM;
4695       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4696       break;
4697
4698     case EL_SP_SNIKSNAK:
4699       MovDir[x][y] = MV_UP;
4700       break;
4701
4702     case EL_SP_ELECTRON:
4703       MovDir[x][y] = MV_LEFT;
4704       break;
4705
4706     case EL_MOLE_LEFT:
4707     case EL_MOLE_RIGHT:
4708     case EL_MOLE_UP:
4709     case EL_MOLE_DOWN:
4710       Tile[x][y] = EL_MOLE;
4711       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4712       break;
4713
4714     case EL_SPRING_LEFT:
4715     case EL_SPRING_RIGHT:
4716       Tile[x][y] = EL_SPRING;
4717       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4718       break;
4719
4720     default:
4721       if (IS_CUSTOM_ELEMENT(element))
4722       {
4723         struct ElementInfo *ei = &element_info[element];
4724         int move_direction_initial = ei->move_direction_initial;
4725         int move_pattern = ei->move_pattern;
4726
4727         if (move_direction_initial == MV_START_PREVIOUS)
4728         {
4729           if (MovDir[x][y] != MV_NONE)
4730             return;
4731
4732           move_direction_initial = MV_START_AUTOMATIC;
4733         }
4734
4735         if (move_direction_initial == MV_START_RANDOM)
4736           MovDir[x][y] = 1 << RND(4);
4737         else if (move_direction_initial & MV_ANY_DIRECTION)
4738           MovDir[x][y] = move_direction_initial;
4739         else if (move_pattern == MV_ALL_DIRECTIONS ||
4740                  move_pattern == MV_TURNING_LEFT ||
4741                  move_pattern == MV_TURNING_RIGHT ||
4742                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4743                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4744                  move_pattern == MV_TURNING_RANDOM)
4745           MovDir[x][y] = 1 << RND(4);
4746         else if (move_pattern == MV_HORIZONTAL)
4747           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4748         else if (move_pattern == MV_VERTICAL)
4749           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4750         else if (move_pattern & MV_ANY_DIRECTION)
4751           MovDir[x][y] = element_info[element].move_pattern;
4752         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4753                  move_pattern == MV_ALONG_RIGHT_SIDE)
4754         {
4755           // use random direction as default start direction
4756           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4757             MovDir[x][y] = 1 << RND(4);
4758
4759           for (i = 0; i < NUM_DIRECTIONS; i++)
4760           {
4761             int x1 = x + xy[i][0];
4762             int y1 = y + xy[i][1];
4763
4764             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4765             {
4766               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4767                 MovDir[x][y] = direction[0][i];
4768               else
4769                 MovDir[x][y] = direction[1][i];
4770
4771               break;
4772             }
4773           }
4774         }                
4775       }
4776       else
4777       {
4778         MovDir[x][y] = 1 << RND(4);
4779
4780         if (element != EL_BUG &&
4781             element != EL_SPACESHIP &&
4782             element != EL_BD_BUTTERFLY &&
4783             element != EL_BD_FIREFLY)
4784           break;
4785
4786         for (i = 0; i < NUM_DIRECTIONS; i++)
4787         {
4788           int x1 = x + xy[i][0];
4789           int y1 = y + xy[i][1];
4790
4791           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4792           {
4793             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4794             {
4795               MovDir[x][y] = direction[0][i];
4796               break;
4797             }
4798             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4799                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4800             {
4801               MovDir[x][y] = direction[1][i];
4802               break;
4803             }
4804           }
4805         }
4806       }
4807       break;
4808   }
4809
4810   GfxDir[x][y] = MovDir[x][y];
4811 }
4812
4813 void InitAmoebaNr(int x, int y)
4814 {
4815   int i;
4816   int group_nr = AmoebaNeighbourNr(x, y);
4817
4818   if (group_nr == 0)
4819   {
4820     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4821     {
4822       if (AmoebaCnt[i] == 0)
4823       {
4824         group_nr = i;
4825         break;
4826       }
4827     }
4828   }
4829
4830   AmoebaNr[x][y] = group_nr;
4831   AmoebaCnt[group_nr]++;
4832   AmoebaCnt2[group_nr]++;
4833 }
4834
4835 static void LevelSolved_SetFinalGameValues(void)
4836 {
4837   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4838                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4839   game.score_time_final = (level.use_step_counter ? TimePlayed :
4840                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4841
4842   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4843                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4844                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4845                       game.score);
4846
4847   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4848                        MM_HEALTH(game_mm.laser_overload_value) :
4849                        game.health);
4850
4851   game.LevelSolved_CountingTime = game.time_final;
4852   game.LevelSolved_CountingScore = game.score_final;
4853   game.LevelSolved_CountingHealth = game.health_final;
4854 }
4855
4856 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4857 {
4858   game.LevelSolved_CountingTime = time;
4859   game.LevelSolved_CountingScore = score;
4860   game.LevelSolved_CountingHealth = health;
4861
4862   game_panel_controls[GAME_PANEL_TIME].value = time;
4863   game_panel_controls[GAME_PANEL_SCORE].value = score;
4864   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4865
4866   DisplayGameControlValues();
4867 }
4868
4869 static void LevelSolved(void)
4870 {
4871   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4872       game.players_still_needed > 0)
4873     return;
4874
4875   game.LevelSolved = TRUE;
4876   game.GameOver = TRUE;
4877
4878   tape.solved = TRUE;
4879
4880   // needed here to display correct panel values while player walks into exit
4881   LevelSolved_SetFinalGameValues();
4882 }
4883
4884 void GameWon(void)
4885 {
4886   static int time_count_steps;
4887   static int time, time_final;
4888   static float score, score_final; // needed for time score < 10 for 10 seconds
4889   static int health, health_final;
4890   static int game_over_delay_1 = 0;
4891   static int game_over_delay_2 = 0;
4892   static int game_over_delay_3 = 0;
4893   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4894   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4895
4896   if (!game.LevelSolved_GameWon)
4897   {
4898     int i;
4899
4900     // do not start end game actions before the player stops moving (to exit)
4901     if (local_player->active && local_player->MovPos)
4902       return;
4903
4904     // calculate final game values after player finished walking into exit
4905     LevelSolved_SetFinalGameValues();
4906
4907     game.LevelSolved_GameWon = TRUE;
4908     game.LevelSolved_SaveTape = tape.recording;
4909     game.LevelSolved_SaveScore = !tape.playing;
4910
4911     if (!tape.playing)
4912     {
4913       LevelStats_incSolved(level_nr);
4914
4915       SaveLevelSetup_SeriesInfo();
4916     }
4917
4918     if (tape.auto_play)         // tape might already be stopped here
4919       tape.auto_play_level_solved = TRUE;
4920
4921     TapeStop();
4922
4923     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4924     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4925     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4926
4927     time = time_final = game.time_final;
4928     score = score_final = game.score_final;
4929     health = health_final = game.health_final;
4930
4931     // update game panel values before (delayed) counting of score (if any)
4932     LevelSolved_DisplayFinalGameValues(time, score, health);
4933
4934     // if level has time score defined, calculate new final game values
4935     if (time_score > 0)
4936     {
4937       int time_final_max = 999;
4938       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4939       int time_frames = 0;
4940       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4941       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4942
4943       if (TimeLeft > 0)
4944       {
4945         time_final = 0;
4946         time_frames = time_frames_left;
4947       }
4948       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4949       {
4950         time_final = time_final_max;
4951         time_frames = time_frames_final_max - time_frames_played;
4952       }
4953
4954       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4955
4956       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4957
4958       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4959       {
4960         // keep previous values (final values already processed here)
4961         time_final = time;
4962         score_final = score;
4963       }
4964       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4965       {
4966         health_final = 0;
4967         score_final += health * time_score;
4968       }
4969
4970       game.score_final = score_final;
4971       game.health_final = health_final;
4972     }
4973
4974     // if not counting score after game, immediately update game panel values
4975     if (level_editor_test_game || !setup.count_score_after_game ||
4976         level.game_engine_type == GAME_ENGINE_TYPE_BD)
4977     {
4978       time = time_final;
4979       score = score_final;
4980
4981       LevelSolved_DisplayFinalGameValues(time, score, health);
4982     }
4983
4984     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4985     {
4986       // check if last player has left the level
4987       if (game.exit_x >= 0 &&
4988           game.exit_y >= 0)
4989       {
4990         int x = game.exit_x;
4991         int y = game.exit_y;
4992         int element = Tile[x][y];
4993
4994         // close exit door after last player
4995         if ((game.all_players_gone &&
4996              (element == EL_EXIT_OPEN ||
4997               element == EL_SP_EXIT_OPEN ||
4998               element == EL_STEEL_EXIT_OPEN)) ||
4999             element == EL_EM_EXIT_OPEN ||
5000             element == EL_EM_STEEL_EXIT_OPEN)
5001         {
5002
5003           Tile[x][y] =
5004             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5005              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5006              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5007              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5008              EL_EM_STEEL_EXIT_CLOSING);
5009
5010           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5011         }
5012
5013         // player disappears
5014         DrawLevelField(x, y);
5015       }
5016
5017       for (i = 0; i < MAX_PLAYERS; i++)
5018       {
5019         struct PlayerInfo *player = &stored_player[i];
5020
5021         if (player->present)
5022         {
5023           RemovePlayer(player);
5024
5025           // player disappears
5026           DrawLevelField(player->jx, player->jy);
5027         }
5028       }
5029     }
5030
5031     PlaySound(SND_GAME_WINNING);
5032   }
5033
5034   if (setup.count_score_after_game)
5035   {
5036     if (time != time_final)
5037     {
5038       if (game_over_delay_1 > 0)
5039       {
5040         game_over_delay_1--;
5041
5042         return;
5043       }
5044
5045       int time_to_go = ABS(time_final - time);
5046       int time_count_dir = (time < time_final ? +1 : -1);
5047
5048       if (time_to_go < time_count_steps)
5049         time_count_steps = 1;
5050
5051       time  += time_count_steps * time_count_dir;
5052       score += time_count_steps * time_score;
5053
5054       // set final score to correct rounding differences after counting score
5055       if (time == time_final)
5056         score = score_final;
5057
5058       LevelSolved_DisplayFinalGameValues(time, score, health);
5059
5060       if (time == time_final)
5061         StopSound(SND_GAME_LEVELTIME_BONUS);
5062       else if (setup.sound_loops)
5063         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5064       else
5065         PlaySound(SND_GAME_LEVELTIME_BONUS);
5066
5067       return;
5068     }
5069
5070     if (health != health_final)
5071     {
5072       if (game_over_delay_2 > 0)
5073       {
5074         game_over_delay_2--;
5075
5076         return;
5077       }
5078
5079       int health_count_dir = (health < health_final ? +1 : -1);
5080
5081       health += health_count_dir;
5082       score  += time_score;
5083
5084       LevelSolved_DisplayFinalGameValues(time, score, health);
5085
5086       if (health == health_final)
5087         StopSound(SND_GAME_LEVELTIME_BONUS);
5088       else if (setup.sound_loops)
5089         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5090       else
5091         PlaySound(SND_GAME_LEVELTIME_BONUS);
5092
5093       return;
5094     }
5095   }
5096
5097   game.panel.active = FALSE;
5098
5099   if (game_over_delay_3 > 0)
5100   {
5101     game_over_delay_3--;
5102
5103     return;
5104   }
5105
5106   GameEnd();
5107 }
5108
5109 void GameEnd(void)
5110 {
5111   // used instead of "level_nr" (needed for network games)
5112   int last_level_nr = levelset.level_nr;
5113   boolean tape_saved = FALSE;
5114
5115   game.LevelSolved_GameEnd = TRUE;
5116
5117   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5118   {
5119     // make sure that request dialog to save tape does not open door again
5120     if (!global.use_envelope_request)
5121       CloseDoor(DOOR_CLOSE_1);
5122
5123     // ask to save tape
5124     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5125
5126     // set unique basename for score tape (also saved in high score table)
5127     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5128   }
5129
5130   // if no tape is to be saved, close both doors simultaneously
5131   CloseDoor(DOOR_CLOSE_ALL);
5132
5133   if (level_editor_test_game || score_info_tape_play)
5134   {
5135     SetGameStatus(GAME_MODE_MAIN);
5136
5137     DrawMainMenu();
5138
5139     return;
5140   }
5141
5142   if (!game.LevelSolved_SaveScore)
5143   {
5144     SetGameStatus(GAME_MODE_MAIN);
5145
5146     DrawMainMenu();
5147
5148     return;
5149   }
5150
5151   if (level_nr == leveldir_current->handicap_level)
5152   {
5153     leveldir_current->handicap_level++;
5154
5155     SaveLevelSetup_SeriesInfo();
5156   }
5157
5158   // save score and score tape before potentially erasing tape below
5159   NewHighScore(last_level_nr, tape_saved);
5160
5161   if (setup.increment_levels &&
5162       level_nr < leveldir_current->last_level &&
5163       !network_playing)
5164   {
5165     level_nr++;         // advance to next level
5166     TapeErase();        // start with empty tape
5167
5168     if (setup.auto_play_next_level)
5169     {
5170       scores.continue_playing = TRUE;
5171       scores.next_level_nr = level_nr;
5172
5173       LoadLevel(level_nr);
5174
5175       SaveLevelSetup_SeriesInfo();
5176     }
5177   }
5178
5179   if (scores.last_added >= 0 && setup.show_scores_after_game)
5180   {
5181     SetGameStatus(GAME_MODE_SCORES);
5182
5183     DrawHallOfFame(last_level_nr);
5184   }
5185   else if (scores.continue_playing)
5186   {
5187     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5188   }
5189   else
5190   {
5191     SetGameStatus(GAME_MODE_MAIN);
5192
5193     DrawMainMenu();
5194   }
5195 }
5196
5197 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5198                          boolean one_score_entry_per_name)
5199 {
5200   int i;
5201
5202   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5203     return -1;
5204
5205   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5206   {
5207     struct ScoreEntry *entry = &list->entry[i];
5208     boolean score_is_better = (new_entry->score >  entry->score);
5209     boolean score_is_equal  = (new_entry->score == entry->score);
5210     boolean time_is_better  = (new_entry->time  <  entry->time);
5211     boolean time_is_equal   = (new_entry->time  == entry->time);
5212     boolean better_by_score = (score_is_better ||
5213                                (score_is_equal && time_is_better));
5214     boolean better_by_time  = (time_is_better ||
5215                                (time_is_equal && score_is_better));
5216     boolean is_better = (level.rate_time_over_score ? better_by_time :
5217                          better_by_score);
5218     boolean entry_is_empty = (entry->score == 0 &&
5219                               entry->time == 0);
5220
5221     // prevent adding server score entries if also existing in local score file
5222     // (special case: historic score entries have an empty tape basename entry)
5223     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5224         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5225     {
5226       // add fields from server score entry not stored in local score entry
5227       // (currently, this means setting platform, version and country fields;
5228       // in rare cases, this may also correct an invalid score value, as
5229       // historic scores might have been truncated to 16-bit values locally)
5230       *entry = *new_entry;
5231
5232       return -1;
5233     }
5234
5235     if (is_better || entry_is_empty)
5236     {
5237       // player has made it to the hall of fame
5238
5239       if (i < MAX_SCORE_ENTRIES - 1)
5240       {
5241         int m = MAX_SCORE_ENTRIES - 1;
5242         int l;
5243
5244         if (one_score_entry_per_name)
5245         {
5246           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5247             if (strEqual(list->entry[l].name, new_entry->name))
5248               m = l;
5249
5250           if (m == i)   // player's new highscore overwrites his old one
5251             goto put_into_list;
5252         }
5253
5254         for (l = m; l > i; l--)
5255           list->entry[l] = list->entry[l - 1];
5256       }
5257
5258       put_into_list:
5259
5260       *entry = *new_entry;
5261
5262       return i;
5263     }
5264     else if (one_score_entry_per_name &&
5265              strEqual(entry->name, new_entry->name))
5266     {
5267       // player already in high score list with better score or time
5268
5269       return -1;
5270     }
5271   }
5272
5273   // special case: new score is beyond the last high score list position
5274   return MAX_SCORE_ENTRIES;
5275 }
5276
5277 void NewHighScore(int level_nr, boolean tape_saved)
5278 {
5279   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5280   boolean one_per_name = FALSE;
5281
5282   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5283   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5284
5285   new_entry.score = game.score_final;
5286   new_entry.time = game.score_time_final;
5287
5288   LoadScore(level_nr);
5289
5290   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5291
5292   if (scores.last_added >= MAX_SCORE_ENTRIES)
5293   {
5294     scores.last_added = MAX_SCORE_ENTRIES - 1;
5295     scores.force_last_added = TRUE;
5296
5297     scores.entry[scores.last_added] = new_entry;
5298
5299     // store last added local score entry (before merging server scores)
5300     scores.last_added_local = scores.last_added;
5301
5302     return;
5303   }
5304
5305   if (scores.last_added < 0)
5306     return;
5307
5308   SaveScore(level_nr);
5309
5310   // store last added local score entry (before merging server scores)
5311   scores.last_added_local = scores.last_added;
5312
5313   if (!game.LevelSolved_SaveTape)
5314     return;
5315
5316   SaveScoreTape(level_nr);
5317
5318   if (setup.ask_for_using_api_server)
5319   {
5320     setup.use_api_server =
5321       Request("Upload your score and tape to the high score server?", REQ_ASK);
5322
5323     if (!setup.use_api_server)
5324       Request("Not using high score server! Use setup menu to enable again!",
5325               REQ_CONFIRM);
5326
5327     runtime.use_api_server = setup.use_api_server;
5328
5329     // after asking for using API server once, do not ask again
5330     setup.ask_for_using_api_server = FALSE;
5331
5332     SaveSetup_ServerSetup();
5333   }
5334
5335   SaveServerScore(level_nr, tape_saved);
5336 }
5337
5338 void MergeServerScore(void)
5339 {
5340   struct ScoreEntry last_added_entry;
5341   boolean one_per_name = FALSE;
5342   int i;
5343
5344   if (scores.last_added >= 0)
5345     last_added_entry = scores.entry[scores.last_added];
5346
5347   for (i = 0; i < server_scores.num_entries; i++)
5348   {
5349     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5350
5351     if (pos >= 0 && pos <= scores.last_added)
5352       scores.last_added++;
5353   }
5354
5355   if (scores.last_added >= MAX_SCORE_ENTRIES)
5356   {
5357     scores.last_added = MAX_SCORE_ENTRIES - 1;
5358     scores.force_last_added = TRUE;
5359
5360     scores.entry[scores.last_added] = last_added_entry;
5361   }
5362 }
5363
5364 static int getElementMoveStepsizeExt(int x, int y, int direction)
5365 {
5366   int element = Tile[x][y];
5367   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5368   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5369   int horiz_move = (dx != 0);
5370   int sign = (horiz_move ? dx : dy);
5371   int step = sign * element_info[element].move_stepsize;
5372
5373   // special values for move stepsize for spring and things on conveyor belt
5374   if (horiz_move)
5375   {
5376     if (CAN_FALL(element) &&
5377         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5378       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5379     else if (element == EL_SPRING)
5380       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5381   }
5382
5383   return step;
5384 }
5385
5386 static int getElementMoveStepsize(int x, int y)
5387 {
5388   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5389 }
5390
5391 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5392 {
5393   if (player->GfxAction != action || player->GfxDir != dir)
5394   {
5395     player->GfxAction = action;
5396     player->GfxDir = dir;
5397     player->Frame = 0;
5398     player->StepFrame = 0;
5399   }
5400 }
5401
5402 static void ResetGfxFrame(int x, int y)
5403 {
5404   // profiling showed that "autotest" spends 10~20% of its time in this function
5405   if (DrawingDeactivatedField())
5406     return;
5407
5408   int element = Tile[x][y];
5409   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5410
5411   if (graphic_info[graphic].anim_global_sync)
5412     GfxFrame[x][y] = FrameCounter;
5413   else if (graphic_info[graphic].anim_global_anim_sync)
5414     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5415   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5416     GfxFrame[x][y] = CustomValue[x][y];
5417   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5418     GfxFrame[x][y] = element_info[element].collect_score;
5419   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5420     GfxFrame[x][y] = ChangeDelay[x][y];
5421 }
5422
5423 static void ResetGfxAnimation(int x, int y)
5424 {
5425   GfxAction[x][y] = ACTION_DEFAULT;
5426   GfxDir[x][y] = MovDir[x][y];
5427   GfxFrame[x][y] = 0;
5428
5429   ResetGfxFrame(x, y);
5430 }
5431
5432 static void ResetRandomAnimationValue(int x, int y)
5433 {
5434   GfxRandom[x][y] = INIT_GFX_RANDOM();
5435 }
5436
5437 static void InitMovingField(int x, int y, int direction)
5438 {
5439   int element = Tile[x][y];
5440   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5441   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5442   int newx = x + dx;
5443   int newy = y + dy;
5444   boolean is_moving_before, is_moving_after;
5445
5446   // check if element was/is moving or being moved before/after mode change
5447   is_moving_before = (WasJustMoving[x][y] != 0);
5448   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5449
5450   // reset animation only for moving elements which change direction of moving
5451   // or which just started or stopped moving
5452   // (else CEs with property "can move" / "not moving" are reset each frame)
5453   if (is_moving_before != is_moving_after ||
5454       direction != MovDir[x][y])
5455     ResetGfxAnimation(x, y);
5456
5457   MovDir[x][y] = direction;
5458   GfxDir[x][y] = direction;
5459
5460   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5461                      direction == MV_DOWN && CAN_FALL(element) ?
5462                      ACTION_FALLING : ACTION_MOVING);
5463
5464   // this is needed for CEs with property "can move" / "not moving"
5465
5466   if (is_moving_after)
5467   {
5468     if (Tile[newx][newy] == EL_EMPTY)
5469       Tile[newx][newy] = EL_BLOCKED;
5470
5471     MovDir[newx][newy] = MovDir[x][y];
5472
5473     CustomValue[newx][newy] = CustomValue[x][y];
5474
5475     GfxFrame[newx][newy] = GfxFrame[x][y];
5476     GfxRandom[newx][newy] = GfxRandom[x][y];
5477     GfxAction[newx][newy] = GfxAction[x][y];
5478     GfxDir[newx][newy] = GfxDir[x][y];
5479   }
5480 }
5481
5482 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5483 {
5484   int direction = MovDir[x][y];
5485   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5486   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5487
5488   *goes_to_x = newx;
5489   *goes_to_y = newy;
5490 }
5491
5492 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5493 {
5494   int direction = MovDir[x][y];
5495   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5496   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5497
5498   *comes_from_x = oldx;
5499   *comes_from_y = oldy;
5500 }
5501
5502 static int MovingOrBlocked2Element(int x, int y)
5503 {
5504   int element = Tile[x][y];
5505
5506   if (element == EL_BLOCKED)
5507   {
5508     int oldx, oldy;
5509
5510     Blocked2Moving(x, y, &oldx, &oldy);
5511
5512     return Tile[oldx][oldy];
5513   }
5514
5515   return element;
5516 }
5517
5518 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5519 {
5520   // like MovingOrBlocked2Element(), but if element is moving
5521   // and (x, y) is the field the moving element is just leaving,
5522   // return EL_BLOCKED instead of the element value
5523   int element = Tile[x][y];
5524
5525   if (IS_MOVING(x, y))
5526   {
5527     if (element == EL_BLOCKED)
5528     {
5529       int oldx, oldy;
5530
5531       Blocked2Moving(x, y, &oldx, &oldy);
5532       return Tile[oldx][oldy];
5533     }
5534     else
5535       return EL_BLOCKED;
5536   }
5537   else
5538     return element;
5539 }
5540
5541 static void RemoveField(int x, int y)
5542 {
5543   Tile[x][y] = EL_EMPTY;
5544
5545   MovPos[x][y] = 0;
5546   MovDir[x][y] = 0;
5547   MovDelay[x][y] = 0;
5548
5549   CustomValue[x][y] = 0;
5550
5551   AmoebaNr[x][y] = 0;
5552   ChangeDelay[x][y] = 0;
5553   ChangePage[x][y] = -1;
5554   Pushed[x][y] = FALSE;
5555
5556   GfxElement[x][y] = EL_UNDEFINED;
5557   GfxAction[x][y] = ACTION_DEFAULT;
5558   GfxDir[x][y] = MV_NONE;
5559 }
5560
5561 static void RemoveMovingField(int x, int y)
5562 {
5563   int oldx = x, oldy = y, newx = x, newy = y;
5564   int element = Tile[x][y];
5565   int next_element = EL_UNDEFINED;
5566
5567   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5568     return;
5569
5570   if (IS_MOVING(x, y))
5571   {
5572     Moving2Blocked(x, y, &newx, &newy);
5573
5574     if (Tile[newx][newy] != EL_BLOCKED)
5575     {
5576       // element is moving, but target field is not free (blocked), but
5577       // already occupied by something different (example: acid pool);
5578       // in this case, only remove the moving field, but not the target
5579
5580       RemoveField(oldx, oldy);
5581
5582       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5583
5584       TEST_DrawLevelField(oldx, oldy);
5585
5586       return;
5587     }
5588   }
5589   else if (element == EL_BLOCKED)
5590   {
5591     Blocked2Moving(x, y, &oldx, &oldy);
5592     if (!IS_MOVING(oldx, oldy))
5593       return;
5594   }
5595
5596   if (element == EL_BLOCKED &&
5597       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5598        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5599        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5600        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5601        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5602        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5603     next_element = get_next_element(Tile[oldx][oldy]);
5604
5605   RemoveField(oldx, oldy);
5606   RemoveField(newx, newy);
5607
5608   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5609
5610   if (next_element != EL_UNDEFINED)
5611     Tile[oldx][oldy] = next_element;
5612
5613   TEST_DrawLevelField(oldx, oldy);
5614   TEST_DrawLevelField(newx, newy);
5615 }
5616
5617 void DrawDynamite(int x, int y)
5618 {
5619   int sx = SCREENX(x), sy = SCREENY(y);
5620   int graphic = el2img(Tile[x][y]);
5621   int frame;
5622
5623   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5624     return;
5625
5626   if (IS_WALKABLE_INSIDE(Back[x][y]))
5627     return;
5628
5629   if (Back[x][y])
5630     DrawLevelElement(x, y, Back[x][y]);
5631   else if (Store[x][y])
5632     DrawLevelElement(x, y, Store[x][y]);
5633   else if (game.use_masked_elements)
5634     DrawLevelElement(x, y, EL_EMPTY);
5635
5636   frame = getGraphicAnimationFrameXY(graphic, x, y);
5637
5638   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5639     DrawGraphicThruMask(sx, sy, graphic, frame);
5640   else
5641     DrawGraphic(sx, sy, graphic, frame);
5642 }
5643
5644 static void CheckDynamite(int x, int y)
5645 {
5646   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5647   {
5648     MovDelay[x][y]--;
5649
5650     if (MovDelay[x][y] != 0)
5651     {
5652       DrawDynamite(x, y);
5653       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5654
5655       return;
5656     }
5657   }
5658
5659   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5660
5661   Bang(x, y);
5662 }
5663
5664 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5665 {
5666   boolean num_checked_players = 0;
5667   int i;
5668
5669   for (i = 0; i < MAX_PLAYERS; i++)
5670   {
5671     if (stored_player[i].active)
5672     {
5673       int sx = stored_player[i].jx;
5674       int sy = stored_player[i].jy;
5675
5676       if (num_checked_players == 0)
5677       {
5678         *sx1 = *sx2 = sx;
5679         *sy1 = *sy2 = sy;
5680       }
5681       else
5682       {
5683         *sx1 = MIN(*sx1, sx);
5684         *sy1 = MIN(*sy1, sy);
5685         *sx2 = MAX(*sx2, sx);
5686         *sy2 = MAX(*sy2, sy);
5687       }
5688
5689       num_checked_players++;
5690     }
5691   }
5692 }
5693
5694 static boolean checkIfAllPlayersFitToScreen_RND(void)
5695 {
5696   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5697
5698   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5699
5700   return (sx2 - sx1 < SCR_FIELDX &&
5701           sy2 - sy1 < SCR_FIELDY);
5702 }
5703
5704 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5705 {
5706   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5707
5708   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5709
5710   *sx = (sx1 + sx2) / 2;
5711   *sy = (sy1 + sy2) / 2;
5712 }
5713
5714 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5715                                boolean center_screen, boolean quick_relocation)
5716 {
5717   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5718   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5719   boolean no_delay = (tape.warp_forward);
5720   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5721   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5722   int new_scroll_x, new_scroll_y;
5723
5724   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5725   {
5726     // case 1: quick relocation inside visible screen (without scrolling)
5727
5728     RedrawPlayfield();
5729
5730     return;
5731   }
5732
5733   if (!level.shifted_relocation || center_screen)
5734   {
5735     // relocation _with_ centering of screen
5736
5737     new_scroll_x = SCROLL_POSITION_X(x);
5738     new_scroll_y = SCROLL_POSITION_Y(y);
5739   }
5740   else
5741   {
5742     // relocation _without_ centering of screen
5743
5744     // apply distance between old and new player position to scroll position
5745     int shifted_scroll_x = scroll_x + (x - old_x);
5746     int shifted_scroll_y = scroll_y + (y - old_y);
5747
5748     // make sure that shifted scroll position does not scroll beyond screen
5749     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5750     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5751
5752     // special case for teleporting from one end of the playfield to the other
5753     // (this kludge prevents the destination area to be shifted by half a tile
5754     // against the source destination for even screen width or screen height;
5755     // probably most useful when used with high "game.forced_scroll_delay_value"
5756     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5757     if (quick_relocation)
5758     {
5759       if (EVEN(SCR_FIELDX))
5760       {
5761         // relocate (teleport) between left and right border (half or full)
5762         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5763           new_scroll_x = SBX_Right;
5764         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5765           new_scroll_x = SBX_Right - 1;
5766         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5767           new_scroll_x = SBX_Left;
5768         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5769           new_scroll_x = SBX_Left + 1;
5770       }
5771
5772       if (EVEN(SCR_FIELDY))
5773       {
5774         // relocate (teleport) between top and bottom border (half or full)
5775         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5776           new_scroll_y = SBY_Lower;
5777         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5778           new_scroll_y = SBY_Lower - 1;
5779         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5780           new_scroll_y = SBY_Upper;
5781         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5782           new_scroll_y = SBY_Upper + 1;
5783       }
5784     }
5785   }
5786
5787   if (quick_relocation)
5788   {
5789     // case 2: quick relocation (redraw without visible scrolling)
5790
5791     scroll_x = new_scroll_x;
5792     scroll_y = new_scroll_y;
5793
5794     RedrawPlayfield();
5795
5796     return;
5797   }
5798
5799   // case 3: visible relocation (with scrolling to new position)
5800
5801   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5802
5803   SetVideoFrameDelay(wait_delay_value);
5804
5805   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5806   {
5807     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5808     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5809
5810     if (dx == 0 && dy == 0)             // no scrolling needed at all
5811       break;
5812
5813     scroll_x -= dx;
5814     scroll_y -= dy;
5815
5816     // set values for horizontal/vertical screen scrolling (half tile size)
5817     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5818     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5819     int pos_x = dx * TILEX / 2;
5820     int pos_y = dy * TILEY / 2;
5821     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5822     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5823
5824     ScrollLevel(dx, dy);
5825     DrawAllPlayers();
5826
5827     // scroll in two steps of half tile size to make things smoother
5828     BlitScreenToBitmapExt_RND(window, fx, fy);
5829
5830     // scroll second step to align at full tile size
5831     BlitScreenToBitmap(window);
5832   }
5833
5834   DrawAllPlayers();
5835   BackToFront();
5836
5837   SetVideoFrameDelay(frame_delay_value_old);
5838 }
5839
5840 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5841 {
5842   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5843   int player_nr = GET_PLAYER_NR(el_player);
5844   struct PlayerInfo *player = &stored_player[player_nr];
5845   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5846   boolean no_delay = (tape.warp_forward);
5847   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5848   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5849   int old_jx = player->jx;
5850   int old_jy = player->jy;
5851   int old_element = Tile[old_jx][old_jy];
5852   int element = Tile[jx][jy];
5853   boolean player_relocated = (old_jx != jx || old_jy != jy);
5854
5855   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5856   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5857   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5858   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5859   int leave_side_horiz = move_dir_horiz;
5860   int leave_side_vert  = move_dir_vert;
5861   int enter_side = enter_side_horiz | enter_side_vert;
5862   int leave_side = leave_side_horiz | leave_side_vert;
5863
5864   if (player->buried)           // do not reanimate dead player
5865     return;
5866
5867   if (!player_relocated)        // no need to relocate the player
5868     return;
5869
5870   if (IS_PLAYER(jx, jy))        // player already placed at new position
5871   {
5872     RemoveField(jx, jy);        // temporarily remove newly placed player
5873     DrawLevelField(jx, jy);
5874   }
5875
5876   if (player->present)
5877   {
5878     while (player->MovPos)
5879     {
5880       ScrollPlayer(player, SCROLL_GO_ON);
5881       ScrollScreen(NULL, SCROLL_GO_ON);
5882
5883       AdvanceFrameAndPlayerCounters(player->index_nr);
5884
5885       DrawPlayer(player);
5886
5887       BackToFront_WithFrameDelay(wait_delay_value);
5888     }
5889
5890     DrawPlayer(player);         // needed here only to cleanup last field
5891     DrawLevelField(player->jx, player->jy);     // remove player graphic
5892
5893     player->is_moving = FALSE;
5894   }
5895
5896   if (IS_CUSTOM_ELEMENT(old_element))
5897     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5898                                CE_LEFT_BY_PLAYER,
5899                                player->index_bit, leave_side);
5900
5901   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5902                                       CE_PLAYER_LEAVES_X,
5903                                       player->index_bit, leave_side);
5904
5905   Tile[jx][jy] = el_player;
5906   InitPlayerField(jx, jy, el_player, TRUE);
5907
5908   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5909      possible that the relocation target field did not contain a player element,
5910      but a walkable element, to which the new player was relocated -- in this
5911      case, restore that (already initialized!) element on the player field */
5912   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5913   {
5914     Tile[jx][jy] = element;     // restore previously existing element
5915   }
5916
5917   // only visually relocate centered player
5918   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5919                      FALSE, level.instant_relocation);
5920
5921   TestIfPlayerTouchesBadThing(jx, jy);
5922   TestIfPlayerTouchesCustomElement(jx, jy);
5923
5924   if (IS_CUSTOM_ELEMENT(element))
5925     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5926                                player->index_bit, enter_side);
5927
5928   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5929                                       player->index_bit, enter_side);
5930
5931   if (player->is_switching)
5932   {
5933     /* ensure that relocation while still switching an element does not cause
5934        a new element to be treated as also switched directly after relocation
5935        (this is important for teleporter switches that teleport the player to
5936        a place where another teleporter switch is in the same direction, which
5937        would then incorrectly be treated as immediately switched before the
5938        direction key that caused the switch was released) */
5939
5940     player->switch_x += jx - old_jx;
5941     player->switch_y += jy - old_jy;
5942   }
5943 }
5944
5945 static void Explode(int ex, int ey, int phase, int mode)
5946 {
5947   int x, y;
5948   int last_phase;
5949   int border_element;
5950
5951   if (game.explosions_delayed)
5952   {
5953     ExplodeField[ex][ey] = mode;
5954     return;
5955   }
5956
5957   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5958   {
5959     int center_element = Tile[ex][ey];
5960     int ce_value = CustomValue[ex][ey];
5961     int ce_score = element_info[center_element].collect_score;
5962     int artwork_element, explosion_element;     // set these values later
5963
5964     // remove things displayed in background while burning dynamite
5965     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5966       Back[ex][ey] = 0;
5967
5968     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5969     {
5970       // put moving element to center field (and let it explode there)
5971       center_element = MovingOrBlocked2Element(ex, ey);
5972       RemoveMovingField(ex, ey);
5973       Tile[ex][ey] = center_element;
5974     }
5975
5976     // now "center_element" is finally determined -- set related values now
5977     artwork_element = center_element;           // for custom player artwork
5978     explosion_element = center_element;         // for custom player artwork
5979
5980     if (IS_PLAYER(ex, ey))
5981     {
5982       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5983
5984       artwork_element = stored_player[player_nr].artwork_element;
5985
5986       if (level.use_explosion_element[player_nr])
5987       {
5988         explosion_element = level.explosion_element[player_nr];
5989         artwork_element = explosion_element;
5990       }
5991     }
5992
5993     if (mode == EX_TYPE_NORMAL ||
5994         mode == EX_TYPE_CENTER ||
5995         mode == EX_TYPE_CROSS)
5996       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5997
5998     last_phase = element_info[explosion_element].explosion_delay + 1;
5999
6000     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6001     {
6002       int xx = x - ex + 1;
6003       int yy = y - ey + 1;
6004       int element;
6005
6006       if (!IN_LEV_FIELD(x, y) ||
6007           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6008           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6009         continue;
6010
6011       element = Tile[x][y];
6012
6013       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6014       {
6015         element = MovingOrBlocked2Element(x, y);
6016
6017         if (!IS_EXPLOSION_PROOF(element))
6018           RemoveMovingField(x, y);
6019       }
6020
6021       // indestructible elements can only explode in center (but not flames)
6022       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6023                                            mode == EX_TYPE_BORDER)) ||
6024           element == EL_FLAMES)
6025         continue;
6026
6027       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6028          behaviour, for example when touching a yamyam that explodes to rocks
6029          with active deadly shield, a rock is created under the player !!! */
6030       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6031 #if 0
6032       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6033           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6034            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6035 #else
6036       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6037 #endif
6038       {
6039         if (IS_ACTIVE_BOMB(element))
6040         {
6041           // re-activate things under the bomb like gate or penguin
6042           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6043           Back[x][y] = 0;
6044         }
6045
6046         continue;
6047       }
6048
6049       // save walkable background elements while explosion on same tile
6050       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6051           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6052         Back[x][y] = element;
6053
6054       // ignite explodable elements reached by other explosion
6055       if (element == EL_EXPLOSION)
6056         element = Store2[x][y];
6057
6058       if (AmoebaNr[x][y] &&
6059           (element == EL_AMOEBA_FULL ||
6060            element == EL_BD_AMOEBA ||
6061            element == EL_AMOEBA_GROWING))
6062       {
6063         AmoebaCnt[AmoebaNr[x][y]]--;
6064         AmoebaCnt2[AmoebaNr[x][y]]--;
6065       }
6066
6067       RemoveField(x, y);
6068
6069       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6070       {
6071         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6072
6073         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6074
6075         if (PLAYERINFO(ex, ey)->use_murphy)
6076           Store[x][y] = EL_EMPTY;
6077       }
6078
6079       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6080       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6081       else if (IS_PLAYER_ELEMENT(center_element))
6082         Store[x][y] = EL_EMPTY;
6083       else if (center_element == EL_YAMYAM)
6084         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6085       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6086         Store[x][y] = element_info[center_element].content.e[xx][yy];
6087 #if 1
6088       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6089       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6090       // otherwise) -- FIX THIS !!!
6091       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6092         Store[x][y] = element_info[element].content.e[1][1];
6093 #else
6094       else if (!CAN_EXPLODE(element))
6095         Store[x][y] = element_info[element].content.e[1][1];
6096 #endif
6097       else
6098         Store[x][y] = EL_EMPTY;
6099
6100       if (IS_CUSTOM_ELEMENT(center_element))
6101         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6102                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6103                        Store[x][y] >= EL_PREV_CE_8 &&
6104                        Store[x][y] <= EL_NEXT_CE_8 ?
6105                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6106                        Store[x][y]);
6107
6108       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6109           center_element == EL_AMOEBA_TO_DIAMOND)
6110         Store2[x][y] = element;
6111
6112       Tile[x][y] = EL_EXPLOSION;
6113       GfxElement[x][y] = artwork_element;
6114
6115       ExplodePhase[x][y] = 1;
6116       ExplodeDelay[x][y] = last_phase;
6117
6118       Stop[x][y] = TRUE;
6119     }
6120
6121     if (center_element == EL_YAMYAM)
6122       game.yamyam_content_nr =
6123         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6124
6125     return;
6126   }
6127
6128   if (Stop[ex][ey])
6129     return;
6130
6131   x = ex;
6132   y = ey;
6133
6134   if (phase == 1)
6135     GfxFrame[x][y] = 0;         // restart explosion animation
6136
6137   last_phase = ExplodeDelay[x][y];
6138
6139   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6140
6141   // this can happen if the player leaves an explosion just in time
6142   if (GfxElement[x][y] == EL_UNDEFINED)
6143     GfxElement[x][y] = EL_EMPTY;
6144
6145   border_element = Store2[x][y];
6146   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6147     border_element = StorePlayer[x][y];
6148
6149   if (phase == element_info[border_element].ignition_delay ||
6150       phase == last_phase)
6151   {
6152     boolean border_explosion = FALSE;
6153
6154     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6155         !PLAYER_EXPLOSION_PROTECTED(x, y))
6156     {
6157       KillPlayerUnlessExplosionProtected(x, y);
6158       border_explosion = TRUE;
6159     }
6160     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6161     {
6162       Tile[x][y] = Store2[x][y];
6163       Store2[x][y] = 0;
6164       Bang(x, y);
6165       border_explosion = TRUE;
6166     }
6167     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6168     {
6169       AmoebaToDiamond(x, y);
6170       Store2[x][y] = 0;
6171       border_explosion = TRUE;
6172     }
6173
6174     // if an element just explodes due to another explosion (chain-reaction),
6175     // do not immediately end the new explosion when it was the last frame of
6176     // the explosion (as it would be done in the following "if"-statement!)
6177     if (border_explosion && phase == last_phase)
6178       return;
6179   }
6180
6181   // this can happen if the player was just killed by an explosion
6182   if (GfxElement[x][y] == EL_UNDEFINED)
6183     GfxElement[x][y] = EL_EMPTY;
6184
6185   if (phase == last_phase)
6186   {
6187     int element;
6188
6189     element = Tile[x][y] = Store[x][y];
6190     Store[x][y] = Store2[x][y] = 0;
6191     GfxElement[x][y] = EL_UNDEFINED;
6192
6193     // player can escape from explosions and might therefore be still alive
6194     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6195         element <= EL_PLAYER_IS_EXPLODING_4)
6196     {
6197       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6198       int explosion_element = EL_PLAYER_1 + player_nr;
6199       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6200       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6201
6202       if (level.use_explosion_element[player_nr])
6203         explosion_element = level.explosion_element[player_nr];
6204
6205       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6206                     element_info[explosion_element].content.e[xx][yy]);
6207     }
6208
6209     // restore probably existing indestructible background element
6210     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6211       element = Tile[x][y] = Back[x][y];
6212     Back[x][y] = 0;
6213
6214     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6215     GfxDir[x][y] = MV_NONE;
6216     ChangeDelay[x][y] = 0;
6217     ChangePage[x][y] = -1;
6218
6219     CustomValue[x][y] = 0;
6220
6221     InitField_WithBug2(x, y, FALSE);
6222
6223     TEST_DrawLevelField(x, y);
6224
6225     TestIfElementTouchesCustomElement(x, y);
6226
6227     if (GFX_CRUMBLED(element))
6228       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6229
6230     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6231       StorePlayer[x][y] = 0;
6232
6233     if (IS_PLAYER_ELEMENT(element))
6234       RelocatePlayer(x, y, element);
6235   }
6236   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6237   {
6238     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6239     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6240
6241     if (phase == 1)
6242       TEST_DrawLevelFieldCrumbled(x, y);
6243
6244     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6245     {
6246       DrawLevelElement(x, y, Back[x][y]);
6247       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6248     }
6249     else if (IS_WALKABLE_UNDER(Back[x][y]))
6250     {
6251       DrawLevelGraphic(x, y, graphic, frame);
6252       DrawLevelElementThruMask(x, y, Back[x][y]);
6253     }
6254     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6255       DrawLevelGraphic(x, y, graphic, frame);
6256   }
6257 }
6258
6259 static void DynaExplode(int ex, int ey)
6260 {
6261   int i, j;
6262   int dynabomb_element = Tile[ex][ey];
6263   int dynabomb_size = 1;
6264   boolean dynabomb_xl = FALSE;
6265   struct PlayerInfo *player;
6266   struct XY *xy = xy_topdown;
6267
6268   if (IS_ACTIVE_BOMB(dynabomb_element))
6269   {
6270     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6271     dynabomb_size = player->dynabomb_size;
6272     dynabomb_xl = player->dynabomb_xl;
6273     player->dynabombs_left++;
6274   }
6275
6276   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6277
6278   for (i = 0; i < NUM_DIRECTIONS; i++)
6279   {
6280     for (j = 1; j <= dynabomb_size; j++)
6281     {
6282       int x = ex + j * xy[i].x;
6283       int y = ey + j * xy[i].y;
6284       int element;
6285
6286       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6287         break;
6288
6289       element = Tile[x][y];
6290
6291       // do not restart explosions of fields with active bombs
6292       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6293         continue;
6294
6295       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6296
6297       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6298           !IS_DIGGABLE(element) && !dynabomb_xl)
6299         break;
6300     }
6301   }
6302 }
6303
6304 void Bang(int x, int y)
6305 {
6306   int element = MovingOrBlocked2Element(x, y);
6307   int explosion_type = EX_TYPE_NORMAL;
6308
6309   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6310   {
6311     struct PlayerInfo *player = PLAYERINFO(x, y);
6312
6313     element = Tile[x][y] = player->initial_element;
6314
6315     if (level.use_explosion_element[player->index_nr])
6316     {
6317       int explosion_element = level.explosion_element[player->index_nr];
6318
6319       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6320         explosion_type = EX_TYPE_CROSS;
6321       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6322         explosion_type = EX_TYPE_CENTER;
6323     }
6324   }
6325
6326   switch (element)
6327   {
6328     case EL_BUG:
6329     case EL_SPACESHIP:
6330     case EL_BD_BUTTERFLY:
6331     case EL_BD_FIREFLY:
6332     case EL_YAMYAM:
6333     case EL_DARK_YAMYAM:
6334     case EL_ROBOT:
6335     case EL_PACMAN:
6336     case EL_MOLE:
6337       RaiseScoreElement(element);
6338       break;
6339
6340     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6341     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6342     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6343     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6344     case EL_DYNABOMB_INCREASE_NUMBER:
6345     case EL_DYNABOMB_INCREASE_SIZE:
6346     case EL_DYNABOMB_INCREASE_POWER:
6347       explosion_type = EX_TYPE_DYNA;
6348       break;
6349
6350     case EL_DC_LANDMINE:
6351       explosion_type = EX_TYPE_CENTER;
6352       break;
6353
6354     case EL_PENGUIN:
6355     case EL_LAMP:
6356     case EL_LAMP_ACTIVE:
6357     case EL_AMOEBA_TO_DIAMOND:
6358       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6359         explosion_type = EX_TYPE_CENTER;
6360       break;
6361
6362     default:
6363       if (element_info[element].explosion_type == EXPLODES_CROSS)
6364         explosion_type = EX_TYPE_CROSS;
6365       else if (element_info[element].explosion_type == EXPLODES_1X1)
6366         explosion_type = EX_TYPE_CENTER;
6367       break;
6368   }
6369
6370   if (explosion_type == EX_TYPE_DYNA)
6371     DynaExplode(x, y);
6372   else
6373     Explode(x, y, EX_PHASE_START, explosion_type);
6374
6375   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6376 }
6377
6378 static void SplashAcid(int x, int y)
6379 {
6380   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6381       (!IN_LEV_FIELD(x - 1, y - 2) ||
6382        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6383     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6384
6385   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6386       (!IN_LEV_FIELD(x + 1, y - 2) ||
6387        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6388     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6389
6390   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6391 }
6392
6393 static void InitBeltMovement(void)
6394 {
6395   static int belt_base_element[4] =
6396   {
6397     EL_CONVEYOR_BELT_1_LEFT,
6398     EL_CONVEYOR_BELT_2_LEFT,
6399     EL_CONVEYOR_BELT_3_LEFT,
6400     EL_CONVEYOR_BELT_4_LEFT
6401   };
6402   static int belt_base_active_element[4] =
6403   {
6404     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6405     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6406     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6407     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6408   };
6409
6410   int x, y, i, j;
6411
6412   // set frame order for belt animation graphic according to belt direction
6413   for (i = 0; i < NUM_BELTS; i++)
6414   {
6415     int belt_nr = i;
6416
6417     for (j = 0; j < NUM_BELT_PARTS; j++)
6418     {
6419       int element = belt_base_active_element[belt_nr] + j;
6420       int graphic_1 = el2img(element);
6421       int graphic_2 = el2panelimg(element);
6422
6423       if (game.belt_dir[i] == MV_LEFT)
6424       {
6425         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6426         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6427       }
6428       else
6429       {
6430         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6431         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6432       }
6433     }
6434   }
6435
6436   SCAN_PLAYFIELD(x, y)
6437   {
6438     int element = Tile[x][y];
6439
6440     for (i = 0; i < NUM_BELTS; i++)
6441     {
6442       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6443       {
6444         int e_belt_nr = getBeltNrFromBeltElement(element);
6445         int belt_nr = i;
6446
6447         if (e_belt_nr == belt_nr)
6448         {
6449           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6450
6451           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6452         }
6453       }
6454     }
6455   }
6456 }
6457
6458 static void ToggleBeltSwitch(int x, int y)
6459 {
6460   static int belt_base_element[4] =
6461   {
6462     EL_CONVEYOR_BELT_1_LEFT,
6463     EL_CONVEYOR_BELT_2_LEFT,
6464     EL_CONVEYOR_BELT_3_LEFT,
6465     EL_CONVEYOR_BELT_4_LEFT
6466   };
6467   static int belt_base_active_element[4] =
6468   {
6469     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6470     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6471     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6472     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6473   };
6474   static int belt_base_switch_element[4] =
6475   {
6476     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6477     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6478     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6479     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6480   };
6481   static int belt_move_dir[4] =
6482   {
6483     MV_LEFT,
6484     MV_NONE,
6485     MV_RIGHT,
6486     MV_NONE,
6487   };
6488
6489   int element = Tile[x][y];
6490   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6491   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6492   int belt_dir = belt_move_dir[belt_dir_nr];
6493   int xx, yy, i;
6494
6495   if (!IS_BELT_SWITCH(element))
6496     return;
6497
6498   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6499   game.belt_dir[belt_nr] = belt_dir;
6500
6501   if (belt_dir_nr == 3)
6502     belt_dir_nr = 1;
6503
6504   // set frame order for belt animation graphic according to belt direction
6505   for (i = 0; i < NUM_BELT_PARTS; i++)
6506   {
6507     int element = belt_base_active_element[belt_nr] + i;
6508     int graphic_1 = el2img(element);
6509     int graphic_2 = el2panelimg(element);
6510
6511     if (belt_dir == MV_LEFT)
6512     {
6513       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6514       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6515     }
6516     else
6517     {
6518       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6519       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6520     }
6521   }
6522
6523   SCAN_PLAYFIELD(xx, yy)
6524   {
6525     int element = Tile[xx][yy];
6526
6527     if (IS_BELT_SWITCH(element))
6528     {
6529       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6530
6531       if (e_belt_nr == belt_nr)
6532       {
6533         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6534         TEST_DrawLevelField(xx, yy);
6535       }
6536     }
6537     else if (IS_BELT(element) && belt_dir != MV_NONE)
6538     {
6539       int e_belt_nr = getBeltNrFromBeltElement(element);
6540
6541       if (e_belt_nr == belt_nr)
6542       {
6543         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6544
6545         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6546         TEST_DrawLevelField(xx, yy);
6547       }
6548     }
6549     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6550     {
6551       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6552
6553       if (e_belt_nr == belt_nr)
6554       {
6555         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6556
6557         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6558         TEST_DrawLevelField(xx, yy);
6559       }
6560     }
6561   }
6562 }
6563
6564 static void ToggleSwitchgateSwitch(void)
6565 {
6566   int xx, yy;
6567
6568   game.switchgate_pos = !game.switchgate_pos;
6569
6570   SCAN_PLAYFIELD(xx, yy)
6571   {
6572     int element = Tile[xx][yy];
6573
6574     if (element == EL_SWITCHGATE_SWITCH_UP)
6575     {
6576       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6577       TEST_DrawLevelField(xx, yy);
6578     }
6579     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6580     {
6581       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6582       TEST_DrawLevelField(xx, yy);
6583     }
6584     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6585     {
6586       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6587       TEST_DrawLevelField(xx, yy);
6588     }
6589     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6590     {
6591       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6592       TEST_DrawLevelField(xx, yy);
6593     }
6594     else if (element == EL_SWITCHGATE_OPEN ||
6595              element == EL_SWITCHGATE_OPENING)
6596     {
6597       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6598
6599       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6600     }
6601     else if (element == EL_SWITCHGATE_CLOSED ||
6602              element == EL_SWITCHGATE_CLOSING)
6603     {
6604       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6605
6606       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6607     }
6608   }
6609 }
6610
6611 static int getInvisibleActiveFromInvisibleElement(int element)
6612 {
6613   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6614           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6615           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6616           element);
6617 }
6618
6619 static int getInvisibleFromInvisibleActiveElement(int element)
6620 {
6621   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6622           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6623           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6624           element);
6625 }
6626
6627 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6628 {
6629   int x, y;
6630
6631   SCAN_PLAYFIELD(x, y)
6632   {
6633     int element = Tile[x][y];
6634
6635     if (element == EL_LIGHT_SWITCH &&
6636         game.light_time_left > 0)
6637     {
6638       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6639       TEST_DrawLevelField(x, y);
6640     }
6641     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6642              game.light_time_left == 0)
6643     {
6644       Tile[x][y] = EL_LIGHT_SWITCH;
6645       TEST_DrawLevelField(x, y);
6646     }
6647     else if (element == EL_EMC_DRIPPER &&
6648              game.light_time_left > 0)
6649     {
6650       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6651       TEST_DrawLevelField(x, y);
6652     }
6653     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6654              game.light_time_left == 0)
6655     {
6656       Tile[x][y] = EL_EMC_DRIPPER;
6657       TEST_DrawLevelField(x, y);
6658     }
6659     else if (element == EL_INVISIBLE_STEELWALL ||
6660              element == EL_INVISIBLE_WALL ||
6661              element == EL_INVISIBLE_SAND)
6662     {
6663       if (game.light_time_left > 0)
6664         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6665
6666       TEST_DrawLevelField(x, y);
6667
6668       // uncrumble neighbour fields, if needed
6669       if (element == EL_INVISIBLE_SAND)
6670         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6671     }
6672     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6673              element == EL_INVISIBLE_WALL_ACTIVE ||
6674              element == EL_INVISIBLE_SAND_ACTIVE)
6675     {
6676       if (game.light_time_left == 0)
6677         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6678
6679       TEST_DrawLevelField(x, y);
6680
6681       // re-crumble neighbour fields, if needed
6682       if (element == EL_INVISIBLE_SAND)
6683         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6684     }
6685   }
6686 }
6687
6688 static void RedrawAllInvisibleElementsForLenses(void)
6689 {
6690   int x, y;
6691
6692   SCAN_PLAYFIELD(x, y)
6693   {
6694     int element = Tile[x][y];
6695
6696     if (element == EL_EMC_DRIPPER &&
6697         game.lenses_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.lenses_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.lenses_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.lenses_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 RedrawAllInvisibleElementsForMagnifier(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_FAKE_GRASS &&
6746         game.magnify_time_left > 0)
6747     {
6748       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6749       TEST_DrawLevelField(x, y);
6750     }
6751     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6752              game.magnify_time_left == 0)
6753     {
6754       Tile[x][y] = EL_EMC_FAKE_GRASS;
6755       TEST_DrawLevelField(x, y);
6756     }
6757     else if (IS_GATE_GRAY(element) &&
6758              game.magnify_time_left > 0)
6759     {
6760       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6761                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6762                     IS_EM_GATE_GRAY(element) ?
6763                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6764                     IS_EMC_GATE_GRAY(element) ?
6765                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6766                     IS_DC_GATE_GRAY(element) ?
6767                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6768                     element);
6769       TEST_DrawLevelField(x, y);
6770     }
6771     else if (IS_GATE_GRAY_ACTIVE(element) &&
6772              game.magnify_time_left == 0)
6773     {
6774       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6775                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6776                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6777                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6778                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6779                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6780                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6781                     EL_DC_GATE_WHITE_GRAY :
6782                     element);
6783       TEST_DrawLevelField(x, y);
6784     }
6785   }
6786 }
6787
6788 static void ToggleLightSwitch(int x, int y)
6789 {
6790   int element = Tile[x][y];
6791
6792   game.light_time_left =
6793     (element == EL_LIGHT_SWITCH ?
6794      level.time_light * FRAMES_PER_SECOND : 0);
6795
6796   RedrawAllLightSwitchesAndInvisibleElements();
6797 }
6798
6799 static void ActivateTimegateSwitch(int x, int y)
6800 {
6801   int xx, yy;
6802
6803   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6804
6805   SCAN_PLAYFIELD(xx, yy)
6806   {
6807     int element = Tile[xx][yy];
6808
6809     if (element == EL_TIMEGATE_CLOSED ||
6810         element == EL_TIMEGATE_CLOSING)
6811     {
6812       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6813       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6814     }
6815
6816     /*
6817     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6818     {
6819       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6820       TEST_DrawLevelField(xx, yy);
6821     }
6822     */
6823
6824   }
6825
6826   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6827                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6828 }
6829
6830 static void Impact(int x, int y)
6831 {
6832   boolean last_line = (y == lev_fieldy - 1);
6833   boolean object_hit = FALSE;
6834   boolean impact = (last_line || object_hit);
6835   int element = Tile[x][y];
6836   int smashed = EL_STEELWALL;
6837
6838   if (!last_line)       // check if element below was hit
6839   {
6840     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6841       return;
6842
6843     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6844                                          MovDir[x][y + 1] != MV_DOWN ||
6845                                          MovPos[x][y + 1] <= TILEY / 2));
6846
6847     // do not smash moving elements that left the smashed field in time
6848     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6849         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6850       object_hit = FALSE;
6851
6852 #if USE_QUICKSAND_IMPACT_BUGFIX
6853     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6854     {
6855       RemoveMovingField(x, y + 1);
6856       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6857       Tile[x][y + 2] = EL_ROCK;
6858       TEST_DrawLevelField(x, y + 2);
6859
6860       object_hit = TRUE;
6861     }
6862
6863     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6864     {
6865       RemoveMovingField(x, y + 1);
6866       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6867       Tile[x][y + 2] = EL_ROCK;
6868       TEST_DrawLevelField(x, y + 2);
6869
6870       object_hit = TRUE;
6871     }
6872 #endif
6873
6874     if (object_hit)
6875       smashed = MovingOrBlocked2Element(x, y + 1);
6876
6877     impact = (last_line || object_hit);
6878   }
6879
6880   if (!last_line && smashed == EL_ACID) // element falls into acid
6881   {
6882     SplashAcid(x, y + 1);
6883     return;
6884   }
6885
6886   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6887   // only reset graphic animation if graphic really changes after impact
6888   if (impact &&
6889       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6890   {
6891     ResetGfxAnimation(x, y);
6892     TEST_DrawLevelField(x, y);
6893   }
6894
6895   if (impact && CAN_EXPLODE_IMPACT(element))
6896   {
6897     Bang(x, y);
6898     return;
6899   }
6900   else if (impact && element == EL_PEARL &&
6901            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6902   {
6903     ResetGfxAnimation(x, y);
6904
6905     Tile[x][y] = EL_PEARL_BREAKING;
6906     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6907     return;
6908   }
6909   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6910   {
6911     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6912
6913     return;
6914   }
6915
6916   if (impact && element == EL_AMOEBA_DROP)
6917   {
6918     if (object_hit && IS_PLAYER(x, y + 1))
6919       KillPlayerUnlessEnemyProtected(x, y + 1);
6920     else if (object_hit && smashed == EL_PENGUIN)
6921       Bang(x, y + 1);
6922     else
6923     {
6924       Tile[x][y] = EL_AMOEBA_GROWING;
6925       Store[x][y] = EL_AMOEBA_WET;
6926
6927       ResetRandomAnimationValue(x, y);
6928     }
6929     return;
6930   }
6931
6932   if (object_hit)               // check which object was hit
6933   {
6934     if ((CAN_PASS_MAGIC_WALL(element) && 
6935          (smashed == EL_MAGIC_WALL ||
6936           smashed == EL_BD_MAGIC_WALL)) ||
6937         (CAN_PASS_DC_MAGIC_WALL(element) &&
6938          smashed == EL_DC_MAGIC_WALL))
6939     {
6940       int xx, yy;
6941       int activated_magic_wall =
6942         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6943          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6944          EL_DC_MAGIC_WALL_ACTIVE);
6945
6946       // activate magic wall / mill
6947       SCAN_PLAYFIELD(xx, yy)
6948       {
6949         if (Tile[xx][yy] == smashed)
6950           Tile[xx][yy] = activated_magic_wall;
6951       }
6952
6953       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6954       game.magic_wall_active = TRUE;
6955
6956       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6957                             SND_MAGIC_WALL_ACTIVATING :
6958                             smashed == EL_BD_MAGIC_WALL ?
6959                             SND_BD_MAGIC_WALL_ACTIVATING :
6960                             SND_DC_MAGIC_WALL_ACTIVATING));
6961     }
6962
6963     if (IS_PLAYER(x, y + 1))
6964     {
6965       if (CAN_SMASH_PLAYER(element))
6966       {
6967         KillPlayerUnlessEnemyProtected(x, y + 1);
6968         return;
6969       }
6970     }
6971     else if (smashed == EL_PENGUIN)
6972     {
6973       if (CAN_SMASH_PLAYER(element))
6974       {
6975         Bang(x, y + 1);
6976         return;
6977       }
6978     }
6979     else if (element == EL_BD_DIAMOND)
6980     {
6981       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6982       {
6983         Bang(x, y + 1);
6984         return;
6985       }
6986     }
6987     else if (((element == EL_SP_INFOTRON ||
6988                element == EL_SP_ZONK) &&
6989               (smashed == EL_SP_SNIKSNAK ||
6990                smashed == EL_SP_ELECTRON ||
6991                smashed == EL_SP_DISK_ORANGE)) ||
6992              (element == EL_SP_INFOTRON &&
6993               smashed == EL_SP_DISK_YELLOW))
6994     {
6995       Bang(x, y + 1);
6996       return;
6997     }
6998     else if (CAN_SMASH_EVERYTHING(element))
6999     {
7000       if (IS_CLASSIC_ENEMY(smashed) ||
7001           CAN_EXPLODE_SMASHED(smashed))
7002       {
7003         Bang(x, y + 1);
7004         return;
7005       }
7006       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7007       {
7008         if (smashed == EL_LAMP ||
7009             smashed == EL_LAMP_ACTIVE)
7010         {
7011           Bang(x, y + 1);
7012           return;
7013         }
7014         else if (smashed == EL_NUT)
7015         {
7016           Tile[x][y + 1] = EL_NUT_BREAKING;
7017           PlayLevelSound(x, y, SND_NUT_BREAKING);
7018           RaiseScoreElement(EL_NUT);
7019           return;
7020         }
7021         else if (smashed == EL_PEARL)
7022         {
7023           ResetGfxAnimation(x, y);
7024
7025           Tile[x][y + 1] = EL_PEARL_BREAKING;
7026           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7027           return;
7028         }
7029         else if (smashed == EL_DIAMOND)
7030         {
7031           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7032           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7033           return;
7034         }
7035         else if (IS_BELT_SWITCH(smashed))
7036         {
7037           ToggleBeltSwitch(x, y + 1);
7038         }
7039         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7040                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7041                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7042                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7043         {
7044           ToggleSwitchgateSwitch();
7045         }
7046         else if (smashed == EL_LIGHT_SWITCH ||
7047                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7048         {
7049           ToggleLightSwitch(x, y + 1);
7050         }
7051         else
7052         {
7053           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7054
7055           CheckElementChangeBySide(x, y + 1, smashed, element,
7056                                    CE_SWITCHED, CH_SIDE_TOP);
7057           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7058                                             CH_SIDE_TOP);
7059         }
7060       }
7061       else
7062       {
7063         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7064       }
7065     }
7066   }
7067
7068   // play sound of magic wall / mill
7069   if (!last_line &&
7070       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7071        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7072        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7073   {
7074     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7075       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7076     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7077       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7078     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7079       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7080
7081     return;
7082   }
7083
7084   // play sound of object that hits the ground
7085   if (last_line || object_hit)
7086     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7087 }
7088
7089 static void TurnRoundExt(int x, int y)
7090 {
7091   static struct
7092   {
7093     int dx, dy;
7094   } move_xy[] =
7095   {
7096     {  0,  0 },
7097     { -1,  0 },
7098     { +1,  0 },
7099     {  0,  0 },
7100     {  0, -1 },
7101     {  0,  0 }, { 0, 0 }, { 0, 0 },
7102     {  0, +1 }
7103   };
7104   static struct
7105   {
7106     int left, right, back;
7107   } turn[] =
7108   {
7109     { 0,        0,              0        },
7110     { MV_DOWN,  MV_UP,          MV_RIGHT },
7111     { MV_UP,    MV_DOWN,        MV_LEFT  },
7112     { 0,        0,              0        },
7113     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7114     { 0,        0,              0        },
7115     { 0,        0,              0        },
7116     { 0,        0,              0        },
7117     { MV_RIGHT, MV_LEFT,        MV_UP    }
7118   };
7119
7120   int element = Tile[x][y];
7121   int move_pattern = element_info[element].move_pattern;
7122
7123   int old_move_dir = MovDir[x][y];
7124   int left_dir  = turn[old_move_dir].left;
7125   int right_dir = turn[old_move_dir].right;
7126   int back_dir  = turn[old_move_dir].back;
7127
7128   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7129   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7130   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7131   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7132
7133   int left_x  = x + left_dx,  left_y  = y + left_dy;
7134   int right_x = x + right_dx, right_y = y + right_dy;
7135   int move_x  = x + move_dx,  move_y  = y + move_dy;
7136
7137   int xx, yy;
7138
7139   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7140   {
7141     TestIfBadThingTouchesOtherBadThing(x, y);
7142
7143     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7144       MovDir[x][y] = right_dir;
7145     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7146       MovDir[x][y] = left_dir;
7147
7148     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7149       MovDelay[x][y] = 9;
7150     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7151       MovDelay[x][y] = 1;
7152   }
7153   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7154   {
7155     TestIfBadThingTouchesOtherBadThing(x, y);
7156
7157     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7158       MovDir[x][y] = left_dir;
7159     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7160       MovDir[x][y] = right_dir;
7161
7162     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7163       MovDelay[x][y] = 9;
7164     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7165       MovDelay[x][y] = 1;
7166   }
7167   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7168   {
7169     TestIfBadThingTouchesOtherBadThing(x, y);
7170
7171     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7172       MovDir[x][y] = left_dir;
7173     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7174       MovDir[x][y] = right_dir;
7175
7176     if (MovDir[x][y] != old_move_dir)
7177       MovDelay[x][y] = 9;
7178   }
7179   else if (element == EL_YAMYAM)
7180   {
7181     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7182     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7183
7184     if (can_turn_left && can_turn_right)
7185       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7186     else if (can_turn_left)
7187       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7188     else if (can_turn_right)
7189       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7190     else
7191       MovDir[x][y] = back_dir;
7192
7193     MovDelay[x][y] = 16 + 16 * RND(3);
7194   }
7195   else if (element == EL_DARK_YAMYAM)
7196   {
7197     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7198                                                          left_x, left_y);
7199     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7200                                                          right_x, right_y);
7201
7202     if (can_turn_left && can_turn_right)
7203       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7204     else if (can_turn_left)
7205       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7206     else if (can_turn_right)
7207       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7208     else
7209       MovDir[x][y] = back_dir;
7210
7211     MovDelay[x][y] = 16 + 16 * RND(3);
7212   }
7213   else if (element == EL_PACMAN)
7214   {
7215     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7216     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7217
7218     if (can_turn_left && can_turn_right)
7219       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7220     else if (can_turn_left)
7221       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7222     else if (can_turn_right)
7223       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7224     else
7225       MovDir[x][y] = back_dir;
7226
7227     MovDelay[x][y] = 6 + RND(40);
7228   }
7229   else if (element == EL_PIG)
7230   {
7231     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7232     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7233     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7234     boolean should_turn_left, should_turn_right, should_move_on;
7235     int rnd_value = 24;
7236     int rnd = RND(rnd_value);
7237
7238     should_turn_left = (can_turn_left &&
7239                         (!can_move_on ||
7240                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7241                                                    y + back_dy + left_dy)));
7242     should_turn_right = (can_turn_right &&
7243                          (!can_move_on ||
7244                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7245                                                     y + back_dy + right_dy)));
7246     should_move_on = (can_move_on &&
7247                       (!can_turn_left ||
7248                        !can_turn_right ||
7249                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7250                                                  y + move_dy + left_dy) ||
7251                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7252                                                  y + move_dy + right_dy)));
7253
7254     if (should_turn_left || should_turn_right || should_move_on)
7255     {
7256       if (should_turn_left && should_turn_right && should_move_on)
7257         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7258                         rnd < 2 * rnd_value / 3 ? right_dir :
7259                         old_move_dir);
7260       else if (should_turn_left && should_turn_right)
7261         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7262       else if (should_turn_left && should_move_on)
7263         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7264       else if (should_turn_right && should_move_on)
7265         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7266       else if (should_turn_left)
7267         MovDir[x][y] = left_dir;
7268       else if (should_turn_right)
7269         MovDir[x][y] = right_dir;
7270       else if (should_move_on)
7271         MovDir[x][y] = old_move_dir;
7272     }
7273     else if (can_move_on && rnd > rnd_value / 8)
7274       MovDir[x][y] = old_move_dir;
7275     else if (can_turn_left && can_turn_right)
7276       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7277     else if (can_turn_left && rnd > rnd_value / 8)
7278       MovDir[x][y] = left_dir;
7279     else if (can_turn_right && rnd > rnd_value/8)
7280       MovDir[x][y] = right_dir;
7281     else
7282       MovDir[x][y] = back_dir;
7283
7284     xx = x + move_xy[MovDir[x][y]].dx;
7285     yy = y + move_xy[MovDir[x][y]].dy;
7286
7287     if (!IN_LEV_FIELD(xx, yy) ||
7288         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7289       MovDir[x][y] = old_move_dir;
7290
7291     MovDelay[x][y] = 0;
7292   }
7293   else if (element == EL_DRAGON)
7294   {
7295     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7296     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7297     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7298     int rnd_value = 24;
7299     int rnd = RND(rnd_value);
7300
7301     if (can_move_on && rnd > rnd_value / 8)
7302       MovDir[x][y] = old_move_dir;
7303     else if (can_turn_left && can_turn_right)
7304       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7305     else if (can_turn_left && rnd > rnd_value / 8)
7306       MovDir[x][y] = left_dir;
7307     else if (can_turn_right && rnd > rnd_value / 8)
7308       MovDir[x][y] = right_dir;
7309     else
7310       MovDir[x][y] = back_dir;
7311
7312     xx = x + move_xy[MovDir[x][y]].dx;
7313     yy = y + move_xy[MovDir[x][y]].dy;
7314
7315     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7316       MovDir[x][y] = old_move_dir;
7317
7318     MovDelay[x][y] = 0;
7319   }
7320   else if (element == EL_MOLE)
7321   {
7322     boolean can_move_on =
7323       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7324                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7325                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7326     if (!can_move_on)
7327     {
7328       boolean can_turn_left =
7329         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7330                               IS_AMOEBOID(Tile[left_x][left_y])));
7331
7332       boolean can_turn_right =
7333         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7334                               IS_AMOEBOID(Tile[right_x][right_y])));
7335
7336       if (can_turn_left && can_turn_right)
7337         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7338       else if (can_turn_left)
7339         MovDir[x][y] = left_dir;
7340       else
7341         MovDir[x][y] = right_dir;
7342     }
7343
7344     if (MovDir[x][y] != old_move_dir)
7345       MovDelay[x][y] = 9;
7346   }
7347   else if (element == EL_BALLOON)
7348   {
7349     MovDir[x][y] = game.wind_direction;
7350     MovDelay[x][y] = 0;
7351   }
7352   else if (element == EL_SPRING)
7353   {
7354     if (MovDir[x][y] & MV_HORIZONTAL)
7355     {
7356       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7357           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7358       {
7359         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7360         ResetGfxAnimation(move_x, move_y);
7361         TEST_DrawLevelField(move_x, move_y);
7362
7363         MovDir[x][y] = back_dir;
7364       }
7365       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7366                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7367         MovDir[x][y] = MV_NONE;
7368     }
7369
7370     MovDelay[x][y] = 0;
7371   }
7372   else if (element == EL_ROBOT ||
7373            element == EL_SATELLITE ||
7374            element == EL_PENGUIN ||
7375            element == EL_EMC_ANDROID)
7376   {
7377     int attr_x = -1, attr_y = -1;
7378
7379     if (game.all_players_gone)
7380     {
7381       attr_x = game.exit_x;
7382       attr_y = game.exit_y;
7383     }
7384     else
7385     {
7386       int i;
7387
7388       for (i = 0; i < MAX_PLAYERS; i++)
7389       {
7390         struct PlayerInfo *player = &stored_player[i];
7391         int jx = player->jx, jy = player->jy;
7392
7393         if (!player->active)
7394           continue;
7395
7396         if (attr_x == -1 ||
7397             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7398         {
7399           attr_x = jx;
7400           attr_y = jy;
7401         }
7402       }
7403     }
7404
7405     if (element == EL_ROBOT &&
7406         game.robot_wheel_x >= 0 &&
7407         game.robot_wheel_y >= 0 &&
7408         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7409          game.engine_version < VERSION_IDENT(3,1,0,0)))
7410     {
7411       attr_x = game.robot_wheel_x;
7412       attr_y = game.robot_wheel_y;
7413     }
7414
7415     if (element == EL_PENGUIN)
7416     {
7417       int i;
7418       struct XY *xy = xy_topdown;
7419
7420       for (i = 0; i < NUM_DIRECTIONS; i++)
7421       {
7422         int ex = x + xy[i].x;
7423         int ey = y + xy[i].y;
7424
7425         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7426                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7427                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7428                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7429         {
7430           attr_x = ex;
7431           attr_y = ey;
7432           break;
7433         }
7434       }
7435     }
7436
7437     MovDir[x][y] = MV_NONE;
7438     if (attr_x < x)
7439       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7440     else if (attr_x > x)
7441       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7442     if (attr_y < y)
7443       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7444     else if (attr_y > y)
7445       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7446
7447     if (element == EL_ROBOT)
7448     {
7449       int newx, newy;
7450
7451       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7452         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7453       Moving2Blocked(x, y, &newx, &newy);
7454
7455       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7456         MovDelay[x][y] = 8 + 8 * !RND(3);
7457       else
7458         MovDelay[x][y] = 16;
7459     }
7460     else if (element == EL_PENGUIN)
7461     {
7462       int newx, newy;
7463
7464       MovDelay[x][y] = 1;
7465
7466       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7467       {
7468         boolean first_horiz = RND(2);
7469         int new_move_dir = MovDir[x][y];
7470
7471         MovDir[x][y] =
7472           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7473         Moving2Blocked(x, y, &newx, &newy);
7474
7475         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7476           return;
7477
7478         MovDir[x][y] =
7479           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7480         Moving2Blocked(x, y, &newx, &newy);
7481
7482         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7483           return;
7484
7485         MovDir[x][y] = old_move_dir;
7486         return;
7487       }
7488     }
7489     else if (element == EL_SATELLITE)
7490     {
7491       int newx, newy;
7492
7493       MovDelay[x][y] = 1;
7494
7495       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7496       {
7497         boolean first_horiz = RND(2);
7498         int new_move_dir = MovDir[x][y];
7499
7500         MovDir[x][y] =
7501           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502         Moving2Blocked(x, y, &newx, &newy);
7503
7504         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7505           return;
7506
7507         MovDir[x][y] =
7508           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509         Moving2Blocked(x, y, &newx, &newy);
7510
7511         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7512           return;
7513
7514         MovDir[x][y] = old_move_dir;
7515         return;
7516       }
7517     }
7518     else if (element == EL_EMC_ANDROID)
7519     {
7520       static int check_pos[16] =
7521       {
7522         -1,             //  0 => (invalid)
7523         7,              //  1 => MV_LEFT
7524         3,              //  2 => MV_RIGHT
7525         -1,             //  3 => (invalid)
7526         1,              //  4 =>            MV_UP
7527         0,              //  5 => MV_LEFT  | MV_UP
7528         2,              //  6 => MV_RIGHT | MV_UP
7529         -1,             //  7 => (invalid)
7530         5,              //  8 =>            MV_DOWN
7531         6,              //  9 => MV_LEFT  | MV_DOWN
7532         4,              // 10 => MV_RIGHT | MV_DOWN
7533         -1,             // 11 => (invalid)
7534         -1,             // 12 => (invalid)
7535         -1,             // 13 => (invalid)
7536         -1,             // 14 => (invalid)
7537         -1,             // 15 => (invalid)
7538       };
7539       static struct
7540       {
7541         int dx, dy;
7542         int dir;
7543       } check_xy[8] =
7544       {
7545         { -1, -1,       MV_LEFT  | MV_UP   },
7546         {  0, -1,                  MV_UP   },
7547         { +1, -1,       MV_RIGHT | MV_UP   },
7548         { +1,  0,       MV_RIGHT           },
7549         { +1, +1,       MV_RIGHT | MV_DOWN },
7550         {  0, +1,                  MV_DOWN },
7551         { -1, +1,       MV_LEFT  | MV_DOWN },
7552         { -1,  0,       MV_LEFT            },
7553       };
7554       int start_pos, check_order;
7555       boolean can_clone = FALSE;
7556       int i;
7557
7558       // check if there is any free field around current position
7559       for (i = 0; i < 8; i++)
7560       {
7561         int newx = x + check_xy[i].dx;
7562         int newy = y + check_xy[i].dy;
7563
7564         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7565         {
7566           can_clone = TRUE;
7567
7568           break;
7569         }
7570       }
7571
7572       if (can_clone)            // randomly find an element to clone
7573       {
7574         can_clone = FALSE;
7575
7576         start_pos = check_pos[RND(8)];
7577         check_order = (RND(2) ? -1 : +1);
7578
7579         for (i = 0; i < 8; i++)
7580         {
7581           int pos_raw = start_pos + i * check_order;
7582           int pos = (pos_raw + 8) % 8;
7583           int newx = x + check_xy[pos].dx;
7584           int newy = y + check_xy[pos].dy;
7585
7586           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7587           {
7588             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7589             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7590
7591             Store[x][y] = Tile[newx][newy];
7592
7593             can_clone = TRUE;
7594
7595             break;
7596           }
7597         }
7598       }
7599
7600       if (can_clone)            // randomly find a direction to move
7601       {
7602         can_clone = FALSE;
7603
7604         start_pos = check_pos[RND(8)];
7605         check_order = (RND(2) ? -1 : +1);
7606
7607         for (i = 0; i < 8; i++)
7608         {
7609           int pos_raw = start_pos + i * check_order;
7610           int pos = (pos_raw + 8) % 8;
7611           int newx = x + check_xy[pos].dx;
7612           int newy = y + check_xy[pos].dy;
7613           int new_move_dir = check_xy[pos].dir;
7614
7615           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7616           {
7617             MovDir[x][y] = new_move_dir;
7618             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7619
7620             can_clone = TRUE;
7621
7622             break;
7623           }
7624         }
7625       }
7626
7627       if (can_clone)            // cloning and moving successful
7628         return;
7629
7630       // cannot clone -- try to move towards player
7631
7632       start_pos = check_pos[MovDir[x][y] & 0x0f];
7633       check_order = (RND(2) ? -1 : +1);
7634
7635       for (i = 0; i < 3; i++)
7636       {
7637         // first check start_pos, then previous/next or (next/previous) pos
7638         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7639         int pos = (pos_raw + 8) % 8;
7640         int newx = x + check_xy[pos].dx;
7641         int newy = y + check_xy[pos].dy;
7642         int new_move_dir = check_xy[pos].dir;
7643
7644         if (IS_PLAYER(newx, newy))
7645           break;
7646
7647         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7648         {
7649           MovDir[x][y] = new_move_dir;
7650           MovDelay[x][y] = level.android_move_time * 8 + 1;
7651
7652           break;
7653         }
7654       }
7655     }
7656   }
7657   else if (move_pattern == MV_TURNING_LEFT ||
7658            move_pattern == MV_TURNING_RIGHT ||
7659            move_pattern == MV_TURNING_LEFT_RIGHT ||
7660            move_pattern == MV_TURNING_RIGHT_LEFT ||
7661            move_pattern == MV_TURNING_RANDOM ||
7662            move_pattern == MV_ALL_DIRECTIONS)
7663   {
7664     boolean can_turn_left =
7665       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7666     boolean can_turn_right =
7667       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7668
7669     if (element_info[element].move_stepsize == 0)       // "not moving"
7670       return;
7671
7672     if (move_pattern == MV_TURNING_LEFT)
7673       MovDir[x][y] = left_dir;
7674     else if (move_pattern == MV_TURNING_RIGHT)
7675       MovDir[x][y] = right_dir;
7676     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7677       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7678     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7679       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7680     else if (move_pattern == MV_TURNING_RANDOM)
7681       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7682                       can_turn_right && !can_turn_left ? right_dir :
7683                       RND(2) ? left_dir : right_dir);
7684     else if (can_turn_left && can_turn_right)
7685       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7686     else if (can_turn_left)
7687       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7688     else if (can_turn_right)
7689       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7690     else
7691       MovDir[x][y] = back_dir;
7692
7693     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7694   }
7695   else if (move_pattern == MV_HORIZONTAL ||
7696            move_pattern == MV_VERTICAL)
7697   {
7698     if (move_pattern & old_move_dir)
7699       MovDir[x][y] = back_dir;
7700     else if (move_pattern == MV_HORIZONTAL)
7701       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7702     else if (move_pattern == MV_VERTICAL)
7703       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7704
7705     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7706   }
7707   else if (move_pattern & MV_ANY_DIRECTION)
7708   {
7709     MovDir[x][y] = move_pattern;
7710     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7711   }
7712   else if (move_pattern & MV_WIND_DIRECTION)
7713   {
7714     MovDir[x][y] = game.wind_direction;
7715     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7716   }
7717   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7718   {
7719     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7720       MovDir[x][y] = left_dir;
7721     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7722       MovDir[x][y] = right_dir;
7723
7724     if (MovDir[x][y] != old_move_dir)
7725       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7726   }
7727   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7728   {
7729     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7730       MovDir[x][y] = right_dir;
7731     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7732       MovDir[x][y] = left_dir;
7733
7734     if (MovDir[x][y] != old_move_dir)
7735       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7736   }
7737   else if (move_pattern == MV_TOWARDS_PLAYER ||
7738            move_pattern == MV_AWAY_FROM_PLAYER)
7739   {
7740     int attr_x = -1, attr_y = -1;
7741     int newx, newy;
7742     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7743
7744     if (game.all_players_gone)
7745     {
7746       attr_x = game.exit_x;
7747       attr_y = game.exit_y;
7748     }
7749     else
7750     {
7751       int i;
7752
7753       for (i = 0; i < MAX_PLAYERS; i++)
7754       {
7755         struct PlayerInfo *player = &stored_player[i];
7756         int jx = player->jx, jy = player->jy;
7757
7758         if (!player->active)
7759           continue;
7760
7761         if (attr_x == -1 ||
7762             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7763         {
7764           attr_x = jx;
7765           attr_y = jy;
7766         }
7767       }
7768     }
7769
7770     MovDir[x][y] = MV_NONE;
7771     if (attr_x < x)
7772       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7773     else if (attr_x > x)
7774       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7775     if (attr_y < y)
7776       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7777     else if (attr_y > y)
7778       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7779
7780     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7781
7782     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7783     {
7784       boolean first_horiz = RND(2);
7785       int new_move_dir = MovDir[x][y];
7786
7787       if (element_info[element].move_stepsize == 0)     // "not moving"
7788       {
7789         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7790         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7791
7792         return;
7793       }
7794
7795       MovDir[x][y] =
7796         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7797       Moving2Blocked(x, y, &newx, &newy);
7798
7799       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7800         return;
7801
7802       MovDir[x][y] =
7803         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7804       Moving2Blocked(x, y, &newx, &newy);
7805
7806       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7807         return;
7808
7809       MovDir[x][y] = old_move_dir;
7810     }
7811   }
7812   else if (move_pattern == MV_WHEN_PUSHED ||
7813            move_pattern == MV_WHEN_DROPPED)
7814   {
7815     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7816       MovDir[x][y] = MV_NONE;
7817
7818     MovDelay[x][y] = 0;
7819   }
7820   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7821   {
7822     struct XY *test_xy = xy_topdown;
7823     static int test_dir[4] =
7824     {
7825       MV_UP,
7826       MV_LEFT,
7827       MV_RIGHT,
7828       MV_DOWN
7829     };
7830     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7831     int move_preference = -1000000;     // start with very low preference
7832     int new_move_dir = MV_NONE;
7833     int start_test = RND(4);
7834     int i;
7835
7836     for (i = 0; i < NUM_DIRECTIONS; i++)
7837     {
7838       int j = (start_test + i) % 4;
7839       int move_dir = test_dir[j];
7840       int move_dir_preference;
7841
7842       xx = x + test_xy[j].x;
7843       yy = y + test_xy[j].y;
7844
7845       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7846           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7847       {
7848         new_move_dir = move_dir;
7849
7850         break;
7851       }
7852
7853       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7854         continue;
7855
7856       move_dir_preference = -1 * RunnerVisit[xx][yy];
7857       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7858         move_dir_preference = PlayerVisit[xx][yy];
7859
7860       if (move_dir_preference > move_preference)
7861       {
7862         // prefer field that has not been visited for the longest time
7863         move_preference = move_dir_preference;
7864         new_move_dir = move_dir;
7865       }
7866       else if (move_dir_preference == move_preference &&
7867                move_dir == old_move_dir)
7868       {
7869         // prefer last direction when all directions are preferred equally
7870         move_preference = move_dir_preference;
7871         new_move_dir = move_dir;
7872       }
7873     }
7874
7875     MovDir[x][y] = new_move_dir;
7876     if (old_move_dir != new_move_dir)
7877       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7878   }
7879 }
7880
7881 static void TurnRound(int x, int y)
7882 {
7883   int direction = MovDir[x][y];
7884
7885   TurnRoundExt(x, y);
7886
7887   GfxDir[x][y] = MovDir[x][y];
7888
7889   if (direction != MovDir[x][y])
7890     GfxFrame[x][y] = 0;
7891
7892   if (MovDelay[x][y])
7893     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7894
7895   ResetGfxFrame(x, y);
7896 }
7897
7898 static boolean JustBeingPushed(int x, int y)
7899 {
7900   int i;
7901
7902   for (i = 0; i < MAX_PLAYERS; i++)
7903   {
7904     struct PlayerInfo *player = &stored_player[i];
7905
7906     if (player->active && player->is_pushing && player->MovPos)
7907     {
7908       int next_jx = player->jx + (player->jx - player->last_jx);
7909       int next_jy = player->jy + (player->jy - player->last_jy);
7910
7911       if (x == next_jx && y == next_jy)
7912         return TRUE;
7913     }
7914   }
7915
7916   return FALSE;
7917 }
7918
7919 static void StartMoving(int x, int y)
7920 {
7921   boolean started_moving = FALSE;       // some elements can fall _and_ move
7922   int element = Tile[x][y];
7923
7924   if (Stop[x][y])
7925     return;
7926
7927   if (MovDelay[x][y] == 0)
7928     GfxAction[x][y] = ACTION_DEFAULT;
7929
7930   if (CAN_FALL(element) && y < lev_fieldy - 1)
7931   {
7932     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7933         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7934       if (JustBeingPushed(x, y))
7935         return;
7936
7937     if (element == EL_QUICKSAND_FULL)
7938     {
7939       if (IS_FREE(x, y + 1))
7940       {
7941         InitMovingField(x, y, MV_DOWN);
7942         started_moving = TRUE;
7943
7944         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7945 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7946         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7947           Store[x][y] = EL_ROCK;
7948 #else
7949         Store[x][y] = EL_ROCK;
7950 #endif
7951
7952         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7953       }
7954       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7955       {
7956         if (!MovDelay[x][y])
7957         {
7958           MovDelay[x][y] = TILEY + 1;
7959
7960           ResetGfxAnimation(x, y);
7961           ResetGfxAnimation(x, y + 1);
7962         }
7963
7964         if (MovDelay[x][y])
7965         {
7966           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7967           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7968
7969           MovDelay[x][y]--;
7970           if (MovDelay[x][y])
7971             return;
7972         }
7973
7974         Tile[x][y] = EL_QUICKSAND_EMPTY;
7975         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7976         Store[x][y + 1] = Store[x][y];
7977         Store[x][y] = 0;
7978
7979         PlayLevelSoundAction(x, y, ACTION_FILLING);
7980       }
7981       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7982       {
7983         if (!MovDelay[x][y])
7984         {
7985           MovDelay[x][y] = TILEY + 1;
7986
7987           ResetGfxAnimation(x, y);
7988           ResetGfxAnimation(x, y + 1);
7989         }
7990
7991         if (MovDelay[x][y])
7992         {
7993           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7994           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7995
7996           MovDelay[x][y]--;
7997           if (MovDelay[x][y])
7998             return;
7999         }
8000
8001         Tile[x][y] = EL_QUICKSAND_EMPTY;
8002         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8003         Store[x][y + 1] = Store[x][y];
8004         Store[x][y] = 0;
8005
8006         PlayLevelSoundAction(x, y, ACTION_FILLING);
8007       }
8008     }
8009     else if (element == EL_QUICKSAND_FAST_FULL)
8010     {
8011       if (IS_FREE(x, y + 1))
8012       {
8013         InitMovingField(x, y, MV_DOWN);
8014         started_moving = TRUE;
8015
8016         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8017 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8018         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8019           Store[x][y] = EL_ROCK;
8020 #else
8021         Store[x][y] = EL_ROCK;
8022 #endif
8023
8024         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8025       }
8026       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8027       {
8028         if (!MovDelay[x][y])
8029         {
8030           MovDelay[x][y] = TILEY + 1;
8031
8032           ResetGfxAnimation(x, y);
8033           ResetGfxAnimation(x, y + 1);
8034         }
8035
8036         if (MovDelay[x][y])
8037         {
8038           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8039           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8040
8041           MovDelay[x][y]--;
8042           if (MovDelay[x][y])
8043             return;
8044         }
8045
8046         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8047         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8048         Store[x][y + 1] = Store[x][y];
8049         Store[x][y] = 0;
8050
8051         PlayLevelSoundAction(x, y, ACTION_FILLING);
8052       }
8053       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8054       {
8055         if (!MovDelay[x][y])
8056         {
8057           MovDelay[x][y] = TILEY + 1;
8058
8059           ResetGfxAnimation(x, y);
8060           ResetGfxAnimation(x, y + 1);
8061         }
8062
8063         if (MovDelay[x][y])
8064         {
8065           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8066           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8067
8068           MovDelay[x][y]--;
8069           if (MovDelay[x][y])
8070             return;
8071         }
8072
8073         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8074         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8075         Store[x][y + 1] = Store[x][y];
8076         Store[x][y] = 0;
8077
8078         PlayLevelSoundAction(x, y, ACTION_FILLING);
8079       }
8080     }
8081     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8082              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8083     {
8084       InitMovingField(x, y, MV_DOWN);
8085       started_moving = TRUE;
8086
8087       Tile[x][y] = EL_QUICKSAND_FILLING;
8088       Store[x][y] = element;
8089
8090       PlayLevelSoundAction(x, y, ACTION_FILLING);
8091     }
8092     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8093              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8094     {
8095       InitMovingField(x, y, MV_DOWN);
8096       started_moving = TRUE;
8097
8098       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8099       Store[x][y] = element;
8100
8101       PlayLevelSoundAction(x, y, ACTION_FILLING);
8102     }
8103     else if (element == EL_MAGIC_WALL_FULL)
8104     {
8105       if (IS_FREE(x, y + 1))
8106       {
8107         InitMovingField(x, y, MV_DOWN);
8108         started_moving = TRUE;
8109
8110         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8111         Store[x][y] = EL_CHANGED(Store[x][y]);
8112       }
8113       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8114       {
8115         if (!MovDelay[x][y])
8116           MovDelay[x][y] = TILEY / 4 + 1;
8117
8118         if (MovDelay[x][y])
8119         {
8120           MovDelay[x][y]--;
8121           if (MovDelay[x][y])
8122             return;
8123         }
8124
8125         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8126         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8127         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8128         Store[x][y] = 0;
8129       }
8130     }
8131     else if (element == EL_BD_MAGIC_WALL_FULL)
8132     {
8133       if (IS_FREE(x, y + 1))
8134       {
8135         InitMovingField(x, y, MV_DOWN);
8136         started_moving = TRUE;
8137
8138         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8139         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8140       }
8141       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8142       {
8143         if (!MovDelay[x][y])
8144           MovDelay[x][y] = TILEY / 4 + 1;
8145
8146         if (MovDelay[x][y])
8147         {
8148           MovDelay[x][y]--;
8149           if (MovDelay[x][y])
8150             return;
8151         }
8152
8153         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8154         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8155         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8156         Store[x][y] = 0;
8157       }
8158     }
8159     else if (element == EL_DC_MAGIC_WALL_FULL)
8160     {
8161       if (IS_FREE(x, y + 1))
8162       {
8163         InitMovingField(x, y, MV_DOWN);
8164         started_moving = TRUE;
8165
8166         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8167         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8168       }
8169       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8170       {
8171         if (!MovDelay[x][y])
8172           MovDelay[x][y] = TILEY / 4 + 1;
8173
8174         if (MovDelay[x][y])
8175         {
8176           MovDelay[x][y]--;
8177           if (MovDelay[x][y])
8178             return;
8179         }
8180
8181         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8182         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8183         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8184         Store[x][y] = 0;
8185       }
8186     }
8187     else if ((CAN_PASS_MAGIC_WALL(element) &&
8188               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8189                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8190              (CAN_PASS_DC_MAGIC_WALL(element) &&
8191               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8192
8193     {
8194       InitMovingField(x, y, MV_DOWN);
8195       started_moving = TRUE;
8196
8197       Tile[x][y] =
8198         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8199          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8200          EL_DC_MAGIC_WALL_FILLING);
8201       Store[x][y] = element;
8202     }
8203     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8204     {
8205       SplashAcid(x, y + 1);
8206
8207       InitMovingField(x, y, MV_DOWN);
8208       started_moving = TRUE;
8209
8210       Store[x][y] = EL_ACID;
8211     }
8212     else if (
8213              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8214               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8215              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8216               CAN_FALL(element) && WasJustFalling[x][y] &&
8217               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8218
8219              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8220               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8221               (Tile[x][y + 1] == EL_BLOCKED)))
8222     {
8223       /* this is needed for a special case not covered by calling "Impact()"
8224          from "ContinueMoving()": if an element moves to a tile directly below
8225          another element which was just falling on that tile (which was empty
8226          in the previous frame), the falling element above would just stop
8227          instead of smashing the element below (in previous version, the above
8228          element was just checked for "moving" instead of "falling", resulting
8229          in incorrect smashes caused by horizontal movement of the above
8230          element; also, the case of the player being the element to smash was
8231          simply not covered here... :-/ ) */
8232
8233       CheckCollision[x][y] = 0;
8234       CheckImpact[x][y] = 0;
8235
8236       Impact(x, y);
8237     }
8238     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8239     {
8240       if (MovDir[x][y] == MV_NONE)
8241       {
8242         InitMovingField(x, y, MV_DOWN);
8243         started_moving = TRUE;
8244       }
8245     }
8246     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8247     {
8248       if (WasJustFalling[x][y]) // prevent animation from being restarted
8249         MovDir[x][y] = MV_DOWN;
8250
8251       InitMovingField(x, y, MV_DOWN);
8252       started_moving = TRUE;
8253     }
8254     else if (element == EL_AMOEBA_DROP)
8255     {
8256       Tile[x][y] = EL_AMOEBA_GROWING;
8257       Store[x][y] = EL_AMOEBA_WET;
8258     }
8259     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8260               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8261              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8262              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8263     {
8264       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8265                                 (IS_FREE(x - 1, y + 1) ||
8266                                  Tile[x - 1][y + 1] == EL_ACID));
8267       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8268                                 (IS_FREE(x + 1, y + 1) ||
8269                                  Tile[x + 1][y + 1] == EL_ACID));
8270       boolean can_fall_any  = (can_fall_left || can_fall_right);
8271       boolean can_fall_both = (can_fall_left && can_fall_right);
8272       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8273
8274       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8275       {
8276         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8277           can_fall_right = FALSE;
8278         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8279           can_fall_left = FALSE;
8280         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8281           can_fall_right = FALSE;
8282         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8283           can_fall_left = FALSE;
8284
8285         can_fall_any  = (can_fall_left || can_fall_right);
8286         can_fall_both = FALSE;
8287       }
8288
8289       if (can_fall_both)
8290       {
8291         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8292           can_fall_right = FALSE;       // slip down on left side
8293         else
8294           can_fall_left = !(can_fall_right = RND(2));
8295
8296         can_fall_both = FALSE;
8297       }
8298
8299       if (can_fall_any)
8300       {
8301         // if not determined otherwise, prefer left side for slipping down
8302         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8303         started_moving = TRUE;
8304       }
8305     }
8306     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8307     {
8308       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8309       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8310       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8311       int belt_dir = game.belt_dir[belt_nr];
8312
8313       if ((belt_dir == MV_LEFT  && left_is_free) ||
8314           (belt_dir == MV_RIGHT && right_is_free))
8315       {
8316         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8317
8318         InitMovingField(x, y, belt_dir);
8319         started_moving = TRUE;
8320
8321         Pushed[x][y] = TRUE;
8322         Pushed[nextx][y] = TRUE;
8323
8324         GfxAction[x][y] = ACTION_DEFAULT;
8325       }
8326       else
8327       {
8328         MovDir[x][y] = 0;       // if element was moving, stop it
8329       }
8330     }
8331   }
8332
8333   // not "else if" because of elements that can fall and move (EL_SPRING)
8334   if (CAN_MOVE(element) && !started_moving)
8335   {
8336     int move_pattern = element_info[element].move_pattern;
8337     int newx, newy;
8338
8339     Moving2Blocked(x, y, &newx, &newy);
8340
8341     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8342       return;
8343
8344     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8345         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8346     {
8347       WasJustMoving[x][y] = 0;
8348       CheckCollision[x][y] = 0;
8349
8350       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8351
8352       if (Tile[x][y] != element)        // element has changed
8353         return;
8354     }
8355
8356     if (!MovDelay[x][y])        // start new movement phase
8357     {
8358       // all objects that can change their move direction after each step
8359       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8360
8361       if (element != EL_YAMYAM &&
8362           element != EL_DARK_YAMYAM &&
8363           element != EL_PACMAN &&
8364           !(move_pattern & MV_ANY_DIRECTION) &&
8365           move_pattern != MV_TURNING_LEFT &&
8366           move_pattern != MV_TURNING_RIGHT &&
8367           move_pattern != MV_TURNING_LEFT_RIGHT &&
8368           move_pattern != MV_TURNING_RIGHT_LEFT &&
8369           move_pattern != MV_TURNING_RANDOM)
8370       {
8371         TurnRound(x, y);
8372
8373         if (MovDelay[x][y] && (element == EL_BUG ||
8374                                element == EL_SPACESHIP ||
8375                                element == EL_SP_SNIKSNAK ||
8376                                element == EL_SP_ELECTRON ||
8377                                element == EL_MOLE))
8378           TEST_DrawLevelField(x, y);
8379       }
8380     }
8381
8382     if (MovDelay[x][y])         // wait some time before next movement
8383     {
8384       MovDelay[x][y]--;
8385
8386       if (element == EL_ROBOT ||
8387           element == EL_YAMYAM ||
8388           element == EL_DARK_YAMYAM)
8389       {
8390         DrawLevelElementAnimationIfNeeded(x, y, element);
8391         PlayLevelSoundAction(x, y, ACTION_WAITING);
8392       }
8393       else if (element == EL_SP_ELECTRON)
8394         DrawLevelElementAnimationIfNeeded(x, y, element);
8395       else if (element == EL_DRAGON)
8396       {
8397         int i;
8398         int dir = MovDir[x][y];
8399         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8400         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8401         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8402                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8403                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8404                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8405         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8406
8407         GfxAction[x][y] = ACTION_ATTACKING;
8408
8409         if (IS_PLAYER(x, y))
8410           DrawPlayerField(x, y);
8411         else
8412           TEST_DrawLevelField(x, y);
8413
8414         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8415
8416         for (i = 1; i <= 3; i++)
8417         {
8418           int xx = x + i * dx;
8419           int yy = y + i * dy;
8420           int sx = SCREENX(xx);
8421           int sy = SCREENY(yy);
8422           int flame_graphic = graphic + (i - 1);
8423
8424           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8425             break;
8426
8427           if (MovDelay[x][y])
8428           {
8429             int flamed = MovingOrBlocked2Element(xx, yy);
8430
8431             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8432               Bang(xx, yy);
8433             else
8434               RemoveMovingField(xx, yy);
8435
8436             ChangeDelay[xx][yy] = 0;
8437
8438             Tile[xx][yy] = EL_FLAMES;
8439
8440             if (IN_SCR_FIELD(sx, sy))
8441             {
8442               TEST_DrawLevelFieldCrumbled(xx, yy);
8443               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8444             }
8445           }
8446           else
8447           {
8448             if (Tile[xx][yy] == EL_FLAMES)
8449               Tile[xx][yy] = EL_EMPTY;
8450             TEST_DrawLevelField(xx, yy);
8451           }
8452         }
8453       }
8454
8455       if (MovDelay[x][y])       // element still has to wait some time
8456       {
8457         PlayLevelSoundAction(x, y, ACTION_WAITING);
8458
8459         return;
8460       }
8461     }
8462
8463     // now make next step
8464
8465     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8466
8467     if (DONT_COLLIDE_WITH(element) &&
8468         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8469         !PLAYER_ENEMY_PROTECTED(newx, newy))
8470     {
8471       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8472
8473       return;
8474     }
8475
8476     else if (CAN_MOVE_INTO_ACID(element) &&
8477              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8478              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8479              (MovDir[x][y] == MV_DOWN ||
8480               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8481     {
8482       SplashAcid(newx, newy);
8483       Store[x][y] = EL_ACID;
8484     }
8485     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8486     {
8487       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8488           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8489           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8490           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8491       {
8492         RemoveField(x, y);
8493         TEST_DrawLevelField(x, y);
8494
8495         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8496         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8497           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8498
8499         game.friends_still_needed--;
8500         if (!game.friends_still_needed &&
8501             !game.GameOver &&
8502             game.all_players_gone)
8503           LevelSolved();
8504
8505         return;
8506       }
8507       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8508       {
8509         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8510           TEST_DrawLevelField(newx, newy);
8511         else
8512           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8513       }
8514       else if (!IS_FREE(newx, newy))
8515       {
8516         GfxAction[x][y] = ACTION_WAITING;
8517
8518         if (IS_PLAYER(x, y))
8519           DrawPlayerField(x, y);
8520         else
8521           TEST_DrawLevelField(x, y);
8522
8523         return;
8524       }
8525     }
8526     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8527     {
8528       if (IS_FOOD_PIG(Tile[newx][newy]))
8529       {
8530         if (IS_MOVING(newx, newy))
8531           RemoveMovingField(newx, newy);
8532         else
8533         {
8534           Tile[newx][newy] = EL_EMPTY;
8535           TEST_DrawLevelField(newx, newy);
8536         }
8537
8538         PlayLevelSound(x, y, SND_PIG_DIGGING);
8539       }
8540       else if (!IS_FREE(newx, newy))
8541       {
8542         if (IS_PLAYER(x, y))
8543           DrawPlayerField(x, y);
8544         else
8545           TEST_DrawLevelField(x, y);
8546
8547         return;
8548       }
8549     }
8550     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8551     {
8552       if (Store[x][y] != EL_EMPTY)
8553       {
8554         boolean can_clone = FALSE;
8555         int xx, yy;
8556
8557         // check if element to clone is still there
8558         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8559         {
8560           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8561           {
8562             can_clone = TRUE;
8563
8564             break;
8565           }
8566         }
8567
8568         // cannot clone or target field not free anymore -- do not clone
8569         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8570           Store[x][y] = EL_EMPTY;
8571       }
8572
8573       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8574       {
8575         if (IS_MV_DIAGONAL(MovDir[x][y]))
8576         {
8577           int diagonal_move_dir = MovDir[x][y];
8578           int stored = Store[x][y];
8579           int change_delay = 8;
8580           int graphic;
8581
8582           // android is moving diagonally
8583
8584           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8585
8586           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8587           GfxElement[x][y] = EL_EMC_ANDROID;
8588           GfxAction[x][y] = ACTION_SHRINKING;
8589           GfxDir[x][y] = diagonal_move_dir;
8590           ChangeDelay[x][y] = change_delay;
8591
8592           if (Store[x][y] == EL_EMPTY)
8593             Store[x][y] = GfxElementEmpty[x][y];
8594
8595           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8596                                    GfxDir[x][y]);
8597
8598           DrawLevelGraphicAnimation(x, y, graphic);
8599           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8600
8601           if (Tile[newx][newy] == EL_ACID)
8602           {
8603             SplashAcid(newx, newy);
8604
8605             return;
8606           }
8607
8608           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8609
8610           Store[newx][newy] = EL_EMC_ANDROID;
8611           GfxElement[newx][newy] = EL_EMC_ANDROID;
8612           GfxAction[newx][newy] = ACTION_GROWING;
8613           GfxDir[newx][newy] = diagonal_move_dir;
8614           ChangeDelay[newx][newy] = change_delay;
8615
8616           graphic = el_act_dir2img(GfxElement[newx][newy],
8617                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8618
8619           DrawLevelGraphicAnimation(newx, newy, graphic);
8620           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8621
8622           return;
8623         }
8624         else
8625         {
8626           Tile[newx][newy] = EL_EMPTY;
8627           TEST_DrawLevelField(newx, newy);
8628
8629           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8630         }
8631       }
8632       else if (!IS_FREE(newx, newy))
8633       {
8634         return;
8635       }
8636     }
8637     else if (IS_CUSTOM_ELEMENT(element) &&
8638              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8639     {
8640       if (!DigFieldByCE(newx, newy, element))
8641         return;
8642
8643       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8644       {
8645         RunnerVisit[x][y] = FrameCounter;
8646         PlayerVisit[x][y] /= 8;         // expire player visit path
8647       }
8648     }
8649     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8650     {
8651       if (!IS_FREE(newx, newy))
8652       {
8653         if (IS_PLAYER(x, y))
8654           DrawPlayerField(x, y);
8655         else
8656           TEST_DrawLevelField(x, y);
8657
8658         return;
8659       }
8660       else
8661       {
8662         boolean wanna_flame = !RND(10);
8663         int dx = newx - x, dy = newy - y;
8664         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8665         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8666         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8667                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8668         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8669                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8670
8671         if ((wanna_flame ||
8672              IS_CLASSIC_ENEMY(element1) ||
8673              IS_CLASSIC_ENEMY(element2)) &&
8674             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8675             element1 != EL_FLAMES && element2 != EL_FLAMES)
8676         {
8677           ResetGfxAnimation(x, y);
8678           GfxAction[x][y] = ACTION_ATTACKING;
8679
8680           if (IS_PLAYER(x, y))
8681             DrawPlayerField(x, y);
8682           else
8683             TEST_DrawLevelField(x, y);
8684
8685           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8686
8687           MovDelay[x][y] = 50;
8688
8689           Tile[newx][newy] = EL_FLAMES;
8690           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8691             Tile[newx1][newy1] = EL_FLAMES;
8692           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8693             Tile[newx2][newy2] = EL_FLAMES;
8694
8695           return;
8696         }
8697       }
8698     }
8699     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8700              Tile[newx][newy] == EL_DIAMOND)
8701     {
8702       if (IS_MOVING(newx, newy))
8703         RemoveMovingField(newx, newy);
8704       else
8705       {
8706         Tile[newx][newy] = EL_EMPTY;
8707         TEST_DrawLevelField(newx, newy);
8708       }
8709
8710       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8711     }
8712     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8713              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8714     {
8715       if (AmoebaNr[newx][newy])
8716       {
8717         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8718         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8719             Tile[newx][newy] == EL_BD_AMOEBA)
8720           AmoebaCnt[AmoebaNr[newx][newy]]--;
8721       }
8722
8723       if (IS_MOVING(newx, newy))
8724       {
8725         RemoveMovingField(newx, newy);
8726       }
8727       else
8728       {
8729         Tile[newx][newy] = EL_EMPTY;
8730         TEST_DrawLevelField(newx, newy);
8731       }
8732
8733       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8734     }
8735     else if ((element == EL_PACMAN || element == EL_MOLE)
8736              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8737     {
8738       if (AmoebaNr[newx][newy])
8739       {
8740         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8741         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8742             Tile[newx][newy] == EL_BD_AMOEBA)
8743           AmoebaCnt[AmoebaNr[newx][newy]]--;
8744       }
8745
8746       if (element == EL_MOLE)
8747       {
8748         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8749         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8750
8751         ResetGfxAnimation(x, y);
8752         GfxAction[x][y] = ACTION_DIGGING;
8753         TEST_DrawLevelField(x, y);
8754
8755         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8756
8757         return;                         // wait for shrinking amoeba
8758       }
8759       else      // element == EL_PACMAN
8760       {
8761         Tile[newx][newy] = EL_EMPTY;
8762         TEST_DrawLevelField(newx, newy);
8763         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8764       }
8765     }
8766     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8767              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8768               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8769     {
8770       // wait for shrinking amoeba to completely disappear
8771       return;
8772     }
8773     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8774     {
8775       // object was running against a wall
8776
8777       TurnRound(x, y);
8778
8779       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8780         DrawLevelElementAnimation(x, y, element);
8781
8782       if (DONT_TOUCH(element))
8783         TestIfBadThingTouchesPlayer(x, y);
8784
8785       return;
8786     }
8787
8788     InitMovingField(x, y, MovDir[x][y]);
8789
8790     PlayLevelSoundAction(x, y, ACTION_MOVING);
8791   }
8792
8793   if (MovDir[x][y])
8794     ContinueMoving(x, y);
8795 }
8796
8797 void ContinueMoving(int x, int y)
8798 {
8799   int element = Tile[x][y];
8800   struct ElementInfo *ei = &element_info[element];
8801   int direction = MovDir[x][y];
8802   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8803   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8804   int newx = x + dx, newy = y + dy;
8805   int stored = Store[x][y];
8806   int stored_new = Store[newx][newy];
8807   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8808   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8809   boolean last_line = (newy == lev_fieldy - 1);
8810   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8811
8812   if (pushed_by_player)         // special case: moving object pushed by player
8813   {
8814     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8815   }
8816   else if (use_step_delay)      // special case: moving object has step delay
8817   {
8818     if (!MovDelay[x][y])
8819       MovPos[x][y] += getElementMoveStepsize(x, y);
8820
8821     if (MovDelay[x][y])
8822       MovDelay[x][y]--;
8823     else
8824       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8825
8826     if (MovDelay[x][y])
8827     {
8828       TEST_DrawLevelField(x, y);
8829
8830       return;   // element is still waiting
8831     }
8832   }
8833   else                          // normal case: generically moving object
8834   {
8835     MovPos[x][y] += getElementMoveStepsize(x, y);
8836   }
8837
8838   if (ABS(MovPos[x][y]) < TILEX)
8839   {
8840     TEST_DrawLevelField(x, y);
8841
8842     return;     // element is still moving
8843   }
8844
8845   // element reached destination field
8846
8847   Tile[x][y] = EL_EMPTY;
8848   Tile[newx][newy] = element;
8849   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8850
8851   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8852   {
8853     element = Tile[newx][newy] = EL_ACID;
8854   }
8855   else if (element == EL_MOLE)
8856   {
8857     Tile[x][y] = EL_SAND;
8858
8859     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8860   }
8861   else if (element == EL_QUICKSAND_FILLING)
8862   {
8863     element = Tile[newx][newy] = get_next_element(element);
8864     Store[newx][newy] = Store[x][y];
8865   }
8866   else if (element == EL_QUICKSAND_EMPTYING)
8867   {
8868     Tile[x][y] = get_next_element(element);
8869     element = Tile[newx][newy] = Store[x][y];
8870   }
8871   else if (element == EL_QUICKSAND_FAST_FILLING)
8872   {
8873     element = Tile[newx][newy] = get_next_element(element);
8874     Store[newx][newy] = Store[x][y];
8875   }
8876   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8877   {
8878     Tile[x][y] = get_next_element(element);
8879     element = Tile[newx][newy] = Store[x][y];
8880   }
8881   else if (element == EL_MAGIC_WALL_FILLING)
8882   {
8883     element = Tile[newx][newy] = get_next_element(element);
8884     if (!game.magic_wall_active)
8885       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8886     Store[newx][newy] = Store[x][y];
8887   }
8888   else if (element == EL_MAGIC_WALL_EMPTYING)
8889   {
8890     Tile[x][y] = get_next_element(element);
8891     if (!game.magic_wall_active)
8892       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8893     element = Tile[newx][newy] = Store[x][y];
8894
8895     InitField(newx, newy, FALSE);
8896   }
8897   else if (element == EL_BD_MAGIC_WALL_FILLING)
8898   {
8899     element = Tile[newx][newy] = get_next_element(element);
8900     if (!game.magic_wall_active)
8901       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8902     Store[newx][newy] = Store[x][y];
8903   }
8904   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8905   {
8906     Tile[x][y] = get_next_element(element);
8907     if (!game.magic_wall_active)
8908       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8909     element = Tile[newx][newy] = Store[x][y];
8910
8911     InitField(newx, newy, FALSE);
8912   }
8913   else if (element == EL_DC_MAGIC_WALL_FILLING)
8914   {
8915     element = Tile[newx][newy] = get_next_element(element);
8916     if (!game.magic_wall_active)
8917       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8918     Store[newx][newy] = Store[x][y];
8919   }
8920   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8921   {
8922     Tile[x][y] = get_next_element(element);
8923     if (!game.magic_wall_active)
8924       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8925     element = Tile[newx][newy] = Store[x][y];
8926
8927     InitField(newx, newy, FALSE);
8928   }
8929   else if (element == EL_AMOEBA_DROPPING)
8930   {
8931     Tile[x][y] = get_next_element(element);
8932     element = Tile[newx][newy] = Store[x][y];
8933   }
8934   else if (element == EL_SOKOBAN_OBJECT)
8935   {
8936     if (Back[x][y])
8937       Tile[x][y] = Back[x][y];
8938
8939     if (Back[newx][newy])
8940       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8941
8942     Back[x][y] = Back[newx][newy] = 0;
8943   }
8944
8945   Store[x][y] = EL_EMPTY;
8946   MovPos[x][y] = 0;
8947   MovDir[x][y] = 0;
8948   MovDelay[x][y] = 0;
8949
8950   MovDelay[newx][newy] = 0;
8951
8952   if (CAN_CHANGE_OR_HAS_ACTION(element))
8953   {
8954     // copy element change control values to new field
8955     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8956     ChangePage[newx][newy]  = ChangePage[x][y];
8957     ChangeCount[newx][newy] = ChangeCount[x][y];
8958     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8959   }
8960
8961   CustomValue[newx][newy] = CustomValue[x][y];
8962
8963   ChangeDelay[x][y] = 0;
8964   ChangePage[x][y] = -1;
8965   ChangeCount[x][y] = 0;
8966   ChangeEvent[x][y] = -1;
8967
8968   CustomValue[x][y] = 0;
8969
8970   // copy animation control values to new field
8971   GfxFrame[newx][newy]  = GfxFrame[x][y];
8972   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8973   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8974   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8975
8976   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8977
8978   // some elements can leave other elements behind after moving
8979   if (ei->move_leave_element != EL_EMPTY &&
8980       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8981       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8982   {
8983     int move_leave_element = ei->move_leave_element;
8984
8985     // this makes it possible to leave the removed element again
8986     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8987       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8988
8989     Tile[x][y] = move_leave_element;
8990
8991     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8992       MovDir[x][y] = direction;
8993
8994     InitField(x, y, FALSE);
8995
8996     if (GFX_CRUMBLED(Tile[x][y]))
8997       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8998
8999     if (IS_PLAYER_ELEMENT(move_leave_element))
9000       RelocatePlayer(x, y, move_leave_element);
9001   }
9002
9003   // do this after checking for left-behind element
9004   ResetGfxAnimation(x, y);      // reset animation values for old field
9005
9006   if (!CAN_MOVE(element) ||
9007       (CAN_FALL(element) && direction == MV_DOWN &&
9008        (element == EL_SPRING ||
9009         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9010         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9011     GfxDir[x][y] = MovDir[newx][newy] = 0;
9012
9013   TEST_DrawLevelField(x, y);
9014   TEST_DrawLevelField(newx, newy);
9015
9016   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9017
9018   // prevent pushed element from moving on in pushed direction
9019   if (pushed_by_player && CAN_MOVE(element) &&
9020       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9021       !(element_info[element].move_pattern & direction))
9022     TurnRound(newx, newy);
9023
9024   // prevent elements on conveyor belt from moving on in last direction
9025   if (pushed_by_conveyor && CAN_FALL(element) &&
9026       direction & MV_HORIZONTAL)
9027     MovDir[newx][newy] = 0;
9028
9029   if (!pushed_by_player)
9030   {
9031     int nextx = newx + dx, nexty = newy + dy;
9032     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9033
9034     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9035
9036     if (CAN_FALL(element) && direction == MV_DOWN)
9037       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9038
9039     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9040       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9041
9042     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9043       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9044   }
9045
9046   if (DONT_TOUCH(element))      // object may be nasty to player or others
9047   {
9048     TestIfBadThingTouchesPlayer(newx, newy);
9049     TestIfBadThingTouchesFriend(newx, newy);
9050
9051     if (!IS_CUSTOM_ELEMENT(element))
9052       TestIfBadThingTouchesOtherBadThing(newx, newy);
9053   }
9054   else if (element == EL_PENGUIN)
9055     TestIfFriendTouchesBadThing(newx, newy);
9056
9057   if (DONT_GET_HIT_BY(element))
9058   {
9059     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9060   }
9061
9062   // give the player one last chance (one more frame) to move away
9063   if (CAN_FALL(element) && direction == MV_DOWN &&
9064       (last_line || (!IS_FREE(x, newy + 1) &&
9065                      (!IS_PLAYER(x, newy + 1) ||
9066                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9067     Impact(x, newy);
9068
9069   if (pushed_by_player && !game.use_change_when_pushing_bug)
9070   {
9071     int push_side = MV_DIR_OPPOSITE(direction);
9072     struct PlayerInfo *player = PLAYERINFO(x, y);
9073
9074     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9075                                player->index_bit, push_side);
9076     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9077                                         player->index_bit, push_side);
9078   }
9079
9080   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9081     MovDelay[newx][newy] = 1;
9082
9083   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9084
9085   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9086   TestIfElementHitsCustomElement(newx, newy, direction);
9087   TestIfPlayerTouchesCustomElement(newx, newy);
9088   TestIfElementTouchesCustomElement(newx, newy);
9089
9090   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9091       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9092     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9093                              MV_DIR_OPPOSITE(direction));
9094 }
9095
9096 int AmoebaNeighbourNr(int ax, int ay)
9097 {
9098   int i;
9099   int element = Tile[ax][ay];
9100   int group_nr = 0;
9101   struct XY *xy = xy_topdown;
9102
9103   for (i = 0; i < NUM_DIRECTIONS; i++)
9104   {
9105     int x = ax + xy[i].x;
9106     int y = ay + xy[i].y;
9107
9108     if (!IN_LEV_FIELD(x, y))
9109       continue;
9110
9111     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9112       group_nr = AmoebaNr[x][y];
9113   }
9114
9115   return group_nr;
9116 }
9117
9118 static void AmoebaMerge(int ax, int ay)
9119 {
9120   int i, x, y, xx, yy;
9121   int new_group_nr = AmoebaNr[ax][ay];
9122   struct XY *xy = xy_topdown;
9123
9124   if (new_group_nr == 0)
9125     return;
9126
9127   for (i = 0; i < NUM_DIRECTIONS; i++)
9128   {
9129     x = ax + xy[i].x;
9130     y = ay + xy[i].y;
9131
9132     if (!IN_LEV_FIELD(x, y))
9133       continue;
9134
9135     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9136          Tile[x][y] == EL_BD_AMOEBA ||
9137          Tile[x][y] == EL_AMOEBA_DEAD) &&
9138         AmoebaNr[x][y] != new_group_nr)
9139     {
9140       int old_group_nr = AmoebaNr[x][y];
9141
9142       if (old_group_nr == 0)
9143         return;
9144
9145       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9146       AmoebaCnt[old_group_nr] = 0;
9147       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9148       AmoebaCnt2[old_group_nr] = 0;
9149
9150       SCAN_PLAYFIELD(xx, yy)
9151       {
9152         if (AmoebaNr[xx][yy] == old_group_nr)
9153           AmoebaNr[xx][yy] = new_group_nr;
9154       }
9155     }
9156   }
9157 }
9158
9159 void AmoebaToDiamond(int ax, int ay)
9160 {
9161   int i, x, y;
9162
9163   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9164   {
9165     int group_nr = AmoebaNr[ax][ay];
9166
9167 #ifdef DEBUG
9168     if (group_nr == 0)
9169     {
9170       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9171       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9172
9173       return;
9174     }
9175 #endif
9176
9177     SCAN_PLAYFIELD(x, y)
9178     {
9179       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9180       {
9181         AmoebaNr[x][y] = 0;
9182         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9183       }
9184     }
9185
9186     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9187                             SND_AMOEBA_TURNING_TO_GEM :
9188                             SND_AMOEBA_TURNING_TO_ROCK));
9189     Bang(ax, ay);
9190   }
9191   else
9192   {
9193     struct XY *xy = xy_topdown;
9194
9195     for (i = 0; i < NUM_DIRECTIONS; i++)
9196     {
9197       x = ax + xy[i].x;
9198       y = ay + xy[i].y;
9199
9200       if (!IN_LEV_FIELD(x, y))
9201         continue;
9202
9203       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9204       {
9205         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9206                               SND_AMOEBA_TURNING_TO_GEM :
9207                               SND_AMOEBA_TURNING_TO_ROCK));
9208         Bang(x, y);
9209       }
9210     }
9211   }
9212 }
9213
9214 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9215 {
9216   int x, y;
9217   int group_nr = AmoebaNr[ax][ay];
9218   boolean done = FALSE;
9219
9220 #ifdef DEBUG
9221   if (group_nr == 0)
9222   {
9223     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9224     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9225
9226     return;
9227   }
9228 #endif
9229
9230   SCAN_PLAYFIELD(x, y)
9231   {
9232     if (AmoebaNr[x][y] == group_nr &&
9233         (Tile[x][y] == EL_AMOEBA_DEAD ||
9234          Tile[x][y] == EL_BD_AMOEBA ||
9235          Tile[x][y] == EL_AMOEBA_GROWING))
9236     {
9237       AmoebaNr[x][y] = 0;
9238       Tile[x][y] = new_element;
9239       InitField(x, y, FALSE);
9240       TEST_DrawLevelField(x, y);
9241       done = TRUE;
9242     }
9243   }
9244
9245   if (done)
9246     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9247                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9248                             SND_BD_AMOEBA_TURNING_TO_GEM));
9249 }
9250
9251 static void AmoebaGrowing(int x, int y)
9252 {
9253   static DelayCounter sound_delay = { 0 };
9254
9255   if (!MovDelay[x][y])          // start new growing cycle
9256   {
9257     MovDelay[x][y] = 7;
9258
9259     if (DelayReached(&sound_delay))
9260     {
9261       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9262       sound_delay.value = 30;
9263     }
9264   }
9265
9266   if (MovDelay[x][y])           // wait some time before growing bigger
9267   {
9268     MovDelay[x][y]--;
9269     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9270     {
9271       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9272                                            6 - MovDelay[x][y]);
9273
9274       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9275     }
9276
9277     if (!MovDelay[x][y])
9278     {
9279       Tile[x][y] = Store[x][y];
9280       Store[x][y] = 0;
9281       TEST_DrawLevelField(x, y);
9282     }
9283   }
9284 }
9285
9286 static void AmoebaShrinking(int x, int y)
9287 {
9288   static DelayCounter sound_delay = { 0 };
9289
9290   if (!MovDelay[x][y])          // start new shrinking cycle
9291   {
9292     MovDelay[x][y] = 7;
9293
9294     if (DelayReached(&sound_delay))
9295       sound_delay.value = 30;
9296   }
9297
9298   if (MovDelay[x][y])           // wait some time before shrinking
9299   {
9300     MovDelay[x][y]--;
9301     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9302     {
9303       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9304                                            6 - MovDelay[x][y]);
9305
9306       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9307     }
9308
9309     if (!MovDelay[x][y])
9310     {
9311       Tile[x][y] = EL_EMPTY;
9312       TEST_DrawLevelField(x, y);
9313
9314       // don't let mole enter this field in this cycle;
9315       // (give priority to objects falling to this field from above)
9316       Stop[x][y] = TRUE;
9317     }
9318   }
9319 }
9320
9321 static void AmoebaReproduce(int ax, int ay)
9322 {
9323   int i;
9324   int element = Tile[ax][ay];
9325   int graphic = el2img(element);
9326   int newax = ax, neway = ay;
9327   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9328   struct XY *xy = xy_topdown;
9329
9330   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9331   {
9332     Tile[ax][ay] = EL_AMOEBA_DEAD;
9333     TEST_DrawLevelField(ax, ay);
9334     return;
9335   }
9336
9337   if (IS_ANIMATED(graphic))
9338     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9339
9340   if (!MovDelay[ax][ay])        // start making new amoeba field
9341     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9342
9343   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9344   {
9345     MovDelay[ax][ay]--;
9346     if (MovDelay[ax][ay])
9347       return;
9348   }
9349
9350   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9351   {
9352     int start = RND(4);
9353     int x = ax + xy[start].x;
9354     int y = ay + xy[start].y;
9355
9356     if (!IN_LEV_FIELD(x, y))
9357       return;
9358
9359     if (IS_FREE(x, y) ||
9360         CAN_GROW_INTO(Tile[x][y]) ||
9361         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9362         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9363     {
9364       newax = x;
9365       neway = y;
9366     }
9367
9368     if (newax == ax && neway == ay)
9369       return;
9370   }
9371   else                          // normal or "filled" (BD style) amoeba
9372   {
9373     int start = RND(4);
9374     boolean waiting_for_player = FALSE;
9375
9376     for (i = 0; i < NUM_DIRECTIONS; i++)
9377     {
9378       int j = (start + i) % 4;
9379       int x = ax + xy[j].x;
9380       int y = ay + xy[j].y;
9381
9382       if (!IN_LEV_FIELD(x, y))
9383         continue;
9384
9385       if (IS_FREE(x, y) ||
9386           CAN_GROW_INTO(Tile[x][y]) ||
9387           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9388           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9389       {
9390         newax = x;
9391         neway = y;
9392         break;
9393       }
9394       else if (IS_PLAYER(x, y))
9395         waiting_for_player = TRUE;
9396     }
9397
9398     if (newax == ax && neway == ay)             // amoeba cannot grow
9399     {
9400       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9401       {
9402         Tile[ax][ay] = EL_AMOEBA_DEAD;
9403         TEST_DrawLevelField(ax, ay);
9404         AmoebaCnt[AmoebaNr[ax][ay]]--;
9405
9406         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9407         {
9408           if (element == EL_AMOEBA_FULL)
9409             AmoebaToDiamond(ax, ay);
9410           else if (element == EL_BD_AMOEBA)
9411             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9412         }
9413       }
9414       return;
9415     }
9416     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9417     {
9418       // amoeba gets larger by growing in some direction
9419
9420       int new_group_nr = AmoebaNr[ax][ay];
9421
9422 #ifdef DEBUG
9423   if (new_group_nr == 0)
9424   {
9425     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9426           newax, neway);
9427     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9428
9429     return;
9430   }
9431 #endif
9432
9433       AmoebaNr[newax][neway] = new_group_nr;
9434       AmoebaCnt[new_group_nr]++;
9435       AmoebaCnt2[new_group_nr]++;
9436
9437       // if amoeba touches other amoeba(s) after growing, unify them
9438       AmoebaMerge(newax, neway);
9439
9440       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9441       {
9442         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9443         return;
9444       }
9445     }
9446   }
9447
9448   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9449       (neway == lev_fieldy - 1 && newax != ax))
9450   {
9451     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9452     Store[newax][neway] = element;
9453   }
9454   else if (neway == ay || element == EL_EMC_DRIPPER)
9455   {
9456     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9457
9458     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9459   }
9460   else
9461   {
9462     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9463     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9464     Store[ax][ay] = EL_AMOEBA_DROP;
9465     ContinueMoving(ax, ay);
9466     return;
9467   }
9468
9469   TEST_DrawLevelField(newax, neway);
9470 }
9471
9472 static void Life(int ax, int ay)
9473 {
9474   int x1, y1, x2, y2;
9475   int life_time = 40;
9476   int element = Tile[ax][ay];
9477   int graphic = el2img(element);
9478   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9479                          level.biomaze);
9480   boolean changed = FALSE;
9481
9482   if (IS_ANIMATED(graphic))
9483     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9484
9485   if (Stop[ax][ay])
9486     return;
9487
9488   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9489     MovDelay[ax][ay] = life_time;
9490
9491   if (MovDelay[ax][ay])         // wait some time before next cycle
9492   {
9493     MovDelay[ax][ay]--;
9494     if (MovDelay[ax][ay])
9495       return;
9496   }
9497
9498   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9499   {
9500     int xx = ax + x1, yy = ay + y1;
9501     int old_element = Tile[xx][yy];
9502     int num_neighbours = 0;
9503
9504     if (!IN_LEV_FIELD(xx, yy))
9505       continue;
9506
9507     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9508     {
9509       int x = xx + x2, y = yy + y2;
9510
9511       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9512         continue;
9513
9514       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9515       boolean is_neighbour = FALSE;
9516
9517       if (level.use_life_bugs)
9518         is_neighbour =
9519           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9520            (IS_FREE(x, y)                             &&  Stop[x][y]));
9521       else
9522         is_neighbour =
9523           (Last[x][y] == element || is_player_cell);
9524
9525       if (is_neighbour)
9526         num_neighbours++;
9527     }
9528
9529     boolean is_free = FALSE;
9530
9531     if (level.use_life_bugs)
9532       is_free = (IS_FREE(xx, yy));
9533     else
9534       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9535
9536     if (xx == ax && yy == ay)           // field in the middle
9537     {
9538       if (num_neighbours < life_parameter[0] ||
9539           num_neighbours > life_parameter[1])
9540       {
9541         Tile[xx][yy] = EL_EMPTY;
9542         if (Tile[xx][yy] != old_element)
9543           TEST_DrawLevelField(xx, yy);
9544         Stop[xx][yy] = TRUE;
9545         changed = TRUE;
9546       }
9547     }
9548     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9549     {                                   // free border field
9550       if (num_neighbours >= life_parameter[2] &&
9551           num_neighbours <= life_parameter[3])
9552       {
9553         Tile[xx][yy] = element;
9554         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9555         if (Tile[xx][yy] != old_element)
9556           TEST_DrawLevelField(xx, yy);
9557         Stop[xx][yy] = TRUE;
9558         changed = TRUE;
9559       }
9560     }
9561   }
9562
9563   if (changed)
9564     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9565                    SND_GAME_OF_LIFE_GROWING);
9566 }
9567
9568 static void InitRobotWheel(int x, int y)
9569 {
9570   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9571 }
9572
9573 static void RunRobotWheel(int x, int y)
9574 {
9575   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9576 }
9577
9578 static void StopRobotWheel(int x, int y)
9579 {
9580   if (game.robot_wheel_x == x &&
9581       game.robot_wheel_y == y)
9582   {
9583     game.robot_wheel_x = -1;
9584     game.robot_wheel_y = -1;
9585     game.robot_wheel_active = FALSE;
9586   }
9587 }
9588
9589 static void InitTimegateWheel(int x, int y)
9590 {
9591   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9592 }
9593
9594 static void RunTimegateWheel(int x, int y)
9595 {
9596   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9597 }
9598
9599 static void InitMagicBallDelay(int x, int y)
9600 {
9601   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9602 }
9603
9604 static void ActivateMagicBall(int bx, int by)
9605 {
9606   int x, y;
9607
9608   if (level.ball_random)
9609   {
9610     int pos_border = RND(8);    // select one of the eight border elements
9611     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9612     int xx = pos_content % 3;
9613     int yy = pos_content / 3;
9614
9615     x = bx - 1 + xx;
9616     y = by - 1 + yy;
9617
9618     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9619       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9620   }
9621   else
9622   {
9623     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9624     {
9625       int xx = x - bx + 1;
9626       int yy = y - by + 1;
9627
9628       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9629         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9630     }
9631   }
9632
9633   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9634 }
9635
9636 static void CheckExit(int x, int y)
9637 {
9638   if (game.gems_still_needed > 0 ||
9639       game.sokoban_fields_still_needed > 0 ||
9640       game.sokoban_objects_still_needed > 0 ||
9641       game.lights_still_needed > 0)
9642   {
9643     int element = Tile[x][y];
9644     int graphic = el2img(element);
9645
9646     if (IS_ANIMATED(graphic))
9647       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9648
9649     return;
9650   }
9651
9652   // do not re-open exit door closed after last player
9653   if (game.all_players_gone)
9654     return;
9655
9656   Tile[x][y] = EL_EXIT_OPENING;
9657
9658   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9659 }
9660
9661 static void CheckExitEM(int x, int y)
9662 {
9663   if (game.gems_still_needed > 0 ||
9664       game.sokoban_fields_still_needed > 0 ||
9665       game.sokoban_objects_still_needed > 0 ||
9666       game.lights_still_needed > 0)
9667   {
9668     int element = Tile[x][y];
9669     int graphic = el2img(element);
9670
9671     if (IS_ANIMATED(graphic))
9672       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9673
9674     return;
9675   }
9676
9677   // do not re-open exit door closed after last player
9678   if (game.all_players_gone)
9679     return;
9680
9681   Tile[x][y] = EL_EM_EXIT_OPENING;
9682
9683   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9684 }
9685
9686 static void CheckExitSteel(int x, int y)
9687 {
9688   if (game.gems_still_needed > 0 ||
9689       game.sokoban_fields_still_needed > 0 ||
9690       game.sokoban_objects_still_needed > 0 ||
9691       game.lights_still_needed > 0)
9692   {
9693     int element = Tile[x][y];
9694     int graphic = el2img(element);
9695
9696     if (IS_ANIMATED(graphic))
9697       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9698
9699     return;
9700   }
9701
9702   // do not re-open exit door closed after last player
9703   if (game.all_players_gone)
9704     return;
9705
9706   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9707
9708   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9709 }
9710
9711 static void CheckExitSteelEM(int x, int y)
9712 {
9713   if (game.gems_still_needed > 0 ||
9714       game.sokoban_fields_still_needed > 0 ||
9715       game.sokoban_objects_still_needed > 0 ||
9716       game.lights_still_needed > 0)
9717   {
9718     int element = Tile[x][y];
9719     int graphic = el2img(element);
9720
9721     if (IS_ANIMATED(graphic))
9722       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9723
9724     return;
9725   }
9726
9727   // do not re-open exit door closed after last player
9728   if (game.all_players_gone)
9729     return;
9730
9731   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9732
9733   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9734 }
9735
9736 static void CheckExitSP(int x, int y)
9737 {
9738   if (game.gems_still_needed > 0)
9739   {
9740     int element = Tile[x][y];
9741     int graphic = el2img(element);
9742
9743     if (IS_ANIMATED(graphic))
9744       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9745
9746     return;
9747   }
9748
9749   // do not re-open exit door closed after last player
9750   if (game.all_players_gone)
9751     return;
9752
9753   Tile[x][y] = EL_SP_EXIT_OPENING;
9754
9755   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9756 }
9757
9758 static void CloseAllOpenTimegates(void)
9759 {
9760   int x, y;
9761
9762   SCAN_PLAYFIELD(x, y)
9763   {
9764     int element = Tile[x][y];
9765
9766     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9767     {
9768       Tile[x][y] = EL_TIMEGATE_CLOSING;
9769
9770       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9771     }
9772   }
9773 }
9774
9775 static void DrawTwinkleOnField(int x, int y)
9776 {
9777   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9778     return;
9779
9780   if (Tile[x][y] == EL_BD_DIAMOND)
9781     return;
9782
9783   if (MovDelay[x][y] == 0)      // next animation frame
9784     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9785
9786   if (MovDelay[x][y] != 0)      // wait some time before next frame
9787   {
9788     MovDelay[x][y]--;
9789
9790     DrawLevelElementAnimation(x, y, Tile[x][y]);
9791
9792     if (MovDelay[x][y] != 0)
9793     {
9794       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9795                                            10 - MovDelay[x][y]);
9796
9797       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9798     }
9799   }
9800 }
9801
9802 static void WallGrowing(int x, int y)
9803 {
9804   int delay = 6;
9805
9806   if (!MovDelay[x][y])          // next animation frame
9807     MovDelay[x][y] = 3 * delay;
9808
9809   if (MovDelay[x][y])           // wait some time before next frame
9810   {
9811     MovDelay[x][y]--;
9812
9813     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9814     {
9815       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9816       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9817
9818       DrawLevelGraphic(x, y, graphic, frame);
9819     }
9820
9821     if (!MovDelay[x][y])
9822     {
9823       if (MovDir[x][y] == MV_LEFT)
9824       {
9825         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9826           TEST_DrawLevelField(x - 1, y);
9827       }
9828       else if (MovDir[x][y] == MV_RIGHT)
9829       {
9830         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9831           TEST_DrawLevelField(x + 1, y);
9832       }
9833       else if (MovDir[x][y] == MV_UP)
9834       {
9835         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9836           TEST_DrawLevelField(x, y - 1);
9837       }
9838       else
9839       {
9840         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9841           TEST_DrawLevelField(x, y + 1);
9842       }
9843
9844       Tile[x][y] = Store[x][y];
9845       Store[x][y] = 0;
9846       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9847       TEST_DrawLevelField(x, y);
9848     }
9849   }
9850 }
9851
9852 static void CheckWallGrowing(int ax, int ay)
9853 {
9854   int element = Tile[ax][ay];
9855   int graphic = el2img(element);
9856   boolean free_top    = FALSE;
9857   boolean free_bottom = FALSE;
9858   boolean free_left   = FALSE;
9859   boolean free_right  = FALSE;
9860   boolean stop_top    = FALSE;
9861   boolean stop_bottom = FALSE;
9862   boolean stop_left   = FALSE;
9863   boolean stop_right  = FALSE;
9864   boolean new_wall    = FALSE;
9865
9866   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9867                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9868                            element == EL_EXPANDABLE_STEELWALL_ANY);
9869
9870   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9871                              element == EL_EXPANDABLE_WALL_ANY ||
9872                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9873                              element == EL_EXPANDABLE_STEELWALL_ANY);
9874
9875   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9876                              element == EL_EXPANDABLE_WALL_ANY ||
9877                              element == EL_EXPANDABLE_WALL ||
9878                              element == EL_BD_EXPANDABLE_WALL ||
9879                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9880                              element == EL_EXPANDABLE_STEELWALL_ANY);
9881
9882   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9883                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9884
9885   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9886                              element == EL_EXPANDABLE_WALL ||
9887                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9888
9889   int wall_growing = (is_steelwall ?
9890                       EL_EXPANDABLE_STEELWALL_GROWING :
9891                       EL_EXPANDABLE_WALL_GROWING);
9892
9893   int gfx_wall_growing_up    = (is_steelwall ?
9894                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9895                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9896   int gfx_wall_growing_down  = (is_steelwall ?
9897                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9898                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9899   int gfx_wall_growing_left  = (is_steelwall ?
9900                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9901                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9902   int gfx_wall_growing_right = (is_steelwall ?
9903                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9904                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9905
9906   if (IS_ANIMATED(graphic))
9907     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9908
9909   if (!MovDelay[ax][ay])        // start building new wall
9910     MovDelay[ax][ay] = 6;
9911
9912   if (MovDelay[ax][ay])         // wait some time before building new wall
9913   {
9914     MovDelay[ax][ay]--;
9915     if (MovDelay[ax][ay])
9916       return;
9917   }
9918
9919   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9920     free_top = TRUE;
9921   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9922     free_bottom = TRUE;
9923   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9924     free_left = TRUE;
9925   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9926     free_right = TRUE;
9927
9928   if (grow_vertical)
9929   {
9930     if (free_top)
9931     {
9932       Tile[ax][ay - 1] = wall_growing;
9933       Store[ax][ay - 1] = element;
9934       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9935
9936       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9937         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9938
9939       new_wall = TRUE;
9940     }
9941
9942     if (free_bottom)
9943     {
9944       Tile[ax][ay + 1] = wall_growing;
9945       Store[ax][ay + 1] = element;
9946       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9947
9948       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9949         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9950
9951       new_wall = TRUE;
9952     }
9953   }
9954
9955   if (grow_horizontal)
9956   {
9957     if (free_left)
9958     {
9959       Tile[ax - 1][ay] = wall_growing;
9960       Store[ax - 1][ay] = element;
9961       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9962
9963       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9964         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9965
9966       new_wall = TRUE;
9967     }
9968
9969     if (free_right)
9970     {
9971       Tile[ax + 1][ay] = wall_growing;
9972       Store[ax + 1][ay] = element;
9973       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9974
9975       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9976         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9977
9978       new_wall = TRUE;
9979     }
9980   }
9981
9982   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9983     TEST_DrawLevelField(ax, ay);
9984
9985   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9986     stop_top = TRUE;
9987   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9988     stop_bottom = TRUE;
9989   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9990     stop_left = TRUE;
9991   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9992     stop_right = TRUE;
9993
9994   if (((stop_top && stop_bottom) || stop_horizontal) &&
9995       ((stop_left && stop_right) || stop_vertical))
9996     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9997
9998   if (new_wall)
9999     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10000 }
10001
10002 static void CheckForDragon(int x, int y)
10003 {
10004   int i, j;
10005   boolean dragon_found = FALSE;
10006   struct XY *xy = xy_topdown;
10007
10008   for (i = 0; i < NUM_DIRECTIONS; i++)
10009   {
10010     for (j = 0; j < 4; j++)
10011     {
10012       int xx = x + j * xy[i].x;
10013       int yy = y + j * xy[i].y;
10014
10015       if (IN_LEV_FIELD(xx, yy) &&
10016           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10017       {
10018         if (Tile[xx][yy] == EL_DRAGON)
10019           dragon_found = TRUE;
10020       }
10021       else
10022         break;
10023     }
10024   }
10025
10026   if (!dragon_found)
10027   {
10028     for (i = 0; i < NUM_DIRECTIONS; i++)
10029     {
10030       for (j = 0; j < 3; j++)
10031       {
10032         int xx = x + j * xy[i].x;
10033         int yy = y + j * xy[i].y;
10034
10035         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10036         {
10037           Tile[xx][yy] = EL_EMPTY;
10038           TEST_DrawLevelField(xx, yy);
10039         }
10040         else
10041           break;
10042       }
10043     }
10044   }
10045 }
10046
10047 static void InitBuggyBase(int x, int y)
10048 {
10049   int element = Tile[x][y];
10050   int activating_delay = FRAMES_PER_SECOND / 4;
10051
10052   ChangeDelay[x][y] =
10053     (element == EL_SP_BUGGY_BASE ?
10054      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10055      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10056      activating_delay :
10057      element == EL_SP_BUGGY_BASE_ACTIVE ?
10058      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10059 }
10060
10061 static void WarnBuggyBase(int x, int y)
10062 {
10063   int i;
10064   struct XY *xy = xy_topdown;
10065
10066   for (i = 0; i < NUM_DIRECTIONS; i++)
10067   {
10068     int xx = x + xy[i].x;
10069     int yy = y + xy[i].y;
10070
10071     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10072     {
10073       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10074
10075       break;
10076     }
10077   }
10078 }
10079
10080 static void InitTrap(int x, int y)
10081 {
10082   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10083 }
10084
10085 static void ActivateTrap(int x, int y)
10086 {
10087   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10088 }
10089
10090 static void ChangeActiveTrap(int x, int y)
10091 {
10092   int graphic = IMG_TRAP_ACTIVE;
10093
10094   // if new animation frame was drawn, correct crumbled sand border
10095   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10096     TEST_DrawLevelFieldCrumbled(x, y);
10097 }
10098
10099 static int getSpecialActionElement(int element, int number, int base_element)
10100 {
10101   return (element != EL_EMPTY ? element :
10102           number != -1 ? base_element + number - 1 :
10103           EL_EMPTY);
10104 }
10105
10106 static int getModifiedActionNumber(int value_old, int operator, int operand,
10107                                    int value_min, int value_max)
10108 {
10109   int value_new = (operator == CA_MODE_SET      ? operand :
10110                    operator == CA_MODE_ADD      ? value_old + operand :
10111                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10112                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10113                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10114                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10115                    value_old);
10116
10117   return (value_new < value_min ? value_min :
10118           value_new > value_max ? value_max :
10119           value_new);
10120 }
10121
10122 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10123 {
10124   struct ElementInfo *ei = &element_info[element];
10125   struct ElementChangeInfo *change = &ei->change_page[page];
10126   int target_element = change->target_element;
10127   int action_type = change->action_type;
10128   int action_mode = change->action_mode;
10129   int action_arg = change->action_arg;
10130   int action_element = change->action_element;
10131   int i;
10132
10133   if (!change->has_action)
10134     return;
10135
10136   // ---------- determine action paramater values -----------------------------
10137
10138   int level_time_value =
10139     (level.time > 0 ? TimeLeft :
10140      TimePlayed);
10141
10142   int action_arg_element_raw =
10143     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10144      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10145      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10146      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10147      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10148      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10149      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10150      EL_EMPTY);
10151   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10152
10153   int action_arg_direction =
10154     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10155      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10156      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10157      change->actual_trigger_side :
10158      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10159      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10160      MV_NONE);
10161
10162   int action_arg_number_min =
10163     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10164      CA_ARG_MIN);
10165
10166   int action_arg_number_max =
10167     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10168      action_type == CA_SET_LEVEL_GEMS ? 999 :
10169      action_type == CA_SET_LEVEL_TIME ? 9999 :
10170      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10171      action_type == CA_SET_CE_VALUE ? 9999 :
10172      action_type == CA_SET_CE_SCORE ? 9999 :
10173      CA_ARG_MAX);
10174
10175   int action_arg_number_reset =
10176     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10177      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10178      action_type == CA_SET_LEVEL_TIME ? level.time :
10179      action_type == CA_SET_LEVEL_SCORE ? 0 :
10180      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10181      action_type == CA_SET_CE_SCORE ? 0 :
10182      0);
10183
10184   int action_arg_number =
10185     (action_arg <= CA_ARG_MAX ? action_arg :
10186      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10187      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10188      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10189      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10190      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10191      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10192      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10193      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10194      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10195      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10196      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10197      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10198      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10199      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10200      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10201      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10202      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10203      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10204      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10205      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10206      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10207      -1);
10208
10209   int action_arg_number_old =
10210     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10211      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10212      action_type == CA_SET_LEVEL_SCORE ? game.score :
10213      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10214      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10215      0);
10216
10217   int action_arg_number_new =
10218     getModifiedActionNumber(action_arg_number_old,
10219                             action_mode, action_arg_number,
10220                             action_arg_number_min, action_arg_number_max);
10221
10222   int trigger_player_bits =
10223     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10224      change->actual_trigger_player_bits : change->trigger_player);
10225
10226   int action_arg_player_bits =
10227     (action_arg >= CA_ARG_PLAYER_1 &&
10228      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10229      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10230      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10231      PLAYER_BITS_ANY);
10232
10233   // ---------- execute action  -----------------------------------------------
10234
10235   switch (action_type)
10236   {
10237     case CA_NO_ACTION:
10238     {
10239       return;
10240     }
10241
10242     // ---------- level actions  ----------------------------------------------
10243
10244     case CA_RESTART_LEVEL:
10245     {
10246       game.restart_level = TRUE;
10247
10248       break;
10249     }
10250
10251     case CA_SHOW_ENVELOPE:
10252     {
10253       int element = getSpecialActionElement(action_arg_element,
10254                                             action_arg_number, EL_ENVELOPE_1);
10255
10256       if (IS_ENVELOPE(element))
10257         local_player->show_envelope = element;
10258
10259       break;
10260     }
10261
10262     case CA_SET_LEVEL_TIME:
10263     {
10264       if (level.time > 0)       // only modify limited time value
10265       {
10266         TimeLeft = action_arg_number_new;
10267
10268         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10269
10270         DisplayGameControlValues();
10271
10272         if (!TimeLeft && game.time_limit)
10273           for (i = 0; i < MAX_PLAYERS; i++)
10274             KillPlayer(&stored_player[i]);
10275       }
10276
10277       break;
10278     }
10279
10280     case CA_SET_LEVEL_SCORE:
10281     {
10282       game.score = action_arg_number_new;
10283
10284       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10285
10286       DisplayGameControlValues();
10287
10288       break;
10289     }
10290
10291     case CA_SET_LEVEL_GEMS:
10292     {
10293       game.gems_still_needed = action_arg_number_new;
10294
10295       game.snapshot.collected_item = TRUE;
10296
10297       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10298
10299       DisplayGameControlValues();
10300
10301       break;
10302     }
10303
10304     case CA_SET_LEVEL_WIND:
10305     {
10306       game.wind_direction = action_arg_direction;
10307
10308       break;
10309     }
10310
10311     case CA_SET_LEVEL_RANDOM_SEED:
10312     {
10313       // ensure that setting a new random seed while playing is predictable
10314       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10315
10316       break;
10317     }
10318
10319     // ---------- player actions  ---------------------------------------------
10320
10321     case CA_MOVE_PLAYER:
10322     case CA_MOVE_PLAYER_NEW:
10323     {
10324       // automatically move to the next field in specified direction
10325       for (i = 0; i < MAX_PLAYERS; i++)
10326         if (trigger_player_bits & (1 << i))
10327           if (action_type == CA_MOVE_PLAYER ||
10328               stored_player[i].MovPos == 0)
10329             stored_player[i].programmed_action = action_arg_direction;
10330
10331       break;
10332     }
10333
10334     case CA_EXIT_PLAYER:
10335     {
10336       for (i = 0; i < MAX_PLAYERS; i++)
10337         if (action_arg_player_bits & (1 << i))
10338           ExitPlayer(&stored_player[i]);
10339
10340       if (game.players_still_needed == 0)
10341         LevelSolved();
10342
10343       break;
10344     }
10345
10346     case CA_KILL_PLAYER:
10347     {
10348       for (i = 0; i < MAX_PLAYERS; i++)
10349         if (action_arg_player_bits & (1 << i))
10350           KillPlayer(&stored_player[i]);
10351
10352       break;
10353     }
10354
10355     case CA_SET_PLAYER_KEYS:
10356     {
10357       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10358       int element = getSpecialActionElement(action_arg_element,
10359                                             action_arg_number, EL_KEY_1);
10360
10361       if (IS_KEY(element))
10362       {
10363         for (i = 0; i < MAX_PLAYERS; i++)
10364         {
10365           if (trigger_player_bits & (1 << i))
10366           {
10367             stored_player[i].key[KEY_NR(element)] = key_state;
10368
10369             DrawGameDoorValues();
10370           }
10371         }
10372       }
10373
10374       break;
10375     }
10376
10377     case CA_SET_PLAYER_SPEED:
10378     {
10379       for (i = 0; i < MAX_PLAYERS; i++)
10380       {
10381         if (trigger_player_bits & (1 << i))
10382         {
10383           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10384
10385           if (action_arg == CA_ARG_SPEED_FASTER &&
10386               stored_player[i].cannot_move)
10387           {
10388             action_arg_number = STEPSIZE_VERY_SLOW;
10389           }
10390           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10391                    action_arg == CA_ARG_SPEED_FASTER)
10392           {
10393             action_arg_number = 2;
10394             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10395                            CA_MODE_MULTIPLY);
10396           }
10397           else if (action_arg == CA_ARG_NUMBER_RESET)
10398           {
10399             action_arg_number = level.initial_player_stepsize[i];
10400           }
10401
10402           move_stepsize =
10403             getModifiedActionNumber(move_stepsize,
10404                                     action_mode,
10405                                     action_arg_number,
10406                                     action_arg_number_min,
10407                                     action_arg_number_max);
10408
10409           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10410         }
10411       }
10412
10413       break;
10414     }
10415
10416     case CA_SET_PLAYER_SHIELD:
10417     {
10418       for (i = 0; i < MAX_PLAYERS; i++)
10419       {
10420         if (trigger_player_bits & (1 << i))
10421         {
10422           if (action_arg == CA_ARG_SHIELD_OFF)
10423           {
10424             stored_player[i].shield_normal_time_left = 0;
10425             stored_player[i].shield_deadly_time_left = 0;
10426           }
10427           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10428           {
10429             stored_player[i].shield_normal_time_left = 999999;
10430           }
10431           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10432           {
10433             stored_player[i].shield_normal_time_left = 999999;
10434             stored_player[i].shield_deadly_time_left = 999999;
10435           }
10436         }
10437       }
10438
10439       break;
10440     }
10441
10442     case CA_SET_PLAYER_GRAVITY:
10443     {
10444       for (i = 0; i < MAX_PLAYERS; i++)
10445       {
10446         if (trigger_player_bits & (1 << i))
10447         {
10448           stored_player[i].gravity =
10449             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10450              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10451              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10452              stored_player[i].gravity);
10453         }
10454       }
10455
10456       break;
10457     }
10458
10459     case CA_SET_PLAYER_ARTWORK:
10460     {
10461       for (i = 0; i < MAX_PLAYERS; i++)
10462       {
10463         if (trigger_player_bits & (1 << i))
10464         {
10465           int artwork_element = action_arg_element;
10466
10467           if (action_arg == CA_ARG_ELEMENT_RESET)
10468             artwork_element =
10469               (level.use_artwork_element[i] ? level.artwork_element[i] :
10470                stored_player[i].element_nr);
10471
10472           if (stored_player[i].artwork_element != artwork_element)
10473             stored_player[i].Frame = 0;
10474
10475           stored_player[i].artwork_element = artwork_element;
10476
10477           SetPlayerWaiting(&stored_player[i], FALSE);
10478
10479           // set number of special actions for bored and sleeping animation
10480           stored_player[i].num_special_action_bored =
10481             get_num_special_action(artwork_element,
10482                                    ACTION_BORING_1, ACTION_BORING_LAST);
10483           stored_player[i].num_special_action_sleeping =
10484             get_num_special_action(artwork_element,
10485                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10486         }
10487       }
10488
10489       break;
10490     }
10491
10492     case CA_SET_PLAYER_INVENTORY:
10493     {
10494       for (i = 0; i < MAX_PLAYERS; i++)
10495       {
10496         struct PlayerInfo *player = &stored_player[i];
10497         int j, k;
10498
10499         if (trigger_player_bits & (1 << i))
10500         {
10501           int inventory_element = action_arg_element;
10502
10503           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10504               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10505               action_arg == CA_ARG_ELEMENT_ACTION)
10506           {
10507             int element = inventory_element;
10508             int collect_count = element_info[element].collect_count_initial;
10509
10510             if (!IS_CUSTOM_ELEMENT(element))
10511               collect_count = 1;
10512
10513             if (collect_count == 0)
10514               player->inventory_infinite_element = element;
10515             else
10516               for (k = 0; k < collect_count; k++)
10517                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10518                   player->inventory_element[player->inventory_size++] =
10519                     element;
10520           }
10521           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10522                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10523                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10524           {
10525             if (player->inventory_infinite_element != EL_UNDEFINED &&
10526                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10527                                      action_arg_element_raw))
10528               player->inventory_infinite_element = EL_UNDEFINED;
10529
10530             for (k = 0, j = 0; j < player->inventory_size; j++)
10531             {
10532               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10533                                         action_arg_element_raw))
10534                 player->inventory_element[k++] = player->inventory_element[j];
10535             }
10536
10537             player->inventory_size = k;
10538           }
10539           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10540           {
10541             if (player->inventory_size > 0)
10542             {
10543               for (j = 0; j < player->inventory_size - 1; j++)
10544                 player->inventory_element[j] = player->inventory_element[j + 1];
10545
10546               player->inventory_size--;
10547             }
10548           }
10549           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10550           {
10551             if (player->inventory_size > 0)
10552               player->inventory_size--;
10553           }
10554           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10555           {
10556             player->inventory_infinite_element = EL_UNDEFINED;
10557             player->inventory_size = 0;
10558           }
10559           else if (action_arg == CA_ARG_INVENTORY_RESET)
10560           {
10561             player->inventory_infinite_element = EL_UNDEFINED;
10562             player->inventory_size = 0;
10563
10564             if (level.use_initial_inventory[i])
10565             {
10566               for (j = 0; j < level.initial_inventory_size[i]; j++)
10567               {
10568                 int element = level.initial_inventory_content[i][j];
10569                 int collect_count = element_info[element].collect_count_initial;
10570
10571                 if (!IS_CUSTOM_ELEMENT(element))
10572                   collect_count = 1;
10573
10574                 if (collect_count == 0)
10575                   player->inventory_infinite_element = element;
10576                 else
10577                   for (k = 0; k < collect_count; k++)
10578                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10579                       player->inventory_element[player->inventory_size++] =
10580                         element;
10581               }
10582             }
10583           }
10584         }
10585       }
10586
10587       break;
10588     }
10589
10590     // ---------- CE actions  -------------------------------------------------
10591
10592     case CA_SET_CE_VALUE:
10593     {
10594       int last_ce_value = CustomValue[x][y];
10595
10596       CustomValue[x][y] = action_arg_number_new;
10597
10598       if (CustomValue[x][y] != last_ce_value)
10599       {
10600         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10601         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10602
10603         if (CustomValue[x][y] == 0)
10604         {
10605           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10606           ChangeCount[x][y] = 0;        // allow at least one more change
10607
10608           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10609           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10610         }
10611       }
10612
10613       break;
10614     }
10615
10616     case CA_SET_CE_SCORE:
10617     {
10618       int last_ce_score = ei->collect_score;
10619
10620       ei->collect_score = action_arg_number_new;
10621
10622       if (ei->collect_score != last_ce_score)
10623       {
10624         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10625         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10626
10627         if (ei->collect_score == 0)
10628         {
10629           int xx, yy;
10630
10631           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10632           ChangeCount[x][y] = 0;        // allow at least one more change
10633
10634           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10635           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10636
10637           /*
10638             This is a very special case that seems to be a mixture between
10639             CheckElementChange() and CheckTriggeredElementChange(): while
10640             the first one only affects single elements that are triggered
10641             directly, the second one affects multiple elements in the playfield
10642             that are triggered indirectly by another element. This is a third
10643             case: Changing the CE score always affects multiple identical CEs,
10644             so every affected CE must be checked, not only the single CE for
10645             which the CE score was changed in the first place (as every instance
10646             of that CE shares the same CE score, and therefore also can change)!
10647           */
10648           SCAN_PLAYFIELD(xx, yy)
10649           {
10650             if (Tile[xx][yy] == element)
10651               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10652                                  CE_SCORE_GETS_ZERO);
10653           }
10654         }
10655       }
10656
10657       break;
10658     }
10659
10660     case CA_SET_CE_ARTWORK:
10661     {
10662       int artwork_element = action_arg_element;
10663       boolean reset_frame = FALSE;
10664       int xx, yy;
10665
10666       if (action_arg == CA_ARG_ELEMENT_RESET)
10667         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10668                            element);
10669
10670       if (ei->gfx_element != artwork_element)
10671         reset_frame = TRUE;
10672
10673       ei->gfx_element = artwork_element;
10674
10675       SCAN_PLAYFIELD(xx, yy)
10676       {
10677         if (Tile[xx][yy] == element)
10678         {
10679           if (reset_frame)
10680           {
10681             ResetGfxAnimation(xx, yy);
10682             ResetRandomAnimationValue(xx, yy);
10683           }
10684
10685           TEST_DrawLevelField(xx, yy);
10686         }
10687       }
10688
10689       break;
10690     }
10691
10692     // ---------- engine actions  ---------------------------------------------
10693
10694     case CA_SET_ENGINE_SCAN_MODE:
10695     {
10696       InitPlayfieldScanMode(action_arg);
10697
10698       break;
10699     }
10700
10701     default:
10702       break;
10703   }
10704 }
10705
10706 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10707 {
10708   int old_element = Tile[x][y];
10709   int new_element = GetElementFromGroupElement(element);
10710   int previous_move_direction = MovDir[x][y];
10711   int last_ce_value = CustomValue[x][y];
10712   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10713   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10714   boolean add_player_onto_element = (new_element_is_player &&
10715                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10716                                      IS_WALKABLE(old_element));
10717
10718   if (!add_player_onto_element)
10719   {
10720     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10721       RemoveMovingField(x, y);
10722     else
10723       RemoveField(x, y);
10724
10725     Tile[x][y] = new_element;
10726
10727     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10728       MovDir[x][y] = previous_move_direction;
10729
10730     if (element_info[new_element].use_last_ce_value)
10731       CustomValue[x][y] = last_ce_value;
10732
10733     InitField_WithBug1(x, y, FALSE);
10734
10735     new_element = Tile[x][y];   // element may have changed
10736
10737     ResetGfxAnimation(x, y);
10738     ResetRandomAnimationValue(x, y);
10739
10740     TEST_DrawLevelField(x, y);
10741
10742     if (GFX_CRUMBLED(new_element))
10743       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10744
10745     if (old_element == EL_EXPLOSION)
10746     {
10747       Store[x][y] = Store2[x][y] = 0;
10748
10749       // check if new element replaces an exploding player, requiring cleanup
10750       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10751         StorePlayer[x][y] = 0;
10752     }
10753
10754     // check if element under the player changes from accessible to unaccessible
10755     // (needed for special case of dropping element which then changes)
10756     // (must be checked after creating new element for walkable group elements)
10757     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10758         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10759     {
10760       KillPlayer(PLAYERINFO(x, y));
10761
10762       return;
10763     }
10764   }
10765
10766   // "ChangeCount" not set yet to allow "entered by player" change one time
10767   if (new_element_is_player)
10768     RelocatePlayer(x, y, new_element);
10769
10770   if (is_change)
10771     ChangeCount[x][y]++;        // count number of changes in the same frame
10772
10773   TestIfBadThingTouchesPlayer(x, y);
10774   TestIfPlayerTouchesCustomElement(x, y);
10775   TestIfElementTouchesCustomElement(x, y);
10776 }
10777
10778 static void CreateField(int x, int y, int element)
10779 {
10780   CreateFieldExt(x, y, element, FALSE);
10781 }
10782
10783 static void CreateElementFromChange(int x, int y, int element)
10784 {
10785   element = GET_VALID_RUNTIME_ELEMENT(element);
10786
10787   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10788   {
10789     int old_element = Tile[x][y];
10790
10791     // prevent changed element from moving in same engine frame
10792     // unless both old and new element can either fall or move
10793     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10794         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10795       Stop[x][y] = TRUE;
10796   }
10797
10798   CreateFieldExt(x, y, element, TRUE);
10799 }
10800
10801 static boolean ChangeElement(int x, int y, int element, int page)
10802 {
10803   struct ElementInfo *ei = &element_info[element];
10804   struct ElementChangeInfo *change = &ei->change_page[page];
10805   int ce_value = CustomValue[x][y];
10806   int ce_score = ei->collect_score;
10807   int target_element;
10808   int old_element = Tile[x][y];
10809
10810   // always use default change event to prevent running into a loop
10811   if (ChangeEvent[x][y] == -1)
10812     ChangeEvent[x][y] = CE_DELAY;
10813
10814   if (ChangeEvent[x][y] == CE_DELAY)
10815   {
10816     // reset actual trigger element, trigger player and action element
10817     change->actual_trigger_element = EL_EMPTY;
10818     change->actual_trigger_player = EL_EMPTY;
10819     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10820     change->actual_trigger_side = CH_SIDE_NONE;
10821     change->actual_trigger_ce_value = 0;
10822     change->actual_trigger_ce_score = 0;
10823     change->actual_trigger_x = -1;
10824     change->actual_trigger_y = -1;
10825   }
10826
10827   // do not change elements more than a specified maximum number of changes
10828   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10829     return FALSE;
10830
10831   ChangeCount[x][y]++;          // count number of changes in the same frame
10832
10833   if (ei->has_anim_event)
10834     HandleGlobalAnimEventByElementChange(element, page, x, y,
10835                                          change->actual_trigger_x,
10836                                          change->actual_trigger_y);
10837
10838   if (change->explode)
10839   {
10840     Bang(x, y);
10841
10842     return TRUE;
10843   }
10844
10845   if (change->use_target_content)
10846   {
10847     boolean complete_replace = TRUE;
10848     boolean can_replace[3][3];
10849     int xx, yy;
10850
10851     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10852     {
10853       boolean is_empty;
10854       boolean is_walkable;
10855       boolean is_diggable;
10856       boolean is_collectible;
10857       boolean is_removable;
10858       boolean is_destructible;
10859       int ex = x + xx - 1;
10860       int ey = y + yy - 1;
10861       int content_element = change->target_content.e[xx][yy];
10862       int e;
10863
10864       can_replace[xx][yy] = TRUE;
10865
10866       if (ex == x && ey == y)   // do not check changing element itself
10867         continue;
10868
10869       if (content_element == EL_EMPTY_SPACE)
10870       {
10871         can_replace[xx][yy] = FALSE;    // do not replace border with space
10872
10873         continue;
10874       }
10875
10876       if (!IN_LEV_FIELD(ex, ey))
10877       {
10878         can_replace[xx][yy] = FALSE;
10879         complete_replace = FALSE;
10880
10881         continue;
10882       }
10883
10884       e = Tile[ex][ey];
10885
10886       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10887         e = MovingOrBlocked2Element(ex, ey);
10888
10889       is_empty = (IS_FREE(ex, ey) ||
10890                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10891
10892       is_walkable     = (is_empty || IS_WALKABLE(e));
10893       is_diggable     = (is_empty || IS_DIGGABLE(e));
10894       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10895       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10896       is_removable    = (is_diggable || is_collectible);
10897
10898       can_replace[xx][yy] =
10899         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10900           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10901           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10902           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10903           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10904           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10905          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10906
10907       if (!can_replace[xx][yy])
10908         complete_replace = FALSE;
10909     }
10910
10911     if (!change->only_if_complete || complete_replace)
10912     {
10913       boolean something_has_changed = FALSE;
10914
10915       if (change->only_if_complete && change->use_random_replace &&
10916           RND(100) < change->random_percentage)
10917         return FALSE;
10918
10919       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10920       {
10921         int ex = x + xx - 1;
10922         int ey = y + yy - 1;
10923         int content_element;
10924
10925         if (can_replace[xx][yy] && (!change->use_random_replace ||
10926                                     RND(100) < change->random_percentage))
10927         {
10928           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10929             RemoveMovingField(ex, ey);
10930
10931           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10932
10933           content_element = change->target_content.e[xx][yy];
10934           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10935                                               ce_value, ce_score);
10936
10937           CreateElementFromChange(ex, ey, target_element);
10938
10939           something_has_changed = TRUE;
10940
10941           // for symmetry reasons, freeze newly created border elements
10942           if (ex != x || ey != y)
10943             Stop[ex][ey] = TRUE;        // no more moving in this frame
10944         }
10945       }
10946
10947       if (something_has_changed)
10948       {
10949         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10950         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10951       }
10952     }
10953   }
10954   else
10955   {
10956     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10957                                         ce_value, ce_score);
10958
10959     if (element == EL_DIAGONAL_GROWING ||
10960         element == EL_DIAGONAL_SHRINKING)
10961     {
10962       target_element = Store[x][y];
10963
10964       Store[x][y] = EL_EMPTY;
10965     }
10966
10967     // special case: element changes to player (and may be kept if walkable)
10968     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10969       CreateElementFromChange(x, y, EL_EMPTY);
10970
10971     CreateElementFromChange(x, y, target_element);
10972
10973     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10974     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10975   }
10976
10977   // this uses direct change before indirect change
10978   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10979
10980   return TRUE;
10981 }
10982
10983 static void HandleElementChange(int x, int y, int page)
10984 {
10985   int element = MovingOrBlocked2Element(x, y);
10986   struct ElementInfo *ei = &element_info[element];
10987   struct ElementChangeInfo *change = &ei->change_page[page];
10988   boolean handle_action_before_change = FALSE;
10989
10990 #ifdef DEBUG
10991   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10992       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10993   {
10994     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10995           x, y, element, element_info[element].token_name);
10996     Debug("game:playing:HandleElementChange", "This should never happen!");
10997   }
10998 #endif
10999
11000   // this can happen with classic bombs on walkable, changing elements
11001   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11002   {
11003     return;
11004   }
11005
11006   if (ChangeDelay[x][y] == 0)           // initialize element change
11007   {
11008     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11009
11010     if (change->can_change)
11011     {
11012       // !!! not clear why graphic animation should be reset at all here !!!
11013       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11014       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11015
11016       /*
11017         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11018
11019         When using an animation frame delay of 1 (this only happens with
11020         "sp_zonk.moving.left/right" in the classic graphics), the default
11021         (non-moving) animation shows wrong animation frames (while the
11022         moving animation, like "sp_zonk.moving.left/right", is correct,
11023         so this graphical bug never shows up with the classic graphics).
11024         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11025         be drawn instead of the correct frames 0,1,2,3. This is caused by
11026         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11027         an element change: First when the change delay ("ChangeDelay[][]")
11028         counter has reached zero after decrementing, then a second time in
11029         the next frame (after "GfxFrame[][]" was already incremented) when
11030         "ChangeDelay[][]" is reset to the initial delay value again.
11031
11032         This causes frame 0 to be drawn twice, while the last frame won't
11033         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11034
11035         As some animations may already be cleverly designed around this bug
11036         (at least the "Snake Bite" snake tail animation does this), it cannot
11037         simply be fixed here without breaking such existing animations.
11038         Unfortunately, it cannot easily be detected if a graphics set was
11039         designed "before" or "after" the bug was fixed. As a workaround,
11040         a new graphics set option "game.graphics_engine_version" was added
11041         to be able to specify the game's major release version for which the
11042         graphics set was designed, which can then be used to decide if the
11043         bugfix should be used (version 4 and above) or not (version 3 or
11044         below, or if no version was specified at all, as with old sets).
11045
11046         (The wrong/fixed animation frames can be tested with the test level set
11047         "test_gfxframe" and level "000", which contains a specially prepared
11048         custom element at level position (x/y) == (11/9) which uses the zonk
11049         animation mentioned above. Using "game.graphics_engine_version: 4"
11050         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11051         This can also be seen from the debug output for this test element.)
11052       */
11053
11054       // when a custom element is about to change (for example by change delay),
11055       // do not reset graphic animation when the custom element is moving
11056       if (game.graphics_engine_version < 4 &&
11057           !IS_MOVING(x, y))
11058       {
11059         ResetGfxAnimation(x, y);
11060         ResetRandomAnimationValue(x, y);
11061       }
11062
11063       if (change->pre_change_function)
11064         change->pre_change_function(x, y);
11065     }
11066   }
11067
11068   ChangeDelay[x][y]--;
11069
11070   if (ChangeDelay[x][y] != 0)           // continue element change
11071   {
11072     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11073
11074     // also needed if CE can not change, but has CE delay with CE action
11075     if (IS_ANIMATED(graphic))
11076       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11077
11078     if (change->can_change)
11079     {
11080       if (change->change_function)
11081         change->change_function(x, y);
11082     }
11083   }
11084   else                                  // finish element change
11085   {
11086     if (ChangePage[x][y] != -1)         // remember page from delayed change
11087     {
11088       page = ChangePage[x][y];
11089       ChangePage[x][y] = -1;
11090
11091       change = &ei->change_page[page];
11092     }
11093
11094     if (IS_MOVING(x, y))                // never change a running system ;-)
11095     {
11096       ChangeDelay[x][y] = 1;            // try change after next move step
11097       ChangePage[x][y] = page;          // remember page to use for change
11098
11099       return;
11100     }
11101
11102     // special case: set new level random seed before changing element
11103     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11104       handle_action_before_change = TRUE;
11105
11106     if (change->has_action && handle_action_before_change)
11107       ExecuteCustomElementAction(x, y, element, page);
11108
11109     if (change->can_change)
11110     {
11111       if (ChangeElement(x, y, element, page))
11112       {
11113         if (change->post_change_function)
11114           change->post_change_function(x, y);
11115       }
11116     }
11117
11118     if (change->has_action && !handle_action_before_change)
11119       ExecuteCustomElementAction(x, y, element, page);
11120   }
11121 }
11122
11123 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11124                                               int trigger_element,
11125                                               int trigger_event,
11126                                               int trigger_player,
11127                                               int trigger_side,
11128                                               int trigger_page)
11129 {
11130   boolean change_done_any = FALSE;
11131   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11132   int i;
11133
11134   if (!(trigger_events[trigger_element][trigger_event]))
11135     return FALSE;
11136
11137   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11138
11139   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11140   {
11141     int element = EL_CUSTOM_START + i;
11142     boolean change_done = FALSE;
11143     int p;
11144
11145     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11146         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11147       continue;
11148
11149     for (p = 0; p < element_info[element].num_change_pages; p++)
11150     {
11151       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11152
11153       if (change->can_change_or_has_action &&
11154           change->has_event[trigger_event] &&
11155           change->trigger_side & trigger_side &&
11156           change->trigger_player & trigger_player &&
11157           change->trigger_page & trigger_page_bits &&
11158           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11159       {
11160         change->actual_trigger_element = trigger_element;
11161         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11162         change->actual_trigger_player_bits = trigger_player;
11163         change->actual_trigger_side = trigger_side;
11164         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11165         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11166         change->actual_trigger_x = trigger_x;
11167         change->actual_trigger_y = trigger_y;
11168
11169         if ((change->can_change && !change_done) || change->has_action)
11170         {
11171           int x, y;
11172
11173           SCAN_PLAYFIELD(x, y)
11174           {
11175             if (Tile[x][y] == element)
11176             {
11177               if (change->can_change && !change_done)
11178               {
11179                 // if element already changed in this frame, not only prevent
11180                 // another element change (checked in ChangeElement()), but
11181                 // also prevent additional element actions for this element
11182
11183                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11184                     !level.use_action_after_change_bug)
11185                   continue;
11186
11187                 ChangeDelay[x][y] = 1;
11188                 ChangeEvent[x][y] = trigger_event;
11189
11190                 HandleElementChange(x, y, p);
11191               }
11192               else if (change->has_action)
11193               {
11194                 // if element already changed in this frame, not only prevent
11195                 // another element change (checked in ChangeElement()), but
11196                 // also prevent additional element actions for this element
11197
11198                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11199                     !level.use_action_after_change_bug)
11200                   continue;
11201
11202                 ExecuteCustomElementAction(x, y, element, p);
11203                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11204               }
11205             }
11206           }
11207
11208           if (change->can_change)
11209           {
11210             change_done = TRUE;
11211             change_done_any = TRUE;
11212           }
11213         }
11214       }
11215     }
11216   }
11217
11218   RECURSION_LOOP_DETECTION_END();
11219
11220   return change_done_any;
11221 }
11222
11223 static boolean CheckElementChangeExt(int x, int y,
11224                                      int element,
11225                                      int trigger_element,
11226                                      int trigger_event,
11227                                      int trigger_player,
11228                                      int trigger_side)
11229 {
11230   boolean change_done = FALSE;
11231   int p;
11232
11233   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11234       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11235     return FALSE;
11236
11237   if (Tile[x][y] == EL_BLOCKED)
11238   {
11239     Blocked2Moving(x, y, &x, &y);
11240     element = Tile[x][y];
11241   }
11242
11243   // check if element has already changed or is about to change after moving
11244   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11245        Tile[x][y] != element) ||
11246
11247       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11248        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11249         ChangePage[x][y] != -1)))
11250     return FALSE;
11251
11252   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11253
11254   for (p = 0; p < element_info[element].num_change_pages; p++)
11255   {
11256     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11257
11258     /* check trigger element for all events where the element that is checked
11259        for changing interacts with a directly adjacent element -- this is
11260        different to element changes that affect other elements to change on the
11261        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11262     boolean check_trigger_element =
11263       (trigger_event == CE_NEXT_TO_X ||
11264        trigger_event == CE_TOUCHING_X ||
11265        trigger_event == CE_HITTING_X ||
11266        trigger_event == CE_HIT_BY_X ||
11267        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11268
11269     if (change->can_change_or_has_action &&
11270         change->has_event[trigger_event] &&
11271         change->trigger_side & trigger_side &&
11272         change->trigger_player & trigger_player &&
11273         (!check_trigger_element ||
11274          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11275     {
11276       change->actual_trigger_element = trigger_element;
11277       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11278       change->actual_trigger_player_bits = trigger_player;
11279       change->actual_trigger_side = trigger_side;
11280       change->actual_trigger_ce_value = CustomValue[x][y];
11281       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11282       change->actual_trigger_x = x;
11283       change->actual_trigger_y = y;
11284
11285       // special case: trigger element not at (x,y) position for some events
11286       if (check_trigger_element)
11287       {
11288         static struct
11289         {
11290           int dx, dy;
11291         } move_xy[] =
11292           {
11293             {  0,  0 },
11294             { -1,  0 },
11295             { +1,  0 },
11296             {  0,  0 },
11297             {  0, -1 },
11298             {  0,  0 }, { 0, 0 }, { 0, 0 },
11299             {  0, +1 }
11300           };
11301
11302         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11303         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11304
11305         change->actual_trigger_ce_value = CustomValue[xx][yy];
11306         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11307         change->actual_trigger_x = xx;
11308         change->actual_trigger_y = yy;
11309       }
11310
11311       if (change->can_change && !change_done)
11312       {
11313         ChangeDelay[x][y] = 1;
11314         ChangeEvent[x][y] = trigger_event;
11315
11316         HandleElementChange(x, y, p);
11317
11318         change_done = TRUE;
11319       }
11320       else if (change->has_action)
11321       {
11322         ExecuteCustomElementAction(x, y, element, p);
11323         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11324       }
11325     }
11326   }
11327
11328   RECURSION_LOOP_DETECTION_END();
11329
11330   return change_done;
11331 }
11332
11333 static void PlayPlayerSound(struct PlayerInfo *player)
11334 {
11335   int jx = player->jx, jy = player->jy;
11336   int sound_element = player->artwork_element;
11337   int last_action = player->last_action_waiting;
11338   int action = player->action_waiting;
11339
11340   if (player->is_waiting)
11341   {
11342     if (action != last_action)
11343       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11344     else
11345       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11346   }
11347   else
11348   {
11349     if (action != last_action)
11350       StopSound(element_info[sound_element].sound[last_action]);
11351
11352     if (last_action == ACTION_SLEEPING)
11353       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11354   }
11355 }
11356
11357 static void PlayAllPlayersSound(void)
11358 {
11359   int i;
11360
11361   for (i = 0; i < MAX_PLAYERS; i++)
11362     if (stored_player[i].active)
11363       PlayPlayerSound(&stored_player[i]);
11364 }
11365
11366 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11367 {
11368   boolean last_waiting = player->is_waiting;
11369   int move_dir = player->MovDir;
11370
11371   player->dir_waiting = move_dir;
11372   player->last_action_waiting = player->action_waiting;
11373
11374   if (is_waiting)
11375   {
11376     if (!last_waiting)          // not waiting -> waiting
11377     {
11378       player->is_waiting = TRUE;
11379
11380       player->frame_counter_bored =
11381         FrameCounter +
11382         game.player_boring_delay_fixed +
11383         GetSimpleRandom(game.player_boring_delay_random);
11384       player->frame_counter_sleeping =
11385         FrameCounter +
11386         game.player_sleeping_delay_fixed +
11387         GetSimpleRandom(game.player_sleeping_delay_random);
11388
11389       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11390     }
11391
11392     if (game.player_sleeping_delay_fixed +
11393         game.player_sleeping_delay_random > 0 &&
11394         player->anim_delay_counter == 0 &&
11395         player->post_delay_counter == 0 &&
11396         FrameCounter >= player->frame_counter_sleeping)
11397       player->is_sleeping = TRUE;
11398     else if (game.player_boring_delay_fixed +
11399              game.player_boring_delay_random > 0 &&
11400              FrameCounter >= player->frame_counter_bored)
11401       player->is_bored = TRUE;
11402
11403     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11404                               player->is_bored ? ACTION_BORING :
11405                               ACTION_WAITING);
11406
11407     if (player->is_sleeping && player->use_murphy)
11408     {
11409       // special case for sleeping Murphy when leaning against non-free tile
11410
11411       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11412           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11413            !IS_MOVING(player->jx - 1, player->jy)))
11414         move_dir = MV_LEFT;
11415       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11416                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11417                 !IS_MOVING(player->jx + 1, player->jy)))
11418         move_dir = MV_RIGHT;
11419       else
11420         player->is_sleeping = FALSE;
11421
11422       player->dir_waiting = move_dir;
11423     }
11424
11425     if (player->is_sleeping)
11426     {
11427       if (player->num_special_action_sleeping > 0)
11428       {
11429         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11430         {
11431           int last_special_action = player->special_action_sleeping;
11432           int num_special_action = player->num_special_action_sleeping;
11433           int special_action =
11434             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11435              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11436              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11437              last_special_action + 1 : ACTION_SLEEPING);
11438           int special_graphic =
11439             el_act_dir2img(player->artwork_element, special_action, move_dir);
11440
11441           player->anim_delay_counter =
11442             graphic_info[special_graphic].anim_delay_fixed +
11443             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11444           player->post_delay_counter =
11445             graphic_info[special_graphic].post_delay_fixed +
11446             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11447
11448           player->special_action_sleeping = special_action;
11449         }
11450
11451         if (player->anim_delay_counter > 0)
11452         {
11453           player->action_waiting = player->special_action_sleeping;
11454           player->anim_delay_counter--;
11455         }
11456         else if (player->post_delay_counter > 0)
11457         {
11458           player->post_delay_counter--;
11459         }
11460       }
11461     }
11462     else if (player->is_bored)
11463     {
11464       if (player->num_special_action_bored > 0)
11465       {
11466         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11467         {
11468           int special_action =
11469             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11470           int special_graphic =
11471             el_act_dir2img(player->artwork_element, special_action, move_dir);
11472
11473           player->anim_delay_counter =
11474             graphic_info[special_graphic].anim_delay_fixed +
11475             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11476           player->post_delay_counter =
11477             graphic_info[special_graphic].post_delay_fixed +
11478             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11479
11480           player->special_action_bored = special_action;
11481         }
11482
11483         if (player->anim_delay_counter > 0)
11484         {
11485           player->action_waiting = player->special_action_bored;
11486           player->anim_delay_counter--;
11487         }
11488         else if (player->post_delay_counter > 0)
11489         {
11490           player->post_delay_counter--;
11491         }
11492       }
11493     }
11494   }
11495   else if (last_waiting)        // waiting -> not waiting
11496   {
11497     player->is_waiting = FALSE;
11498     player->is_bored = FALSE;
11499     player->is_sleeping = FALSE;
11500
11501     player->frame_counter_bored = -1;
11502     player->frame_counter_sleeping = -1;
11503
11504     player->anim_delay_counter = 0;
11505     player->post_delay_counter = 0;
11506
11507     player->dir_waiting = player->MovDir;
11508     player->action_waiting = ACTION_DEFAULT;
11509
11510     player->special_action_bored = ACTION_DEFAULT;
11511     player->special_action_sleeping = ACTION_DEFAULT;
11512   }
11513 }
11514
11515 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11516 {
11517   if ((!player->is_moving  && player->was_moving) ||
11518       (player->MovPos == 0 && player->was_moving) ||
11519       (player->is_snapping && !player->was_snapping) ||
11520       (player->is_dropping && !player->was_dropping))
11521   {
11522     if (!CheckSaveEngineSnapshotToList())
11523       return;
11524
11525     player->was_moving = FALSE;
11526     player->was_snapping = TRUE;
11527     player->was_dropping = TRUE;
11528   }
11529   else
11530   {
11531     if (player->is_moving)
11532       player->was_moving = TRUE;
11533
11534     if (!player->is_snapping)
11535       player->was_snapping = FALSE;
11536
11537     if (!player->is_dropping)
11538       player->was_dropping = FALSE;
11539   }
11540
11541   static struct MouseActionInfo mouse_action_last = { 0 };
11542   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11543   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11544
11545   if (new_released)
11546     CheckSaveEngineSnapshotToList();
11547
11548   mouse_action_last = mouse_action;
11549 }
11550
11551 static void CheckSingleStepMode(struct PlayerInfo *player)
11552 {
11553   if (tape.single_step && tape.recording && !tape.pausing)
11554   {
11555     // as it is called "single step mode", just return to pause mode when the
11556     // player stopped moving after one tile (or never starts moving at all)
11557     // (reverse logic needed here in case single step mode used in team mode)
11558     if (player->is_moving ||
11559         player->is_pushing ||
11560         player->is_dropping_pressed ||
11561         player->effective_mouse_action.button)
11562       game.enter_single_step_mode = FALSE;
11563   }
11564
11565   CheckSaveEngineSnapshot(player);
11566 }
11567
11568 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11569 {
11570   int left      = player_action & JOY_LEFT;
11571   int right     = player_action & JOY_RIGHT;
11572   int up        = player_action & JOY_UP;
11573   int down      = player_action & JOY_DOWN;
11574   int button1   = player_action & JOY_BUTTON_1;
11575   int button2   = player_action & JOY_BUTTON_2;
11576   int dx        = (left ? -1 : right ? 1 : 0);
11577   int dy        = (up   ? -1 : down  ? 1 : 0);
11578
11579   if (!player->active || tape.pausing)
11580     return 0;
11581
11582   if (player_action)
11583   {
11584     if (button1)
11585       SnapField(player, dx, dy);
11586     else
11587     {
11588       if (button2)
11589         DropElement(player);
11590
11591       MovePlayer(player, dx, dy);
11592     }
11593
11594     CheckSingleStepMode(player);
11595
11596     SetPlayerWaiting(player, FALSE);
11597
11598     return player_action;
11599   }
11600   else
11601   {
11602     // no actions for this player (no input at player's configured device)
11603
11604     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11605     SnapField(player, 0, 0);
11606     CheckGravityMovementWhenNotMoving(player);
11607
11608     if (player->MovPos == 0)
11609       SetPlayerWaiting(player, TRUE);
11610
11611     if (player->MovPos == 0)    // needed for tape.playing
11612       player->is_moving = FALSE;
11613
11614     player->is_dropping = FALSE;
11615     player->is_dropping_pressed = FALSE;
11616     player->drop_pressed_delay = 0;
11617
11618     CheckSingleStepMode(player);
11619
11620     return 0;
11621   }
11622 }
11623
11624 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11625                                          byte *tape_action)
11626 {
11627   if (!tape.use_mouse_actions)
11628     return;
11629
11630   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11631   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11632   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11633 }
11634
11635 static void SetTapeActionFromMouseAction(byte *tape_action,
11636                                          struct MouseActionInfo *mouse_action)
11637 {
11638   if (!tape.use_mouse_actions)
11639     return;
11640
11641   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11642   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11643   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11644 }
11645
11646 static void CheckLevelSolved(void)
11647 {
11648   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11649   {
11650     if (game_bd.level_solved &&
11651         !game_bd.game_over)                             // game won
11652     {
11653       LevelSolved();
11654
11655       game_bd.game_over = TRUE;
11656
11657       game.all_players_gone = TRUE;
11658     }
11659
11660     if (game_bd.game_over)                              // game lost
11661       game.all_players_gone = TRUE;
11662   }
11663   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11664   {
11665     if (game_em.level_solved &&
11666         !game_em.game_over)                             // game won
11667     {
11668       LevelSolved();
11669
11670       game_em.game_over = TRUE;
11671
11672       game.all_players_gone = TRUE;
11673     }
11674
11675     if (game_em.game_over)                              // game lost
11676       game.all_players_gone = TRUE;
11677   }
11678   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11679   {
11680     if (game_sp.level_solved &&
11681         !game_sp.game_over)                             // game won
11682     {
11683       LevelSolved();
11684
11685       game_sp.game_over = TRUE;
11686
11687       game.all_players_gone = TRUE;
11688     }
11689
11690     if (game_sp.game_over)                              // game lost
11691       game.all_players_gone = TRUE;
11692   }
11693   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11694   {
11695     if (game_mm.level_solved &&
11696         !game_mm.game_over)                             // game won
11697     {
11698       LevelSolved();
11699
11700       game_mm.game_over = TRUE;
11701
11702       game.all_players_gone = TRUE;
11703     }
11704
11705     if (game_mm.game_over)                              // game lost
11706       game.all_players_gone = TRUE;
11707   }
11708 }
11709
11710 static void PlayTimeoutSound(int seconds_left)
11711 {
11712   // will be played directly by BD engine (for classic bonus time sounds)
11713   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11714     return;
11715
11716   // try to use individual "running out of time" sound for each second left
11717   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11718
11719   // if special sound per second not defined, use default sound
11720   if (getSoundInfoEntryFilename(sound) == NULL)
11721     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11722
11723   // if out of time, but player still alive, play special "timeout" sound, if defined
11724   if (seconds_left == 0 && !checkGameFailed())
11725     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11726       sound = SND_GAME_TIMEOUT;
11727
11728   PlaySound(sound);
11729 }
11730
11731 static void CheckLevelTime_StepCounter(void)
11732 {
11733   int i;
11734
11735   TimePlayed++;
11736
11737   if (TimeLeft > 0)
11738   {
11739     TimeLeft--;
11740
11741     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11742       PlayTimeoutSound(TimeLeft);
11743
11744     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11745
11746     DisplayGameControlValues();
11747
11748     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11749       for (i = 0; i < MAX_PLAYERS; i++)
11750         KillPlayer(&stored_player[i]);
11751   }
11752   else if (game.no_level_time_limit && !game.all_players_gone)
11753   {
11754     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11755
11756     DisplayGameControlValues();
11757   }
11758 }
11759
11760 static void CheckLevelTime(void)
11761 {
11762   int frames_per_second = FRAMES_PER_SECOND;
11763   int i;
11764
11765   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11766   {
11767     // level time may be running slower in native BD engine
11768     frames_per_second = getFramesPerSecond_BD();
11769
11770     // if native engine time changed, force main engine time change
11771     if (getTimeLeft_BD() < TimeLeft)
11772       TimeFrames = frames_per_second;
11773
11774     // if last second running, wait for native engine time to exactly reach zero
11775     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11776       TimeFrames = frames_per_second - 1;
11777   }
11778
11779   if (TimeFrames >= frames_per_second)
11780   {
11781     TimeFrames = 0;
11782
11783     for (i = 0; i < MAX_PLAYERS; i++)
11784     {
11785       struct PlayerInfo *player = &stored_player[i];
11786
11787       if (SHIELD_ON(player))
11788       {
11789         player->shield_normal_time_left--;
11790
11791         if (player->shield_deadly_time_left > 0)
11792           player->shield_deadly_time_left--;
11793       }
11794     }
11795
11796     if (!game.LevelSolved && !level.use_step_counter)
11797     {
11798       TimePlayed++;
11799
11800       if (TimeLeft > 0)
11801       {
11802         TimeLeft--;
11803
11804         if (TimeLeft <= 10 && game.time_limit)
11805           PlayTimeoutSound(TimeLeft);
11806
11807         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11808            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11809
11810         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11811
11812         if (!TimeLeft && game.time_limit)
11813         {
11814           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11815           {
11816             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11817               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11818           }
11819           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11820           {
11821             game_em.lev->killed_out_of_time = TRUE;
11822           }
11823           else
11824           {
11825             for (i = 0; i < MAX_PLAYERS; i++)
11826               KillPlayer(&stored_player[i]);
11827           }
11828         }
11829       }
11830       else if (game.no_level_time_limit && !game.all_players_gone)
11831       {
11832         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11833       }
11834
11835       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11836     }
11837   }
11838
11839   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11840   {
11841     TapeTimeFrames = 0;
11842     TapeTime++;
11843
11844     if (tape.recording || tape.playing)
11845       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11846   }
11847
11848   if (tape.recording || tape.playing)
11849     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11850
11851   UpdateAndDisplayGameControlValues();
11852 }
11853
11854 void AdvanceFrameAndPlayerCounters(int player_nr)
11855 {
11856   int i;
11857
11858   // handle game and tape time differently for native BD game engine
11859
11860   // tape time is running in native BD engine even if player is not hatched yet
11861   if (!checkGameRunning())
11862     return;
11863
11864   // advance frame counters (global frame counter and tape time frame counter)
11865   FrameCounter++;
11866   TapeTimeFrames++;
11867
11868   // level time is running in native BD engine after player is being hatched
11869   if (!checkGamePlaying())
11870     return;
11871
11872   // advance time frame counter (used to control available time to solve level)
11873   TimeFrames++;
11874
11875   // advance player counters (counters for move delay, move animation etc.)
11876   for (i = 0; i < MAX_PLAYERS; i++)
11877   {
11878     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11879     int move_delay_value = stored_player[i].move_delay_value;
11880     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11881
11882     if (!advance_player_counters)       // not all players may be affected
11883       continue;
11884
11885     if (move_frames == 0)       // less than one move per game frame
11886     {
11887       int stepsize = TILEX / move_delay_value;
11888       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11889       int count = (stored_player[i].is_moving ?
11890                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11891
11892       if (count % delay == 0)
11893         move_frames = 1;
11894     }
11895
11896     stored_player[i].Frame += move_frames;
11897
11898     if (stored_player[i].MovPos != 0)
11899       stored_player[i].StepFrame += move_frames;
11900
11901     if (stored_player[i].move_delay > 0)
11902       stored_player[i].move_delay--;
11903
11904     // due to bugs in previous versions, counter must count up, not down
11905     if (stored_player[i].push_delay != -1)
11906       stored_player[i].push_delay++;
11907
11908     if (stored_player[i].drop_delay > 0)
11909       stored_player[i].drop_delay--;
11910
11911     if (stored_player[i].is_dropping_pressed)
11912       stored_player[i].drop_pressed_delay++;
11913   }
11914 }
11915
11916 void AdvanceFrameCounter(void)
11917 {
11918   FrameCounter++;
11919 }
11920
11921 void AdvanceGfxFrame(void)
11922 {
11923   int x, y;
11924
11925   SCAN_PLAYFIELD(x, y)
11926   {
11927     GfxFrame[x][y]++;
11928   }
11929 }
11930
11931 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11932                               struct MouseActionInfo *mouse_action_last)
11933 {
11934   if (mouse_action->button)
11935   {
11936     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11937     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11938     int x = mouse_action->lx;
11939     int y = mouse_action->ly;
11940     int element = Tile[x][y];
11941
11942     if (new_button)
11943     {
11944       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11945       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11946                                          ch_button);
11947     }
11948
11949     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11950     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11951                                        ch_button);
11952
11953     if (level.use_step_counter)
11954     {
11955       boolean counted_click = FALSE;
11956
11957       // element clicked that can change when clicked/pressed
11958       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11959           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11960            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11961         counted_click = TRUE;
11962
11963       // element clicked that can trigger change when clicked/pressed
11964       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11965           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11966         counted_click = TRUE;
11967
11968       if (new_button && counted_click)
11969         CheckLevelTime_StepCounter();
11970     }
11971   }
11972 }
11973
11974 void StartGameActions(boolean init_network_game, boolean record_tape,
11975                       int random_seed)
11976 {
11977   unsigned int new_random_seed = InitRND(random_seed);
11978
11979   if (record_tape)
11980     TapeStartRecording(new_random_seed);
11981
11982   if (setup.auto_pause_on_start && !tape.pausing)
11983     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11984
11985   if (init_network_game)
11986   {
11987     SendToServer_LevelFile();
11988     SendToServer_StartPlaying();
11989
11990     return;
11991   }
11992
11993   InitGame();
11994 }
11995
11996 static void GameActionsExt(void)
11997 {
11998 #if 0
11999   static unsigned int game_frame_delay = 0;
12000 #endif
12001   unsigned int game_frame_delay_value;
12002   byte *recorded_player_action;
12003   byte summarized_player_action = 0;
12004   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12005   int i;
12006
12007   // detect endless loops, caused by custom element programming
12008   if (recursion_loop_detected && recursion_loop_depth == 0)
12009   {
12010     char *message = getStringCat3("Internal Error! Element ",
12011                                   EL_NAME(recursion_loop_element),
12012                                   " caused endless loop! Quit the game?");
12013
12014     Warn("element '%s' caused endless loop in game engine",
12015          EL_NAME(recursion_loop_element));
12016
12017     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12018
12019     recursion_loop_detected = FALSE;    // if game should be continued
12020
12021     free(message);
12022
12023     return;
12024   }
12025
12026   if (game.restart_level)
12027     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12028
12029   CheckLevelSolved();
12030
12031   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12032     GameWon();
12033
12034   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12035     TapeStop();
12036
12037   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12038     return;
12039
12040   game_frame_delay_value =
12041     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12042
12043   if (tape.playing && tape.warp_forward && !tape.pausing)
12044     game_frame_delay_value = 0;
12045
12046   SetVideoFrameDelay(game_frame_delay_value);
12047
12048   // (de)activate virtual buttons depending on current game status
12049   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12050   {
12051     if (game.all_players_gone)  // if no players there to be controlled anymore
12052       SetOverlayActive(FALSE);
12053     else if (!tape.playing)     // if game continues after tape stopped playing
12054       SetOverlayActive(TRUE);
12055   }
12056
12057 #if 0
12058 #if 0
12059   // ---------- main game synchronization point ----------
12060
12061   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12062
12063   Debug("game:playing:skip", "skip == %d", skip);
12064
12065 #else
12066   // ---------- main game synchronization point ----------
12067
12068   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12069 #endif
12070 #endif
12071
12072   if (network_playing && !network_player_action_received)
12073   {
12074     // try to get network player actions in time
12075
12076     // last chance to get network player actions without main loop delay
12077     HandleNetworking();
12078
12079     // game was quit by network peer
12080     if (game_status != GAME_MODE_PLAYING)
12081       return;
12082
12083     // check if network player actions still missing and game still running
12084     if (!network_player_action_received && !checkGameEnded())
12085       return;           // failed to get network player actions in time
12086
12087     // do not yet reset "network_player_action_received" (for tape.pausing)
12088   }
12089
12090   if (tape.pausing)
12091     return;
12092
12093   // at this point we know that we really continue executing the game
12094
12095   network_player_action_received = FALSE;
12096
12097   // when playing tape, read previously recorded player input from tape data
12098   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12099
12100   local_player->effective_mouse_action = local_player->mouse_action;
12101
12102   if (recorded_player_action != NULL)
12103     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12104                                  recorded_player_action);
12105
12106   // TapePlayAction() may return NULL when toggling to "pause before death"
12107   if (tape.pausing)
12108     return;
12109
12110   if (tape.set_centered_player)
12111   {
12112     game.centered_player_nr_next = tape.centered_player_nr_next;
12113     game.set_centered_player = TRUE;
12114   }
12115
12116   for (i = 0; i < MAX_PLAYERS; i++)
12117   {
12118     summarized_player_action |= stored_player[i].action;
12119
12120     if (!network_playing && (game.team_mode || tape.playing))
12121       stored_player[i].effective_action = stored_player[i].action;
12122   }
12123
12124   if (network_playing && !checkGameEnded())
12125     SendToServer_MovePlayer(summarized_player_action);
12126
12127   // summarize all actions at local players mapped input device position
12128   // (this allows using different input devices in single player mode)
12129   if (!network.enabled && !game.team_mode)
12130     stored_player[map_player_action[local_player->index_nr]].effective_action =
12131       summarized_player_action;
12132
12133   // summarize all actions at centered player in local team mode
12134   if (tape.recording &&
12135       setup.team_mode && !network.enabled &&
12136       setup.input_on_focus &&
12137       game.centered_player_nr != -1)
12138   {
12139     for (i = 0; i < MAX_PLAYERS; i++)
12140       stored_player[map_player_action[i]].effective_action =
12141         (i == game.centered_player_nr ? summarized_player_action : 0);
12142   }
12143
12144   if (recorded_player_action != NULL)
12145     for (i = 0; i < MAX_PLAYERS; i++)
12146       stored_player[i].effective_action = recorded_player_action[i];
12147
12148   for (i = 0; i < MAX_PLAYERS; i++)
12149   {
12150     tape_action[i] = stored_player[i].effective_action;
12151
12152     /* (this may happen in the RND game engine if a player was not present on
12153        the playfield on level start, but appeared later from a custom element */
12154     if (setup.team_mode &&
12155         tape.recording &&
12156         tape_action[i] &&
12157         !tape.player_participates[i])
12158       tape.player_participates[i] = TRUE;
12159   }
12160
12161   SetTapeActionFromMouseAction(tape_action,
12162                                &local_player->effective_mouse_action);
12163
12164   // only record actions from input devices, but not programmed actions
12165   if (tape.recording)
12166     TapeRecordAction(tape_action);
12167
12168   // remember if game was played (especially after tape stopped playing)
12169   if (!tape.playing && summarized_player_action && !checkGameFailed())
12170     game.GamePlayed = TRUE;
12171
12172 #if USE_NEW_PLAYER_ASSIGNMENTS
12173   // !!! also map player actions in single player mode !!!
12174   // if (game.team_mode)
12175   if (1)
12176   {
12177     byte mapped_action[MAX_PLAYERS];
12178
12179 #if DEBUG_PLAYER_ACTIONS
12180     for (i = 0; i < MAX_PLAYERS; i++)
12181       DebugContinued("", "%d, ", stored_player[i].effective_action);
12182 #endif
12183
12184     for (i = 0; i < MAX_PLAYERS; i++)
12185       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12186
12187     for (i = 0; i < MAX_PLAYERS; i++)
12188       stored_player[i].effective_action = mapped_action[i];
12189
12190 #if DEBUG_PLAYER_ACTIONS
12191     DebugContinued("", "=> ");
12192     for (i = 0; i < MAX_PLAYERS; i++)
12193       DebugContinued("", "%d, ", stored_player[i].effective_action);
12194     DebugContinued("game:playing:player", "\n");
12195 #endif
12196   }
12197 #if DEBUG_PLAYER_ACTIONS
12198   else
12199   {
12200     for (i = 0; i < MAX_PLAYERS; i++)
12201       DebugContinued("", "%d, ", stored_player[i].effective_action);
12202     DebugContinued("game:playing:player", "\n");
12203   }
12204 #endif
12205 #endif
12206
12207   for (i = 0; i < MAX_PLAYERS; i++)
12208   {
12209     // allow engine snapshot in case of changed movement attempt
12210     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12211         (stored_player[i].effective_action & KEY_MOTION))
12212       game.snapshot.changed_action = TRUE;
12213
12214     // allow engine snapshot in case of snapping/dropping attempt
12215     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12216         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12217       game.snapshot.changed_action = TRUE;
12218
12219     game.snapshot.last_action[i] = stored_player[i].effective_action;
12220   }
12221
12222   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12223   {
12224     GameActions_BD_Main();
12225   }
12226   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12227   {
12228     GameActions_EM_Main();
12229   }
12230   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12231   {
12232     GameActions_SP_Main();
12233   }
12234   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12235   {
12236     GameActions_MM_Main();
12237   }
12238   else
12239   {
12240     GameActions_RND_Main();
12241   }
12242
12243   BlitScreenToBitmap(backbuffer);
12244
12245   CheckLevelSolved();
12246   CheckLevelTime();
12247
12248   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12249
12250   if (global.show_frames_per_second)
12251   {
12252     static unsigned int fps_counter = 0;
12253     static int fps_frames = 0;
12254     unsigned int fps_delay_ms = Counter() - fps_counter;
12255
12256     fps_frames++;
12257
12258     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12259     {
12260       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12261
12262       fps_frames = 0;
12263       fps_counter = Counter();
12264
12265       // always draw FPS to screen after FPS value was updated
12266       redraw_mask |= REDRAW_FPS;
12267     }
12268
12269     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12270     if (GetDrawDeactivationMask() == REDRAW_NONE)
12271       redraw_mask |= REDRAW_FPS;
12272   }
12273 }
12274
12275 static void GameActions_CheckSaveEngineSnapshot(void)
12276 {
12277   if (!game.snapshot.save_snapshot)
12278     return;
12279
12280   // clear flag for saving snapshot _before_ saving snapshot
12281   game.snapshot.save_snapshot = FALSE;
12282
12283   SaveEngineSnapshotToList();
12284 }
12285
12286 void GameActions(void)
12287 {
12288   GameActionsExt();
12289
12290   GameActions_CheckSaveEngineSnapshot();
12291 }
12292
12293 void GameActions_BD_Main(void)
12294 {
12295   byte effective_action[MAX_PLAYERS];
12296   int i;
12297
12298   for (i = 0; i < MAX_PLAYERS; i++)
12299     effective_action[i] = stored_player[i].effective_action;
12300
12301   GameActions_BD(effective_action);
12302 }
12303
12304 void GameActions_EM_Main(void)
12305 {
12306   byte effective_action[MAX_PLAYERS];
12307   int i;
12308
12309   for (i = 0; i < MAX_PLAYERS; i++)
12310     effective_action[i] = stored_player[i].effective_action;
12311
12312   GameActions_EM(effective_action);
12313 }
12314
12315 void GameActions_SP_Main(void)
12316 {
12317   byte effective_action[MAX_PLAYERS];
12318   int i;
12319
12320   for (i = 0; i < MAX_PLAYERS; i++)
12321     effective_action[i] = stored_player[i].effective_action;
12322
12323   GameActions_SP(effective_action);
12324
12325   for (i = 0; i < MAX_PLAYERS; i++)
12326   {
12327     if (stored_player[i].force_dropping)
12328       stored_player[i].action |= KEY_BUTTON_DROP;
12329
12330     stored_player[i].force_dropping = FALSE;
12331   }
12332 }
12333
12334 void GameActions_MM_Main(void)
12335 {
12336   AdvanceGfxFrame();
12337
12338   GameActions_MM(local_player->effective_mouse_action);
12339 }
12340
12341 void GameActions_RND_Main(void)
12342 {
12343   GameActions_RND();
12344 }
12345
12346 void GameActions_RND(void)
12347 {
12348   static struct MouseActionInfo mouse_action_last = { 0 };
12349   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12350   int magic_wall_x = 0, magic_wall_y = 0;
12351   int i, x, y, element, graphic, last_gfx_frame;
12352
12353   InitPlayfieldScanModeVars();
12354
12355   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12356   {
12357     SCAN_PLAYFIELD(x, y)
12358     {
12359       ChangeCount[x][y] = 0;
12360       ChangeEvent[x][y] = -1;
12361     }
12362   }
12363
12364   if (game.set_centered_player)
12365   {
12366     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12367
12368     // switching to "all players" only possible if all players fit to screen
12369     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12370     {
12371       game.centered_player_nr_next = game.centered_player_nr;
12372       game.set_centered_player = FALSE;
12373     }
12374
12375     // do not switch focus to non-existing (or non-active) player
12376     if (game.centered_player_nr_next >= 0 &&
12377         !stored_player[game.centered_player_nr_next].active)
12378     {
12379       game.centered_player_nr_next = game.centered_player_nr;
12380       game.set_centered_player = FALSE;
12381     }
12382   }
12383
12384   if (game.set_centered_player &&
12385       ScreenMovPos == 0)        // screen currently aligned at tile position
12386   {
12387     int sx, sy;
12388
12389     if (game.centered_player_nr_next == -1)
12390     {
12391       setScreenCenteredToAllPlayers(&sx, &sy);
12392     }
12393     else
12394     {
12395       sx = stored_player[game.centered_player_nr_next].jx;
12396       sy = stored_player[game.centered_player_nr_next].jy;
12397     }
12398
12399     game.centered_player_nr = game.centered_player_nr_next;
12400     game.set_centered_player = FALSE;
12401
12402     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12403     DrawGameDoorValues();
12404   }
12405
12406   // check single step mode (set flag and clear again if any player is active)
12407   game.enter_single_step_mode =
12408     (tape.single_step && tape.recording && !tape.pausing);
12409
12410   for (i = 0; i < MAX_PLAYERS; i++)
12411   {
12412     int actual_player_action = stored_player[i].effective_action;
12413
12414 #if 1
12415     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12416        - rnd_equinox_tetrachloride 048
12417        - rnd_equinox_tetrachloride_ii 096
12418        - rnd_emanuel_schmieg 002
12419        - doctor_sloan_ww 001, 020
12420     */
12421     if (stored_player[i].MovPos == 0)
12422       CheckGravityMovement(&stored_player[i]);
12423 #endif
12424
12425     // overwrite programmed action with tape action
12426     if (stored_player[i].programmed_action)
12427       actual_player_action = stored_player[i].programmed_action;
12428
12429     PlayerActions(&stored_player[i], actual_player_action);
12430
12431     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12432   }
12433
12434   // single step pause mode may already have been toggled by "ScrollPlayer()"
12435   if (game.enter_single_step_mode && !tape.pausing)
12436     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12437
12438   ScrollScreen(NULL, SCROLL_GO_ON);
12439
12440   /* for backwards compatibility, the following code emulates a fixed bug that
12441      occured when pushing elements (causing elements that just made their last
12442      pushing step to already (if possible) make their first falling step in the
12443      same game frame, which is bad); this code is also needed to use the famous
12444      "spring push bug" which is used in older levels and might be wanted to be
12445      used also in newer levels, but in this case the buggy pushing code is only
12446      affecting the "spring" element and no other elements */
12447
12448   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12449   {
12450     for (i = 0; i < MAX_PLAYERS; i++)
12451     {
12452       struct PlayerInfo *player = &stored_player[i];
12453       int x = player->jx;
12454       int y = player->jy;
12455
12456       if (player->active && player->is_pushing && player->is_moving &&
12457           IS_MOVING(x, y) &&
12458           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12459            Tile[x][y] == EL_SPRING))
12460       {
12461         ContinueMoving(x, y);
12462
12463         // continue moving after pushing (this is actually a bug)
12464         if (!IS_MOVING(x, y))
12465           Stop[x][y] = FALSE;
12466       }
12467     }
12468   }
12469
12470   SCAN_PLAYFIELD(x, y)
12471   {
12472     Last[x][y] = Tile[x][y];
12473
12474     ChangeCount[x][y] = 0;
12475     ChangeEvent[x][y] = -1;
12476
12477     // this must be handled before main playfield loop
12478     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12479     {
12480       MovDelay[x][y]--;
12481       if (MovDelay[x][y] <= 0)
12482         RemoveField(x, y);
12483     }
12484
12485     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12486     {
12487       MovDelay[x][y]--;
12488       if (MovDelay[x][y] <= 0)
12489       {
12490         int element = Store[x][y];
12491         int move_direction = MovDir[x][y];
12492         int player_index_bit = Store2[x][y];
12493
12494         Store[x][y] = 0;
12495         Store2[x][y] = 0;
12496
12497         RemoveField(x, y);
12498         TEST_DrawLevelField(x, y);
12499
12500         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12501
12502         if (IS_ENVELOPE(element))
12503           local_player->show_envelope = element;
12504       }
12505     }
12506
12507 #if DEBUG
12508     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12509     {
12510       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12511             x, y);
12512       Debug("game:playing:GameActions_RND", "This should never happen!");
12513
12514       ChangePage[x][y] = -1;
12515     }
12516 #endif
12517
12518     Stop[x][y] = FALSE;
12519     if (WasJustMoving[x][y] > 0)
12520       WasJustMoving[x][y]--;
12521     if (WasJustFalling[x][y] > 0)
12522       WasJustFalling[x][y]--;
12523     if (CheckCollision[x][y] > 0)
12524       CheckCollision[x][y]--;
12525     if (CheckImpact[x][y] > 0)
12526       CheckImpact[x][y]--;
12527
12528     GfxFrame[x][y]++;
12529
12530     /* reset finished pushing action (not done in ContinueMoving() to allow
12531        continuous pushing animation for elements with zero push delay) */
12532     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12533     {
12534       ResetGfxAnimation(x, y);
12535       TEST_DrawLevelField(x, y);
12536     }
12537
12538 #if DEBUG
12539     if (IS_BLOCKED(x, y))
12540     {
12541       int oldx, oldy;
12542
12543       Blocked2Moving(x, y, &oldx, &oldy);
12544       if (!IS_MOVING(oldx, oldy))
12545       {
12546         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12547         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12548         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12549         Debug("game:playing:GameActions_RND", "This should never happen!");
12550       }
12551     }
12552 #endif
12553   }
12554
12555   HandleMouseAction(&mouse_action, &mouse_action_last);
12556
12557   SCAN_PLAYFIELD(x, y)
12558   {
12559     element = Tile[x][y];
12560     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12561     last_gfx_frame = GfxFrame[x][y];
12562
12563     if (element == EL_EMPTY)
12564       graphic = el2img(GfxElementEmpty[x][y]);
12565
12566     ResetGfxFrame(x, y);
12567
12568     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12569       DrawLevelGraphicAnimation(x, y, graphic);
12570
12571     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12572         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12573       ResetRandomAnimationValue(x, y);
12574
12575     SetRandomAnimationValue(x, y);
12576
12577     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12578
12579     if (IS_INACTIVE(element))
12580     {
12581       if (IS_ANIMATED(graphic))
12582         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12583
12584       continue;
12585     }
12586
12587     // this may take place after moving, so 'element' may have changed
12588     if (IS_CHANGING(x, y) &&
12589         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12590     {
12591       int page = element_info[element].event_page_nr[CE_DELAY];
12592
12593       HandleElementChange(x, y, page);
12594
12595       element = Tile[x][y];
12596       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12597     }
12598
12599     CheckNextToConditions(x, y);
12600
12601     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12602     {
12603       StartMoving(x, y);
12604
12605       element = Tile[x][y];
12606       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12607
12608       if (IS_ANIMATED(graphic) &&
12609           !IS_MOVING(x, y) &&
12610           !Stop[x][y])
12611         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12612
12613       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12614         TEST_DrawTwinkleOnField(x, y);
12615     }
12616     else if (element == EL_ACID)
12617     {
12618       if (!Stop[x][y])
12619         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12620     }
12621     else if ((element == EL_EXIT_OPEN ||
12622               element == EL_EM_EXIT_OPEN ||
12623               element == EL_SP_EXIT_OPEN ||
12624               element == EL_STEEL_EXIT_OPEN ||
12625               element == EL_EM_STEEL_EXIT_OPEN ||
12626               element == EL_SP_TERMINAL ||
12627               element == EL_SP_TERMINAL_ACTIVE ||
12628               element == EL_EXTRA_TIME ||
12629               element == EL_SHIELD_NORMAL ||
12630               element == EL_SHIELD_DEADLY) &&
12631              IS_ANIMATED(graphic))
12632       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12633     else if (IS_MOVING(x, y))
12634       ContinueMoving(x, y);
12635     else if (IS_ACTIVE_BOMB(element))
12636       CheckDynamite(x, y);
12637     else if (element == EL_AMOEBA_GROWING)
12638       AmoebaGrowing(x, y);
12639     else if (element == EL_AMOEBA_SHRINKING)
12640       AmoebaShrinking(x, y);
12641
12642 #if !USE_NEW_AMOEBA_CODE
12643     else if (IS_AMOEBALIVE(element))
12644       AmoebaReproduce(x, y);
12645 #endif
12646
12647     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12648       Life(x, y);
12649     else if (element == EL_EXIT_CLOSED)
12650       CheckExit(x, y);
12651     else if (element == EL_EM_EXIT_CLOSED)
12652       CheckExitEM(x, y);
12653     else if (element == EL_STEEL_EXIT_CLOSED)
12654       CheckExitSteel(x, y);
12655     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12656       CheckExitSteelEM(x, y);
12657     else if (element == EL_SP_EXIT_CLOSED)
12658       CheckExitSP(x, y);
12659     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12660              element == EL_EXPANDABLE_STEELWALL_GROWING)
12661       WallGrowing(x, y);
12662     else if (element == EL_EXPANDABLE_WALL ||
12663              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12664              element == EL_EXPANDABLE_WALL_VERTICAL ||
12665              element == EL_EXPANDABLE_WALL_ANY ||
12666              element == EL_BD_EXPANDABLE_WALL ||
12667              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12668              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12669              element == EL_EXPANDABLE_STEELWALL_ANY)
12670       CheckWallGrowing(x, y);
12671     else if (element == EL_FLAMES)
12672       CheckForDragon(x, y);
12673     else if (element == EL_EXPLOSION)
12674       ; // drawing of correct explosion animation is handled separately
12675     else if (element == EL_ELEMENT_SNAPPING ||
12676              element == EL_DIAGONAL_SHRINKING ||
12677              element == EL_DIAGONAL_GROWING)
12678     {
12679       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12680
12681       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12682     }
12683     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12684       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12685
12686     if (IS_BELT_ACTIVE(element))
12687       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12688
12689     if (game.magic_wall_active)
12690     {
12691       int jx = local_player->jx, jy = local_player->jy;
12692
12693       // play the element sound at the position nearest to the player
12694       if ((element == EL_MAGIC_WALL_FULL ||
12695            element == EL_MAGIC_WALL_ACTIVE ||
12696            element == EL_MAGIC_WALL_EMPTYING ||
12697            element == EL_BD_MAGIC_WALL_FULL ||
12698            element == EL_BD_MAGIC_WALL_ACTIVE ||
12699            element == EL_BD_MAGIC_WALL_EMPTYING ||
12700            element == EL_DC_MAGIC_WALL_FULL ||
12701            element == EL_DC_MAGIC_WALL_ACTIVE ||
12702            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12703           ABS(x - jx) + ABS(y - jy) <
12704           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12705       {
12706         magic_wall_x = x;
12707         magic_wall_y = y;
12708       }
12709     }
12710   }
12711
12712 #if USE_NEW_AMOEBA_CODE
12713   // new experimental amoeba growth stuff
12714   if (!(FrameCounter % 8))
12715   {
12716     static unsigned int random = 1684108901;
12717
12718     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12719     {
12720       x = RND(lev_fieldx);
12721       y = RND(lev_fieldy);
12722       element = Tile[x][y];
12723
12724       if (!IS_PLAYER(x, y) &&
12725           (element == EL_EMPTY ||
12726            CAN_GROW_INTO(element) ||
12727            element == EL_QUICKSAND_EMPTY ||
12728            element == EL_QUICKSAND_FAST_EMPTY ||
12729            element == EL_ACID_SPLASH_LEFT ||
12730            element == EL_ACID_SPLASH_RIGHT))
12731       {
12732         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12733             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12734             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12735             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12736           Tile[x][y] = EL_AMOEBA_DROP;
12737       }
12738
12739       random = random * 129 + 1;
12740     }
12741   }
12742 #endif
12743
12744   game.explosions_delayed = FALSE;
12745
12746   SCAN_PLAYFIELD(x, y)
12747   {
12748     element = Tile[x][y];
12749
12750     if (ExplodeField[x][y])
12751       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12752     else if (element == EL_EXPLOSION)
12753       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12754
12755     ExplodeField[x][y] = EX_TYPE_NONE;
12756   }
12757
12758   game.explosions_delayed = TRUE;
12759
12760   if (game.magic_wall_active)
12761   {
12762     if (!(game.magic_wall_time_left % 4))
12763     {
12764       int element = Tile[magic_wall_x][magic_wall_y];
12765
12766       if (element == EL_BD_MAGIC_WALL_FULL ||
12767           element == EL_BD_MAGIC_WALL_ACTIVE ||
12768           element == EL_BD_MAGIC_WALL_EMPTYING)
12769         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12770       else if (element == EL_DC_MAGIC_WALL_FULL ||
12771                element == EL_DC_MAGIC_WALL_ACTIVE ||
12772                element == EL_DC_MAGIC_WALL_EMPTYING)
12773         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12774       else
12775         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12776     }
12777
12778     if (game.magic_wall_time_left > 0)
12779     {
12780       game.magic_wall_time_left--;
12781
12782       if (!game.magic_wall_time_left)
12783       {
12784         SCAN_PLAYFIELD(x, y)
12785         {
12786           element = Tile[x][y];
12787
12788           if (element == EL_MAGIC_WALL_ACTIVE ||
12789               element == EL_MAGIC_WALL_FULL)
12790           {
12791             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12792             TEST_DrawLevelField(x, y);
12793           }
12794           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12795                    element == EL_BD_MAGIC_WALL_FULL)
12796           {
12797             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12798             TEST_DrawLevelField(x, y);
12799           }
12800           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12801                    element == EL_DC_MAGIC_WALL_FULL)
12802           {
12803             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12804             TEST_DrawLevelField(x, y);
12805           }
12806         }
12807
12808         game.magic_wall_active = FALSE;
12809       }
12810     }
12811   }
12812
12813   if (game.light_time_left > 0)
12814   {
12815     game.light_time_left--;
12816
12817     if (game.light_time_left == 0)
12818       RedrawAllLightSwitchesAndInvisibleElements();
12819   }
12820
12821   if (game.timegate_time_left > 0)
12822   {
12823     game.timegate_time_left--;
12824
12825     if (game.timegate_time_left == 0)
12826       CloseAllOpenTimegates();
12827   }
12828
12829   if (game.lenses_time_left > 0)
12830   {
12831     game.lenses_time_left--;
12832
12833     if (game.lenses_time_left == 0)
12834       RedrawAllInvisibleElementsForLenses();
12835   }
12836
12837   if (game.magnify_time_left > 0)
12838   {
12839     game.magnify_time_left--;
12840
12841     if (game.magnify_time_left == 0)
12842       RedrawAllInvisibleElementsForMagnifier();
12843   }
12844
12845   for (i = 0; i < MAX_PLAYERS; i++)
12846   {
12847     struct PlayerInfo *player = &stored_player[i];
12848
12849     if (SHIELD_ON(player))
12850     {
12851       if (player->shield_deadly_time_left)
12852         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12853       else if (player->shield_normal_time_left)
12854         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12855     }
12856   }
12857
12858 #if USE_DELAYED_GFX_REDRAW
12859   SCAN_PLAYFIELD(x, y)
12860   {
12861     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12862     {
12863       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12864          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12865
12866       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12867         DrawLevelField(x, y);
12868
12869       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12870         DrawLevelFieldCrumbled(x, y);
12871
12872       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12873         DrawLevelFieldCrumbledNeighbours(x, y);
12874
12875       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12876         DrawTwinkleOnField(x, y);
12877     }
12878
12879     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12880   }
12881 #endif
12882
12883   DrawAllPlayers();
12884   PlayAllPlayersSound();
12885
12886   for (i = 0; i < MAX_PLAYERS; i++)
12887   {
12888     struct PlayerInfo *player = &stored_player[i];
12889
12890     if (player->show_envelope != 0 && (!player->active ||
12891                                        player->MovPos == 0))
12892     {
12893       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12894
12895       player->show_envelope = 0;
12896     }
12897   }
12898
12899   // use random number generator in every frame to make it less predictable
12900   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12901     RND(1);
12902
12903   mouse_action_last = mouse_action;
12904 }
12905
12906 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12907 {
12908   int min_x = x, min_y = y, max_x = x, max_y = y;
12909   int scr_fieldx = getScreenFieldSizeX();
12910   int scr_fieldy = getScreenFieldSizeY();
12911   int i;
12912
12913   for (i = 0; i < MAX_PLAYERS; i++)
12914   {
12915     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12916
12917     if (!stored_player[i].active || &stored_player[i] == player)
12918       continue;
12919
12920     min_x = MIN(min_x, jx);
12921     min_y = MIN(min_y, jy);
12922     max_x = MAX(max_x, jx);
12923     max_y = MAX(max_y, jy);
12924   }
12925
12926   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12927 }
12928
12929 static boolean AllPlayersInVisibleScreen(void)
12930 {
12931   int i;
12932
12933   for (i = 0; i < MAX_PLAYERS; i++)
12934   {
12935     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12936
12937     if (!stored_player[i].active)
12938       continue;
12939
12940     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12941       return FALSE;
12942   }
12943
12944   return TRUE;
12945 }
12946
12947 void ScrollLevel(int dx, int dy)
12948 {
12949   int scroll_offset = 2 * TILEX_VAR;
12950   int x, y;
12951
12952   BlitBitmap(drawto_field, drawto_field,
12953              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12954              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12955              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12956              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12957              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12958              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12959
12960   if (dx != 0)
12961   {
12962     x = (dx == 1 ? BX1 : BX2);
12963     for (y = BY1; y <= BY2; y++)
12964       DrawScreenField(x, y);
12965   }
12966
12967   if (dy != 0)
12968   {
12969     y = (dy == 1 ? BY1 : BY2);
12970     for (x = BX1; x <= BX2; x++)
12971       DrawScreenField(x, y);
12972   }
12973
12974   redraw_mask |= REDRAW_FIELD;
12975 }
12976
12977 static boolean canFallDown(struct PlayerInfo *player)
12978 {
12979   int jx = player->jx, jy = player->jy;
12980
12981   return (IN_LEV_FIELD(jx, jy + 1) &&
12982           (IS_FREE(jx, jy + 1) ||
12983            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12984           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12985           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12986 }
12987
12988 static boolean canPassField(int x, int y, int move_dir)
12989 {
12990   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12991   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12992   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12993   int nextx = x + dx;
12994   int nexty = y + dy;
12995   int element = Tile[x][y];
12996
12997   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12998           !CAN_MOVE(element) &&
12999           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13000           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13001           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13002 }
13003
13004 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13005 {
13006   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13007   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13008   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13009   int newx = x + dx;
13010   int newy = y + dy;
13011
13012   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13013           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13014           (IS_DIGGABLE(Tile[newx][newy]) ||
13015            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13016            canPassField(newx, newy, move_dir)));
13017 }
13018
13019 static void CheckGravityMovement(struct PlayerInfo *player)
13020 {
13021   if (player->gravity && !player->programmed_action)
13022   {
13023     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13024     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13025     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13026     int jx = player->jx, jy = player->jy;
13027     boolean player_is_moving_to_valid_field =
13028       (!player_is_snapping &&
13029        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13030         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13031     boolean player_can_fall_down = canFallDown(player);
13032
13033     if (player_can_fall_down &&
13034         !player_is_moving_to_valid_field)
13035       player->programmed_action = MV_DOWN;
13036   }
13037 }
13038
13039 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13040 {
13041   return CheckGravityMovement(player);
13042
13043   if (player->gravity && !player->programmed_action)
13044   {
13045     int jx = player->jx, jy = player->jy;
13046     boolean field_under_player_is_free =
13047       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13048     boolean player_is_standing_on_valid_field =
13049       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13050        (IS_WALKABLE(Tile[jx][jy]) &&
13051         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13052
13053     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13054       player->programmed_action = MV_DOWN;
13055   }
13056 }
13057
13058 /*
13059   MovePlayerOneStep()
13060   -----------------------------------------------------------------------------
13061   dx, dy:               direction (non-diagonal) to try to move the player to
13062   real_dx, real_dy:     direction as read from input device (can be diagonal)
13063 */
13064
13065 boolean MovePlayerOneStep(struct PlayerInfo *player,
13066                           int dx, int dy, int real_dx, int real_dy)
13067 {
13068   int jx = player->jx, jy = player->jy;
13069   int new_jx = jx + dx, new_jy = jy + dy;
13070   int can_move;
13071   boolean player_can_move = !player->cannot_move;
13072
13073   if (!player->active || (!dx && !dy))
13074     return MP_NO_ACTION;
13075
13076   player->MovDir = (dx < 0 ? MV_LEFT :
13077                     dx > 0 ? MV_RIGHT :
13078                     dy < 0 ? MV_UP :
13079                     dy > 0 ? MV_DOWN :  MV_NONE);
13080
13081   if (!IN_LEV_FIELD(new_jx, new_jy))
13082     return MP_NO_ACTION;
13083
13084   if (!player_can_move)
13085   {
13086     if (player->MovPos == 0)
13087     {
13088       player->is_moving = FALSE;
13089       player->is_digging = FALSE;
13090       player->is_collecting = FALSE;
13091       player->is_snapping = FALSE;
13092       player->is_pushing = FALSE;
13093     }
13094   }
13095
13096   if (!network.enabled && game.centered_player_nr == -1 &&
13097       !AllPlayersInSight(player, new_jx, new_jy))
13098     return MP_NO_ACTION;
13099
13100   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13101   if (can_move != MP_MOVING)
13102     return can_move;
13103
13104   // check if DigField() has caused relocation of the player
13105   if (player->jx != jx || player->jy != jy)
13106     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13107
13108   StorePlayer[jx][jy] = 0;
13109   player->last_jx = jx;
13110   player->last_jy = jy;
13111   player->jx = new_jx;
13112   player->jy = new_jy;
13113   StorePlayer[new_jx][new_jy] = player->element_nr;
13114
13115   if (player->move_delay_value_next != -1)
13116   {
13117     player->move_delay_value = player->move_delay_value_next;
13118     player->move_delay_value_next = -1;
13119   }
13120
13121   player->MovPos =
13122     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13123
13124   player->step_counter++;
13125
13126   PlayerVisit[jx][jy] = FrameCounter;
13127
13128   player->is_moving = TRUE;
13129
13130 #if 1
13131   // should better be called in MovePlayer(), but this breaks some tapes
13132   ScrollPlayer(player, SCROLL_INIT);
13133 #endif
13134
13135   return MP_MOVING;
13136 }
13137
13138 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13139 {
13140   int jx = player->jx, jy = player->jy;
13141   int old_jx = jx, old_jy = jy;
13142   int moved = MP_NO_ACTION;
13143
13144   if (!player->active)
13145     return FALSE;
13146
13147   if (!dx && !dy)
13148   {
13149     if (player->MovPos == 0)
13150     {
13151       player->is_moving = FALSE;
13152       player->is_digging = FALSE;
13153       player->is_collecting = FALSE;
13154       player->is_snapping = FALSE;
13155       player->is_pushing = FALSE;
13156     }
13157
13158     return FALSE;
13159   }
13160
13161   if (player->move_delay > 0)
13162     return FALSE;
13163
13164   player->move_delay = -1;              // set to "uninitialized" value
13165
13166   // store if player is automatically moved to next field
13167   player->is_auto_moving = (player->programmed_action != MV_NONE);
13168
13169   // remove the last programmed player action
13170   player->programmed_action = 0;
13171
13172   if (player->MovPos)
13173   {
13174     // should only happen if pre-1.2 tape recordings are played
13175     // this is only for backward compatibility
13176
13177     int original_move_delay_value = player->move_delay_value;
13178
13179 #if DEBUG
13180     Debug("game:playing:MovePlayer",
13181           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13182           tape.counter);
13183 #endif
13184
13185     // scroll remaining steps with finest movement resolution
13186     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13187
13188     while (player->MovPos)
13189     {
13190       ScrollPlayer(player, SCROLL_GO_ON);
13191       ScrollScreen(NULL, SCROLL_GO_ON);
13192
13193       AdvanceFrameAndPlayerCounters(player->index_nr);
13194
13195       DrawAllPlayers();
13196       BackToFront_WithFrameDelay(0);
13197     }
13198
13199     player->move_delay_value = original_move_delay_value;
13200   }
13201
13202   player->is_active = FALSE;
13203
13204   if (player->last_move_dir & MV_HORIZONTAL)
13205   {
13206     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13207       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13208   }
13209   else
13210   {
13211     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13212       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13213   }
13214
13215   if (!moved && !player->is_active)
13216   {
13217     player->is_moving = FALSE;
13218     player->is_digging = FALSE;
13219     player->is_collecting = FALSE;
13220     player->is_snapping = FALSE;
13221     player->is_pushing = FALSE;
13222   }
13223
13224   jx = player->jx;
13225   jy = player->jy;
13226
13227   if (moved & MP_MOVING && !ScreenMovPos &&
13228       (player->index_nr == game.centered_player_nr ||
13229        game.centered_player_nr == -1))
13230   {
13231     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13232
13233     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13234     {
13235       // actual player has left the screen -- scroll in that direction
13236       if (jx != old_jx)         // player has moved horizontally
13237         scroll_x += (jx - old_jx);
13238       else                      // player has moved vertically
13239         scroll_y += (jy - old_jy);
13240     }
13241     else
13242     {
13243       int offset_raw = game.scroll_delay_value;
13244
13245       if (jx != old_jx)         // player has moved horizontally
13246       {
13247         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13248         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13249         int new_scroll_x = jx - MIDPOSX + offset_x;
13250
13251         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13252             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13253           scroll_x = new_scroll_x;
13254
13255         // don't scroll over playfield boundaries
13256         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13257
13258         // don't scroll more than one field at a time
13259         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13260
13261         // don't scroll against the player's moving direction
13262         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13263             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13264           scroll_x = old_scroll_x;
13265       }
13266       else                      // player has moved vertically
13267       {
13268         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13269         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13270         int new_scroll_y = jy - MIDPOSY + offset_y;
13271
13272         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13273             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13274           scroll_y = new_scroll_y;
13275
13276         // don't scroll over playfield boundaries
13277         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13278
13279         // don't scroll more than one field at a time
13280         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13281
13282         // don't scroll against the player's moving direction
13283         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13284             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13285           scroll_y = old_scroll_y;
13286       }
13287     }
13288
13289     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13290     {
13291       if (!network.enabled && game.centered_player_nr == -1 &&
13292           !AllPlayersInVisibleScreen())
13293       {
13294         scroll_x = old_scroll_x;
13295         scroll_y = old_scroll_y;
13296       }
13297       else
13298       {
13299         ScrollScreen(player, SCROLL_INIT);
13300         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13301       }
13302     }
13303   }
13304
13305   player->StepFrame = 0;
13306
13307   if (moved & MP_MOVING)
13308   {
13309     if (old_jx != jx && old_jy == jy)
13310       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13311     else if (old_jx == jx && old_jy != jy)
13312       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13313
13314     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13315
13316     player->last_move_dir = player->MovDir;
13317     player->is_moving = TRUE;
13318     player->is_snapping = FALSE;
13319     player->is_switching = FALSE;
13320     player->is_dropping = FALSE;
13321     player->is_dropping_pressed = FALSE;
13322     player->drop_pressed_delay = 0;
13323
13324 #if 0
13325     // should better be called here than above, but this breaks some tapes
13326     ScrollPlayer(player, SCROLL_INIT);
13327 #endif
13328   }
13329   else
13330   {
13331     CheckGravityMovementWhenNotMoving(player);
13332
13333     player->is_moving = FALSE;
13334
13335     /* at this point, the player is allowed to move, but cannot move right now
13336        (e.g. because of something blocking the way) -- ensure that the player
13337        is also allowed to move in the next frame (in old versions before 3.1.1,
13338        the player was forced to wait again for eight frames before next try) */
13339
13340     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13341       player->move_delay = 0;   // allow direct movement in the next frame
13342   }
13343
13344   if (player->move_delay == -1)         // not yet initialized by DigField()
13345     player->move_delay = player->move_delay_value;
13346
13347   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13348   {
13349     TestIfPlayerTouchesBadThing(jx, jy);
13350     TestIfPlayerTouchesCustomElement(jx, jy);
13351   }
13352
13353   if (!player->active)
13354     RemovePlayer(player);
13355
13356   return moved;
13357 }
13358
13359 void ScrollPlayer(struct PlayerInfo *player, int mode)
13360 {
13361   int jx = player->jx, jy = player->jy;
13362   int last_jx = player->last_jx, last_jy = player->last_jy;
13363   int move_stepsize = TILEX / player->move_delay_value;
13364
13365   if (!player->active)
13366     return;
13367
13368   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13369     return;
13370
13371   if (mode == SCROLL_INIT)
13372   {
13373     player->actual_frame_counter.count = FrameCounter;
13374     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13375
13376     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13377         Tile[last_jx][last_jy] == EL_EMPTY)
13378     {
13379       int last_field_block_delay = 0;   // start with no blocking at all
13380       int block_delay_adjustment = player->block_delay_adjustment;
13381
13382       // if player blocks last field, add delay for exactly one move
13383       if (player->block_last_field)
13384       {
13385         last_field_block_delay += player->move_delay_value;
13386
13387         // when blocking enabled, prevent moving up despite gravity
13388         if (player->gravity && player->MovDir == MV_UP)
13389           block_delay_adjustment = -1;
13390       }
13391
13392       // add block delay adjustment (also possible when not blocking)
13393       last_field_block_delay += block_delay_adjustment;
13394
13395       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13396       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13397     }
13398
13399     if (player->MovPos != 0)    // player has not yet reached destination
13400       return;
13401   }
13402   else if (!FrameReached(&player->actual_frame_counter))
13403     return;
13404
13405   if (player->MovPos != 0)
13406   {
13407     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13408     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13409
13410     // before DrawPlayer() to draw correct player graphic for this case
13411     if (player->MovPos == 0)
13412       CheckGravityMovement(player);
13413   }
13414
13415   if (player->MovPos == 0)      // player reached destination field
13416   {
13417     if (player->move_delay_reset_counter > 0)
13418     {
13419       player->move_delay_reset_counter--;
13420
13421       if (player->move_delay_reset_counter == 0)
13422       {
13423         // continue with normal speed after quickly moving through gate
13424         HALVE_PLAYER_SPEED(player);
13425
13426         // be able to make the next move without delay
13427         player->move_delay = 0;
13428       }
13429     }
13430
13431     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13432         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13433         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13434         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13435         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13436         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13437         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13438         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13439     {
13440       ExitPlayer(player);
13441
13442       if (game.players_still_needed == 0 &&
13443           (game.friends_still_needed == 0 ||
13444            IS_SP_ELEMENT(Tile[jx][jy])))
13445         LevelSolved();
13446     }
13447
13448     player->last_jx = jx;
13449     player->last_jy = jy;
13450
13451     // this breaks one level: "machine", level 000
13452     {
13453       int move_direction = player->MovDir;
13454       int enter_side = MV_DIR_OPPOSITE(move_direction);
13455       int leave_side = move_direction;
13456       int old_jx = last_jx;
13457       int old_jy = last_jy;
13458       int old_element = Tile[old_jx][old_jy];
13459       int new_element = Tile[jx][jy];
13460
13461       if (IS_CUSTOM_ELEMENT(old_element))
13462         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13463                                    CE_LEFT_BY_PLAYER,
13464                                    player->index_bit, leave_side);
13465
13466       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13467                                           CE_PLAYER_LEAVES_X,
13468                                           player->index_bit, leave_side);
13469
13470       // needed because pushed element has not yet reached its destination,
13471       // so it would trigger a change event at its previous field location
13472       if (!player->is_pushing)
13473       {
13474         if (IS_CUSTOM_ELEMENT(new_element))
13475           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13476                                      player->index_bit, enter_side);
13477
13478         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13479                                             CE_PLAYER_ENTERS_X,
13480                                             player->index_bit, enter_side);
13481       }
13482
13483       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13484                                         CE_MOVE_OF_X, move_direction);
13485     }
13486
13487     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13488     {
13489       TestIfPlayerTouchesBadThing(jx, jy);
13490       TestIfPlayerTouchesCustomElement(jx, jy);
13491
13492       // needed because pushed element has not yet reached its destination,
13493       // so it would trigger a change event at its previous field location
13494       if (!player->is_pushing)
13495         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13496
13497       if (level.finish_dig_collect &&
13498           (player->is_digging || player->is_collecting))
13499       {
13500         int last_element = player->last_removed_element;
13501         int move_direction = player->MovDir;
13502         int enter_side = MV_DIR_OPPOSITE(move_direction);
13503         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13504                             CE_PLAYER_COLLECTS_X);
13505
13506         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13507                                             player->index_bit, enter_side);
13508
13509         player->last_removed_element = EL_UNDEFINED;
13510       }
13511
13512       if (!player->active)
13513         RemovePlayer(player);
13514     }
13515
13516     if (level.use_step_counter)
13517       CheckLevelTime_StepCounter();
13518
13519     if (tape.single_step && tape.recording && !tape.pausing &&
13520         !player->programmed_action)
13521       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13522
13523     if (!player->programmed_action)
13524       CheckSaveEngineSnapshot(player);
13525   }
13526 }
13527
13528 void ScrollScreen(struct PlayerInfo *player, int mode)
13529 {
13530   static DelayCounter screen_frame_counter = { 0 };
13531
13532   if (mode == SCROLL_INIT)
13533   {
13534     // set scrolling step size according to actual player's moving speed
13535     ScrollStepSize = TILEX / player->move_delay_value;
13536
13537     screen_frame_counter.count = FrameCounter;
13538     screen_frame_counter.value = 1;
13539
13540     ScreenMovDir = player->MovDir;
13541     ScreenMovPos = player->MovPos;
13542     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13543     return;
13544   }
13545   else if (!FrameReached(&screen_frame_counter))
13546     return;
13547
13548   if (ScreenMovPos)
13549   {
13550     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13551     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13552     redraw_mask |= REDRAW_FIELD;
13553   }
13554   else
13555     ScreenMovDir = MV_NONE;
13556 }
13557
13558 void CheckNextToConditions(int x, int y)
13559 {
13560   int element = Tile[x][y];
13561
13562   if (IS_PLAYER(x, y))
13563     TestIfPlayerNextToCustomElement(x, y);
13564
13565   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13566       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13567     TestIfElementNextToCustomElement(x, y);
13568 }
13569
13570 void TestIfPlayerNextToCustomElement(int x, int y)
13571 {
13572   struct XY *xy = xy_topdown;
13573   static int trigger_sides[4][2] =
13574   {
13575     // center side       border side
13576     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13577     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13578     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13579     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13580   };
13581   int i;
13582
13583   if (!IS_PLAYER(x, y))
13584     return;
13585
13586   struct PlayerInfo *player = PLAYERINFO(x, y);
13587
13588   if (player->is_moving)
13589     return;
13590
13591   for (i = 0; i < NUM_DIRECTIONS; i++)
13592   {
13593     int xx = x + xy[i].x;
13594     int yy = y + xy[i].y;
13595     int border_side = trigger_sides[i][1];
13596     int border_element;
13597
13598     if (!IN_LEV_FIELD(xx, yy))
13599       continue;
13600
13601     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13602       continue;         // center and border element not connected
13603
13604     border_element = Tile[xx][yy];
13605
13606     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13607                                player->index_bit, border_side);
13608     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13609                                         CE_PLAYER_NEXT_TO_X,
13610                                         player->index_bit, border_side);
13611
13612     /* use player element that is initially defined in the level playfield,
13613        not the player element that corresponds to the runtime player number
13614        (example: a level that contains EL_PLAYER_3 as the only player would
13615        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13616
13617     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13618                              CE_NEXT_TO_X, border_side);
13619   }
13620 }
13621
13622 void TestIfPlayerTouchesCustomElement(int x, int y)
13623 {
13624   struct XY *xy = xy_topdown;
13625   static int trigger_sides[4][2] =
13626   {
13627     // center side       border side
13628     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13629     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13630     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13631     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13632   };
13633   static int touch_dir[4] =
13634   {
13635     MV_LEFT | MV_RIGHT,
13636     MV_UP   | MV_DOWN,
13637     MV_UP   | MV_DOWN,
13638     MV_LEFT | MV_RIGHT
13639   };
13640   int center_element = Tile[x][y];      // should always be non-moving!
13641   int i;
13642
13643   for (i = 0; i < NUM_DIRECTIONS; i++)
13644   {
13645     int xx = x + xy[i].x;
13646     int yy = y + xy[i].y;
13647     int center_side = trigger_sides[i][0];
13648     int border_side = trigger_sides[i][1];
13649     int border_element;
13650
13651     if (!IN_LEV_FIELD(xx, yy))
13652       continue;
13653
13654     if (IS_PLAYER(x, y))                // player found at center element
13655     {
13656       struct PlayerInfo *player = PLAYERINFO(x, y);
13657
13658       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13659         border_element = Tile[xx][yy];          // may be moving!
13660       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13661         border_element = Tile[xx][yy];
13662       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13663         border_element = MovingOrBlocked2Element(xx, yy);
13664       else
13665         continue;               // center and border element do not touch
13666
13667       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13668                                  player->index_bit, border_side);
13669       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13670                                           CE_PLAYER_TOUCHES_X,
13671                                           player->index_bit, border_side);
13672
13673       {
13674         /* use player element that is initially defined in the level playfield,
13675            not the player element that corresponds to the runtime player number
13676            (example: a level that contains EL_PLAYER_3 as the only player would
13677            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13678         int player_element = PLAYERINFO(x, y)->initial_element;
13679
13680         // as element "X" is the player here, check opposite (center) side
13681         CheckElementChangeBySide(xx, yy, border_element, player_element,
13682                                  CE_TOUCHING_X, center_side);
13683       }
13684     }
13685     else if (IS_PLAYER(xx, yy))         // player found at border element
13686     {
13687       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13688
13689       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13690       {
13691         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13692           continue;             // center and border element do not touch
13693       }
13694
13695       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13696                                  player->index_bit, center_side);
13697       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13698                                           CE_PLAYER_TOUCHES_X,
13699                                           player->index_bit, center_side);
13700
13701       {
13702         /* use player element that is initially defined in the level playfield,
13703            not the player element that corresponds to the runtime player number
13704            (example: a level that contains EL_PLAYER_3 as the only player would
13705            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13706         int player_element = PLAYERINFO(xx, yy)->initial_element;
13707
13708         // as element "X" is the player here, check opposite (border) side
13709         CheckElementChangeBySide(x, y, center_element, player_element,
13710                                  CE_TOUCHING_X, border_side);
13711       }
13712
13713       break;
13714     }
13715   }
13716 }
13717
13718 void TestIfElementNextToCustomElement(int x, int y)
13719 {
13720   struct XY *xy = xy_topdown;
13721   static int trigger_sides[4][2] =
13722   {
13723     // center side      border side
13724     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13725     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13726     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13727     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13728   };
13729   int center_element = Tile[x][y];      // should always be non-moving!
13730   int i;
13731
13732   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13733     return;
13734
13735   for (i = 0; i < NUM_DIRECTIONS; i++)
13736   {
13737     int xx = x + xy[i].x;
13738     int yy = y + xy[i].y;
13739     int border_side = trigger_sides[i][1];
13740     int border_element;
13741
13742     if (!IN_LEV_FIELD(xx, yy))
13743       continue;
13744
13745     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13746       continue;                 // center and border element not connected
13747
13748     border_element = Tile[xx][yy];
13749
13750     // check for change of center element (but change it only once)
13751     if (CheckElementChangeBySide(x, y, center_element, border_element,
13752                                  CE_NEXT_TO_X, border_side))
13753       break;
13754   }
13755 }
13756
13757 void TestIfElementTouchesCustomElement(int x, int y)
13758 {
13759   struct XY *xy = xy_topdown;
13760   static int trigger_sides[4][2] =
13761   {
13762     // center side      border side
13763     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13764     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13765     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13766     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13767   };
13768   static int touch_dir[4] =
13769   {
13770     MV_LEFT | MV_RIGHT,
13771     MV_UP   | MV_DOWN,
13772     MV_UP   | MV_DOWN,
13773     MV_LEFT | MV_RIGHT
13774   };
13775   boolean change_center_element = FALSE;
13776   int center_element = Tile[x][y];      // should always be non-moving!
13777   int border_element_old[NUM_DIRECTIONS];
13778   int i;
13779
13780   for (i = 0; i < NUM_DIRECTIONS; i++)
13781   {
13782     int xx = x + xy[i].x;
13783     int yy = y + xy[i].y;
13784     int border_element;
13785
13786     border_element_old[i] = -1;
13787
13788     if (!IN_LEV_FIELD(xx, yy))
13789       continue;
13790
13791     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13792       border_element = Tile[xx][yy];    // may be moving!
13793     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13794       border_element = Tile[xx][yy];
13795     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13796       border_element = MovingOrBlocked2Element(xx, yy);
13797     else
13798       continue;                 // center and border element do not touch
13799
13800     border_element_old[i] = border_element;
13801   }
13802
13803   for (i = 0; i < NUM_DIRECTIONS; i++)
13804   {
13805     int xx = x + xy[i].x;
13806     int yy = y + xy[i].y;
13807     int center_side = trigger_sides[i][0];
13808     int border_element = border_element_old[i];
13809
13810     if (border_element == -1)
13811       continue;
13812
13813     // check for change of border element
13814     CheckElementChangeBySide(xx, yy, border_element, center_element,
13815                              CE_TOUCHING_X, center_side);
13816
13817     // (center element cannot be player, so we don't have to check this here)
13818   }
13819
13820   for (i = 0; i < NUM_DIRECTIONS; i++)
13821   {
13822     int xx = x + xy[i].x;
13823     int yy = y + xy[i].y;
13824     int border_side = trigger_sides[i][1];
13825     int border_element = border_element_old[i];
13826
13827     if (border_element == -1)
13828       continue;
13829
13830     // check for change of center element (but change it only once)
13831     if (!change_center_element)
13832       change_center_element =
13833         CheckElementChangeBySide(x, y, center_element, border_element,
13834                                  CE_TOUCHING_X, border_side);
13835
13836     if (IS_PLAYER(xx, yy))
13837     {
13838       /* use player element that is initially defined in the level playfield,
13839          not the player element that corresponds to the runtime player number
13840          (example: a level that contains EL_PLAYER_3 as the only player would
13841          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13842       int player_element = PLAYERINFO(xx, yy)->initial_element;
13843
13844       // as element "X" is the player here, check opposite (border) side
13845       CheckElementChangeBySide(x, y, center_element, player_element,
13846                                CE_TOUCHING_X, border_side);
13847     }
13848   }
13849 }
13850
13851 void TestIfElementHitsCustomElement(int x, int y, int direction)
13852 {
13853   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13854   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13855   int hitx = x + dx, hity = y + dy;
13856   int hitting_element = Tile[x][y];
13857   int touched_element;
13858
13859   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13860     return;
13861
13862   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13863                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13864
13865   if (IN_LEV_FIELD(hitx, hity))
13866   {
13867     int opposite_direction = MV_DIR_OPPOSITE(direction);
13868     int hitting_side = direction;
13869     int touched_side = opposite_direction;
13870     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13871                           MovDir[hitx][hity] != direction ||
13872                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13873
13874     object_hit = TRUE;
13875
13876     if (object_hit)
13877     {
13878       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13879                                CE_HITTING_X, touched_side);
13880
13881       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13882                                CE_HIT_BY_X, hitting_side);
13883
13884       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13885                                CE_HIT_BY_SOMETHING, opposite_direction);
13886
13887       if (IS_PLAYER(hitx, hity))
13888       {
13889         /* use player element that is initially defined in the level playfield,
13890            not the player element that corresponds to the runtime player number
13891            (example: a level that contains EL_PLAYER_3 as the only player would
13892            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13893         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13894
13895         CheckElementChangeBySide(x, y, hitting_element, player_element,
13896                                  CE_HITTING_X, touched_side);
13897       }
13898     }
13899   }
13900
13901   // "hitting something" is also true when hitting the playfield border
13902   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13903                            CE_HITTING_SOMETHING, direction);
13904 }
13905
13906 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13907 {
13908   int i, kill_x = -1, kill_y = -1;
13909
13910   int bad_element = -1;
13911   struct XY *test_xy = xy_topdown;
13912   static int test_dir[4] =
13913   {
13914     MV_UP,
13915     MV_LEFT,
13916     MV_RIGHT,
13917     MV_DOWN
13918   };
13919
13920   for (i = 0; i < NUM_DIRECTIONS; i++)
13921   {
13922     int test_x, test_y, test_move_dir, test_element;
13923
13924     test_x = good_x + test_xy[i].x;
13925     test_y = good_y + test_xy[i].y;
13926
13927     if (!IN_LEV_FIELD(test_x, test_y))
13928       continue;
13929
13930     test_move_dir =
13931       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13932
13933     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13934
13935     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13936        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13937     */
13938     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13939         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13940     {
13941       kill_x = test_x;
13942       kill_y = test_y;
13943       bad_element = test_element;
13944
13945       break;
13946     }
13947   }
13948
13949   if (kill_x != -1 || kill_y != -1)
13950   {
13951     if (IS_PLAYER(good_x, good_y))
13952     {
13953       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13954
13955       if (player->shield_deadly_time_left > 0 &&
13956           !IS_INDESTRUCTIBLE(bad_element))
13957         Bang(kill_x, kill_y);
13958       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13959         KillPlayer(player);
13960     }
13961     else
13962       Bang(good_x, good_y);
13963   }
13964 }
13965
13966 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13967 {
13968   int i, kill_x = -1, kill_y = -1;
13969   int bad_element = Tile[bad_x][bad_y];
13970   struct XY *test_xy = xy_topdown;
13971   static int touch_dir[4] =
13972   {
13973     MV_LEFT | MV_RIGHT,
13974     MV_UP   | MV_DOWN,
13975     MV_UP   | MV_DOWN,
13976     MV_LEFT | MV_RIGHT
13977   };
13978   static int test_dir[4] =
13979   {
13980     MV_UP,
13981     MV_LEFT,
13982     MV_RIGHT,
13983     MV_DOWN
13984   };
13985
13986   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13987     return;
13988
13989   for (i = 0; i < NUM_DIRECTIONS; i++)
13990   {
13991     int test_x, test_y, test_move_dir, test_element;
13992
13993     test_x = bad_x + test_xy[i].x;
13994     test_y = bad_y + test_xy[i].y;
13995
13996     if (!IN_LEV_FIELD(test_x, test_y))
13997       continue;
13998
13999     test_move_dir =
14000       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14001
14002     test_element = Tile[test_x][test_y];
14003
14004     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14005        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14006     */
14007     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14008         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14009     {
14010       // good thing is player or penguin that does not move away
14011       if (IS_PLAYER(test_x, test_y))
14012       {
14013         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14014
14015         if (bad_element == EL_ROBOT && player->is_moving)
14016           continue;     // robot does not kill player if he is moving
14017
14018         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14019         {
14020           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14021             continue;           // center and border element do not touch
14022         }
14023
14024         kill_x = test_x;
14025         kill_y = test_y;
14026
14027         break;
14028       }
14029       else if (test_element == EL_PENGUIN)
14030       {
14031         kill_x = test_x;
14032         kill_y = test_y;
14033
14034         break;
14035       }
14036     }
14037   }
14038
14039   if (kill_x != -1 || kill_y != -1)
14040   {
14041     if (IS_PLAYER(kill_x, kill_y))
14042     {
14043       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14044
14045       if (player->shield_deadly_time_left > 0 &&
14046           !IS_INDESTRUCTIBLE(bad_element))
14047         Bang(bad_x, bad_y);
14048       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14049         KillPlayer(player);
14050     }
14051     else
14052       Bang(kill_x, kill_y);
14053   }
14054 }
14055
14056 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14057 {
14058   int bad_element = Tile[bad_x][bad_y];
14059   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14060   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14061   int test_x = bad_x + dx, test_y = bad_y + dy;
14062   int test_move_dir, test_element;
14063   int kill_x = -1, kill_y = -1;
14064
14065   if (!IN_LEV_FIELD(test_x, test_y))
14066     return;
14067
14068   test_move_dir =
14069     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14070
14071   test_element = Tile[test_x][test_y];
14072
14073   if (test_move_dir != bad_move_dir)
14074   {
14075     // good thing can be player or penguin that does not move away
14076     if (IS_PLAYER(test_x, test_y))
14077     {
14078       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14079
14080       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14081          player as being hit when he is moving towards the bad thing, because
14082          the "get hit by" condition would be lost after the player stops) */
14083       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14084         return;         // player moves away from bad thing
14085
14086       kill_x = test_x;
14087       kill_y = test_y;
14088     }
14089     else if (test_element == EL_PENGUIN)
14090     {
14091       kill_x = test_x;
14092       kill_y = test_y;
14093     }
14094   }
14095
14096   if (kill_x != -1 || kill_y != -1)
14097   {
14098     if (IS_PLAYER(kill_x, kill_y))
14099     {
14100       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14101
14102       if (player->shield_deadly_time_left > 0 &&
14103           !IS_INDESTRUCTIBLE(bad_element))
14104         Bang(bad_x, bad_y);
14105       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14106         KillPlayer(player);
14107     }
14108     else
14109       Bang(kill_x, kill_y);
14110   }
14111 }
14112
14113 void TestIfPlayerTouchesBadThing(int x, int y)
14114 {
14115   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14116 }
14117
14118 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14119 {
14120   TestIfGoodThingHitsBadThing(x, y, move_dir);
14121 }
14122
14123 void TestIfBadThingTouchesPlayer(int x, int y)
14124 {
14125   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14126 }
14127
14128 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14129 {
14130   TestIfBadThingHitsGoodThing(x, y, move_dir);
14131 }
14132
14133 void TestIfFriendTouchesBadThing(int x, int y)
14134 {
14135   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14136 }
14137
14138 void TestIfBadThingTouchesFriend(int x, int y)
14139 {
14140   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14141 }
14142
14143 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14144 {
14145   int i, kill_x = bad_x, kill_y = bad_y;
14146   struct XY *xy = xy_topdown;
14147
14148   for (i = 0; i < NUM_DIRECTIONS; i++)
14149   {
14150     int x, y, element;
14151
14152     x = bad_x + xy[i].x;
14153     y = bad_y + xy[i].y;
14154     if (!IN_LEV_FIELD(x, y))
14155       continue;
14156
14157     element = Tile[x][y];
14158     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14159         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14160     {
14161       kill_x = x;
14162       kill_y = y;
14163       break;
14164     }
14165   }
14166
14167   if (kill_x != bad_x || kill_y != bad_y)
14168     Bang(bad_x, bad_y);
14169 }
14170
14171 void KillPlayer(struct PlayerInfo *player)
14172 {
14173   int jx = player->jx, jy = player->jy;
14174
14175   if (!player->active)
14176     return;
14177
14178 #if 0
14179   Debug("game:playing:KillPlayer",
14180         "0: killed == %d, active == %d, reanimated == %d",
14181         player->killed, player->active, player->reanimated);
14182 #endif
14183
14184   /* the following code was introduced to prevent an infinite loop when calling
14185      -> Bang()
14186      -> CheckTriggeredElementChangeExt()
14187      -> ExecuteCustomElementAction()
14188      -> KillPlayer()
14189      -> (infinitely repeating the above sequence of function calls)
14190      which occurs when killing the player while having a CE with the setting
14191      "kill player X when explosion of <player X>"; the solution using a new
14192      field "player->killed" was chosen for backwards compatibility, although
14193      clever use of the fields "player->active" etc. would probably also work */
14194 #if 1
14195   if (player->killed)
14196     return;
14197 #endif
14198
14199   player->killed = TRUE;
14200
14201   // remove accessible field at the player's position
14202   RemoveField(jx, jy);
14203
14204   // deactivate shield (else Bang()/Explode() would not work right)
14205   player->shield_normal_time_left = 0;
14206   player->shield_deadly_time_left = 0;
14207
14208 #if 0
14209   Debug("game:playing:KillPlayer",
14210         "1: killed == %d, active == %d, reanimated == %d",
14211         player->killed, player->active, player->reanimated);
14212 #endif
14213
14214   Bang(jx, jy);
14215
14216 #if 0
14217   Debug("game:playing:KillPlayer",
14218         "2: killed == %d, active == %d, reanimated == %d",
14219         player->killed, player->active, player->reanimated);
14220 #endif
14221
14222   if (player->reanimated)       // killed player may have been reanimated
14223     player->killed = player->reanimated = FALSE;
14224   else
14225     BuryPlayer(player);
14226 }
14227
14228 static void KillPlayerUnlessEnemyProtected(int x, int y)
14229 {
14230   if (!PLAYER_ENEMY_PROTECTED(x, y))
14231     KillPlayer(PLAYERINFO(x, y));
14232 }
14233
14234 static void KillPlayerUnlessExplosionProtected(int x, int y)
14235 {
14236   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14237     KillPlayer(PLAYERINFO(x, y));
14238 }
14239
14240 void BuryPlayer(struct PlayerInfo *player)
14241 {
14242   int jx = player->jx, jy = player->jy;
14243
14244   if (!player->active)
14245     return;
14246
14247   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14248
14249   RemovePlayer(player);
14250
14251   player->buried = TRUE;
14252
14253   if (game.all_players_gone)
14254     game.GameOver = TRUE;
14255 }
14256
14257 void RemovePlayer(struct PlayerInfo *player)
14258 {
14259   int jx = player->jx, jy = player->jy;
14260   int i, found = FALSE;
14261
14262   player->present = FALSE;
14263   player->active = FALSE;
14264
14265   // required for some CE actions (even if the player is not active anymore)
14266   player->MovPos = 0;
14267
14268   if (!ExplodeField[jx][jy])
14269     StorePlayer[jx][jy] = 0;
14270
14271   if (player->is_moving)
14272     TEST_DrawLevelField(player->last_jx, player->last_jy);
14273
14274   for (i = 0; i < MAX_PLAYERS; i++)
14275     if (stored_player[i].active)
14276       found = TRUE;
14277
14278   if (!found)
14279   {
14280     game.all_players_gone = TRUE;
14281     game.GameOver = TRUE;
14282   }
14283
14284   game.exit_x = game.robot_wheel_x = jx;
14285   game.exit_y = game.robot_wheel_y = jy;
14286 }
14287
14288 void ExitPlayer(struct PlayerInfo *player)
14289 {
14290   DrawPlayer(player);   // needed here only to cleanup last field
14291   RemovePlayer(player);
14292
14293   if (game.players_still_needed > 0)
14294     game.players_still_needed--;
14295 }
14296
14297 static void SetFieldForSnapping(int x, int y, int element, int direction,
14298                                 int player_index_bit)
14299 {
14300   struct ElementInfo *ei = &element_info[element];
14301   int direction_bit = MV_DIR_TO_BIT(direction);
14302   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14303   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14304                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14305
14306   Tile[x][y] = EL_ELEMENT_SNAPPING;
14307   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14308   MovDir[x][y] = direction;
14309   Store[x][y] = element;
14310   Store2[x][y] = player_index_bit;
14311
14312   ResetGfxAnimation(x, y);
14313
14314   GfxElement[x][y] = element;
14315   GfxAction[x][y] = action;
14316   GfxDir[x][y] = direction;
14317   GfxFrame[x][y] = -1;
14318 }
14319
14320 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14321                                    int player_index_bit)
14322 {
14323   TestIfElementTouchesCustomElement(x, y);      // for empty space
14324
14325   if (level.finish_dig_collect)
14326   {
14327     int dig_side = MV_DIR_OPPOSITE(direction);
14328     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14329                         CE_PLAYER_COLLECTS_X);
14330
14331     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14332                                         player_index_bit, dig_side);
14333     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14334                                         player_index_bit, dig_side);
14335   }
14336 }
14337
14338 /*
14339   =============================================================================
14340   checkDiagonalPushing()
14341   -----------------------------------------------------------------------------
14342   check if diagonal input device direction results in pushing of object
14343   (by checking if the alternative direction is walkable, diggable, ...)
14344   =============================================================================
14345 */
14346
14347 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14348                                     int x, int y, int real_dx, int real_dy)
14349 {
14350   int jx, jy, dx, dy, xx, yy;
14351
14352   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14353     return TRUE;
14354
14355   // diagonal direction: check alternative direction
14356   jx = player->jx;
14357   jy = player->jy;
14358   dx = x - jx;
14359   dy = y - jy;
14360   xx = jx + (dx == 0 ? real_dx : 0);
14361   yy = jy + (dy == 0 ? real_dy : 0);
14362
14363   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14364 }
14365
14366 /*
14367   =============================================================================
14368   DigField()
14369   -----------------------------------------------------------------------------
14370   x, y:                 field next to player (non-diagonal) to try to dig to
14371   real_dx, real_dy:     direction as read from input device (can be diagonal)
14372   =============================================================================
14373 */
14374
14375 static int DigField(struct PlayerInfo *player,
14376                     int oldx, int oldy, int x, int y,
14377                     int real_dx, int real_dy, int mode)
14378 {
14379   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14380   boolean player_was_pushing = player->is_pushing;
14381   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14382   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14383   int jx = oldx, jy = oldy;
14384   int dx = x - jx, dy = y - jy;
14385   int nextx = x + dx, nexty = y + dy;
14386   int move_direction = (dx == -1 ? MV_LEFT  :
14387                         dx == +1 ? MV_RIGHT :
14388                         dy == -1 ? MV_UP    :
14389                         dy == +1 ? MV_DOWN  : MV_NONE);
14390   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14391   int dig_side = MV_DIR_OPPOSITE(move_direction);
14392   int old_element = Tile[jx][jy];
14393   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14394   int collect_count;
14395
14396   if (is_player)                // function can also be called by EL_PENGUIN
14397   {
14398     if (player->MovPos == 0)
14399     {
14400       player->is_digging = FALSE;
14401       player->is_collecting = FALSE;
14402     }
14403
14404     if (player->MovPos == 0)    // last pushing move finished
14405       player->is_pushing = FALSE;
14406
14407     if (mode == DF_NO_PUSH)     // player just stopped pushing
14408     {
14409       player->is_switching = FALSE;
14410       player->push_delay = -1;
14411
14412       return MP_NO_ACTION;
14413     }
14414   }
14415   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14416     old_element = Back[jx][jy];
14417
14418   // in case of element dropped at player position, check background
14419   else if (Back[jx][jy] != EL_EMPTY &&
14420            game.engine_version >= VERSION_IDENT(2,2,0,0))
14421     old_element = Back[jx][jy];
14422
14423   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14424     return MP_NO_ACTION;        // field has no opening in this direction
14425
14426   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14427     return MP_NO_ACTION;        // field has no opening in this direction
14428
14429   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14430   {
14431     SplashAcid(x, y);
14432
14433     Tile[jx][jy] = player->artwork_element;
14434     InitMovingField(jx, jy, MV_DOWN);
14435     Store[jx][jy] = EL_ACID;
14436     ContinueMoving(jx, jy);
14437     BuryPlayer(player);
14438
14439     return MP_DONT_RUN_INTO;
14440   }
14441
14442   if (player_can_move && DONT_RUN_INTO(element))
14443   {
14444     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14445
14446     return MP_DONT_RUN_INTO;
14447   }
14448
14449   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14450     return MP_NO_ACTION;
14451
14452   collect_count = element_info[element].collect_count_initial;
14453
14454   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14455     return MP_NO_ACTION;
14456
14457   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14458     player_can_move = player_can_move_or_snap;
14459
14460   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14461       game.engine_version >= VERSION_IDENT(2,2,0,0))
14462   {
14463     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14464                                player->index_bit, dig_side);
14465     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14466                                         player->index_bit, dig_side);
14467
14468     if (element == EL_DC_LANDMINE)
14469       Bang(x, y);
14470
14471     if (Tile[x][y] != element)          // field changed by snapping
14472       return MP_ACTION;
14473
14474     return MP_NO_ACTION;
14475   }
14476
14477   if (player->gravity && is_player && !player->is_auto_moving &&
14478       canFallDown(player) && move_direction != MV_DOWN &&
14479       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14480     return MP_NO_ACTION;        // player cannot walk here due to gravity
14481
14482   if (player_can_move &&
14483       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14484   {
14485     int sound_element = SND_ELEMENT(element);
14486     int sound_action = ACTION_WALKING;
14487
14488     if (IS_RND_GATE(element))
14489     {
14490       if (!player->key[RND_GATE_NR(element)])
14491         return MP_NO_ACTION;
14492     }
14493     else if (IS_RND_GATE_GRAY(element))
14494     {
14495       if (!player->key[RND_GATE_GRAY_NR(element)])
14496         return MP_NO_ACTION;
14497     }
14498     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14499     {
14500       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14501         return MP_NO_ACTION;
14502     }
14503     else if (element == EL_EXIT_OPEN ||
14504              element == EL_EM_EXIT_OPEN ||
14505              element == EL_EM_EXIT_OPENING ||
14506              element == EL_STEEL_EXIT_OPEN ||
14507              element == EL_EM_STEEL_EXIT_OPEN ||
14508              element == EL_EM_STEEL_EXIT_OPENING ||
14509              element == EL_SP_EXIT_OPEN ||
14510              element == EL_SP_EXIT_OPENING)
14511     {
14512       sound_action = ACTION_PASSING;    // player is passing exit
14513     }
14514     else if (element == EL_EMPTY)
14515     {
14516       sound_action = ACTION_MOVING;             // nothing to walk on
14517     }
14518
14519     // play sound from background or player, whatever is available
14520     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14521       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14522     else
14523       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14524   }
14525   else if (player_can_move &&
14526            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14527   {
14528     if (!ACCESS_FROM(element, opposite_direction))
14529       return MP_NO_ACTION;      // field not accessible from this direction
14530
14531     if (CAN_MOVE(element))      // only fixed elements can be passed!
14532       return MP_NO_ACTION;
14533
14534     if (IS_EM_GATE(element))
14535     {
14536       if (!player->key[EM_GATE_NR(element)])
14537         return MP_NO_ACTION;
14538     }
14539     else if (IS_EM_GATE_GRAY(element))
14540     {
14541       if (!player->key[EM_GATE_GRAY_NR(element)])
14542         return MP_NO_ACTION;
14543     }
14544     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14545     {
14546       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14547         return MP_NO_ACTION;
14548     }
14549     else if (IS_EMC_GATE(element))
14550     {
14551       if (!player->key[EMC_GATE_NR(element)])
14552         return MP_NO_ACTION;
14553     }
14554     else if (IS_EMC_GATE_GRAY(element))
14555     {
14556       if (!player->key[EMC_GATE_GRAY_NR(element)])
14557         return MP_NO_ACTION;
14558     }
14559     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14560     {
14561       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14562         return MP_NO_ACTION;
14563     }
14564     else if (element == EL_DC_GATE_WHITE ||
14565              element == EL_DC_GATE_WHITE_GRAY ||
14566              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14567     {
14568       if (player->num_white_keys == 0)
14569         return MP_NO_ACTION;
14570
14571       player->num_white_keys--;
14572     }
14573     else if (IS_SP_PORT(element))
14574     {
14575       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14576           element == EL_SP_GRAVITY_PORT_RIGHT ||
14577           element == EL_SP_GRAVITY_PORT_UP ||
14578           element == EL_SP_GRAVITY_PORT_DOWN)
14579         player->gravity = !player->gravity;
14580       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14581                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14582                element == EL_SP_GRAVITY_ON_PORT_UP ||
14583                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14584         player->gravity = TRUE;
14585       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14586                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14587                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14588                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14589         player->gravity = FALSE;
14590     }
14591
14592     // automatically move to the next field with double speed
14593     player->programmed_action = move_direction;
14594
14595     if (player->move_delay_reset_counter == 0)
14596     {
14597       player->move_delay_reset_counter = 2;     // two double speed steps
14598
14599       DOUBLE_PLAYER_SPEED(player);
14600     }
14601
14602     PlayLevelSoundAction(x, y, ACTION_PASSING);
14603   }
14604   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14605   {
14606     RemoveField(x, y);
14607
14608     if (mode != DF_SNAP)
14609     {
14610       GfxElement[x][y] = GFX_ELEMENT(element);
14611       player->is_digging = TRUE;
14612     }
14613
14614     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14615
14616     // use old behaviour for old levels (digging)
14617     if (!level.finish_dig_collect)
14618     {
14619       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14620                                           player->index_bit, dig_side);
14621
14622       // if digging triggered player relocation, finish digging tile
14623       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14624         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14625     }
14626
14627     if (mode == DF_SNAP)
14628     {
14629       if (level.block_snap_field)
14630         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14631       else
14632         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14633
14634       // use old behaviour for old levels (snapping)
14635       if (!level.finish_dig_collect)
14636         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14637                                             player->index_bit, dig_side);
14638     }
14639   }
14640   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14641   {
14642     RemoveField(x, y);
14643
14644     if (is_player && mode != DF_SNAP)
14645     {
14646       GfxElement[x][y] = element;
14647       player->is_collecting = TRUE;
14648     }
14649
14650     if (element == EL_SPEED_PILL)
14651     {
14652       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14653     }
14654     else if (element == EL_EXTRA_TIME && level.time > 0)
14655     {
14656       TimeLeft += level.extra_time;
14657
14658       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14659
14660       DisplayGameControlValues();
14661     }
14662     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14663     {
14664       int shield_time = (element == EL_SHIELD_DEADLY ?
14665                          level.shield_deadly_time :
14666                          level.shield_normal_time);
14667
14668       player->shield_normal_time_left += shield_time;
14669       if (element == EL_SHIELD_DEADLY)
14670         player->shield_deadly_time_left += shield_time;
14671     }
14672     else if (element == EL_DYNAMITE ||
14673              element == EL_EM_DYNAMITE ||
14674              element == EL_SP_DISK_RED)
14675     {
14676       if (player->inventory_size < MAX_INVENTORY_SIZE)
14677         player->inventory_element[player->inventory_size++] = element;
14678
14679       DrawGameDoorValues();
14680     }
14681     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14682     {
14683       player->dynabomb_count++;
14684       player->dynabombs_left++;
14685     }
14686     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14687     {
14688       player->dynabomb_size++;
14689     }
14690     else if (element == EL_DYNABOMB_INCREASE_POWER)
14691     {
14692       player->dynabomb_xl = TRUE;
14693     }
14694     else if (IS_KEY(element))
14695     {
14696       player->key[KEY_NR(element)] = TRUE;
14697
14698       DrawGameDoorValues();
14699     }
14700     else if (element == EL_DC_KEY_WHITE)
14701     {
14702       player->num_white_keys++;
14703
14704       // display white keys?
14705       // DrawGameDoorValues();
14706     }
14707     else if (IS_ENVELOPE(element))
14708     {
14709       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14710
14711       if (!wait_for_snapping)
14712         player->show_envelope = element;
14713     }
14714     else if (element == EL_EMC_LENSES)
14715     {
14716       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14717
14718       RedrawAllInvisibleElementsForLenses();
14719     }
14720     else if (element == EL_EMC_MAGNIFIER)
14721     {
14722       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14723
14724       RedrawAllInvisibleElementsForMagnifier();
14725     }
14726     else if (IS_DROPPABLE(element) ||
14727              IS_THROWABLE(element))     // can be collected and dropped
14728     {
14729       int i;
14730
14731       if (collect_count == 0)
14732         player->inventory_infinite_element = element;
14733       else
14734         for (i = 0; i < collect_count; i++)
14735           if (player->inventory_size < MAX_INVENTORY_SIZE)
14736             player->inventory_element[player->inventory_size++] = element;
14737
14738       DrawGameDoorValues();
14739     }
14740     else if (collect_count > 0)
14741     {
14742       game.gems_still_needed -= collect_count;
14743       if (game.gems_still_needed < 0)
14744         game.gems_still_needed = 0;
14745
14746       game.snapshot.collected_item = TRUE;
14747
14748       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14749
14750       DisplayGameControlValues();
14751     }
14752
14753     RaiseScoreElement(element);
14754     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14755
14756     // use old behaviour for old levels (collecting)
14757     if (!level.finish_dig_collect && is_player)
14758     {
14759       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14760                                           player->index_bit, dig_side);
14761
14762       // if collecting triggered player relocation, finish collecting tile
14763       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14764         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14765     }
14766
14767     if (mode == DF_SNAP)
14768     {
14769       if (level.block_snap_field)
14770         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14771       else
14772         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14773
14774       // use old behaviour for old levels (snapping)
14775       if (!level.finish_dig_collect)
14776         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14777                                             player->index_bit, dig_side);
14778     }
14779   }
14780   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14781   {
14782     if (mode == DF_SNAP && element != EL_BD_ROCK)
14783       return MP_NO_ACTION;
14784
14785     if (CAN_FALL(element) && dy)
14786       return MP_NO_ACTION;
14787
14788     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14789         !(element == EL_SPRING && level.use_spring_bug))
14790       return MP_NO_ACTION;
14791
14792     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14793         ((move_direction & MV_VERTICAL &&
14794           ((element_info[element].move_pattern & MV_LEFT &&
14795             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14796            (element_info[element].move_pattern & MV_RIGHT &&
14797             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14798          (move_direction & MV_HORIZONTAL &&
14799           ((element_info[element].move_pattern & MV_UP &&
14800             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14801            (element_info[element].move_pattern & MV_DOWN &&
14802             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14803       return MP_NO_ACTION;
14804
14805     // do not push elements already moving away faster than player
14806     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14807         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14808       return MP_NO_ACTION;
14809
14810     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14811     {
14812       if (player->push_delay_value == -1 || !player_was_pushing)
14813         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14814     }
14815     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14816     {
14817       if (player->push_delay_value == -1)
14818         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14819     }
14820     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14821     {
14822       if (!player->is_pushing)
14823         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14824     }
14825
14826     player->is_pushing = TRUE;
14827     player->is_active = TRUE;
14828
14829     if (!(IN_LEV_FIELD(nextx, nexty) &&
14830           (IS_FREE(nextx, nexty) ||
14831            (IS_SB_ELEMENT(element) &&
14832             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14833            (IS_CUSTOM_ELEMENT(element) &&
14834             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14835       return MP_NO_ACTION;
14836
14837     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14838       return MP_NO_ACTION;
14839
14840     if (player->push_delay == -1)       // new pushing; restart delay
14841       player->push_delay = 0;
14842
14843     if (player->push_delay < player->push_delay_value &&
14844         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14845         element != EL_SPRING && element != EL_BALLOON)
14846     {
14847       // make sure that there is no move delay before next try to push
14848       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14849         player->move_delay = 0;
14850
14851       return MP_NO_ACTION;
14852     }
14853
14854     if (IS_CUSTOM_ELEMENT(element) &&
14855         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14856     {
14857       if (!DigFieldByCE(nextx, nexty, element))
14858         return MP_NO_ACTION;
14859     }
14860
14861     if (IS_SB_ELEMENT(element))
14862     {
14863       boolean sokoban_task_solved = FALSE;
14864
14865       if (element == EL_SOKOBAN_FIELD_FULL)
14866       {
14867         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14868
14869         IncrementSokobanFieldsNeeded();
14870         IncrementSokobanObjectsNeeded();
14871       }
14872
14873       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14874       {
14875         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14876
14877         DecrementSokobanFieldsNeeded();
14878         DecrementSokobanObjectsNeeded();
14879
14880         // sokoban object was pushed from empty field to sokoban field
14881         if (Back[x][y] == EL_EMPTY)
14882           sokoban_task_solved = TRUE;
14883       }
14884
14885       Tile[x][y] = EL_SOKOBAN_OBJECT;
14886
14887       if (Back[x][y] == Back[nextx][nexty])
14888         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14889       else if (Back[x][y] != 0)
14890         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14891                                     ACTION_EMPTYING);
14892       else
14893         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14894                                     ACTION_FILLING);
14895
14896       if (sokoban_task_solved &&
14897           game.sokoban_fields_still_needed == 0 &&
14898           game.sokoban_objects_still_needed == 0 &&
14899           level.auto_exit_sokoban)
14900       {
14901         game.players_still_needed = 0;
14902
14903         LevelSolved();
14904
14905         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14906       }
14907     }
14908     else
14909       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14910
14911     InitMovingField(x, y, move_direction);
14912     GfxAction[x][y] = ACTION_PUSHING;
14913
14914     if (mode == DF_SNAP)
14915       ContinueMoving(x, y);
14916     else
14917       MovPos[x][y] = (dx != 0 ? dx : dy);
14918
14919     Pushed[x][y] = TRUE;
14920     Pushed[nextx][nexty] = TRUE;
14921
14922     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14923       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14924     else
14925       player->push_delay_value = -1;    // get new value later
14926
14927     // check for element change _after_ element has been pushed
14928     if (game.use_change_when_pushing_bug)
14929     {
14930       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14931                                  player->index_bit, dig_side);
14932       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14933                                           player->index_bit, dig_side);
14934     }
14935   }
14936   else if (IS_SWITCHABLE(element))
14937   {
14938     if (PLAYER_SWITCHING(player, x, y))
14939     {
14940       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14941                                           player->index_bit, dig_side);
14942
14943       return MP_ACTION;
14944     }
14945
14946     player->is_switching = TRUE;
14947     player->switch_x = x;
14948     player->switch_y = y;
14949
14950     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14951
14952     if (element == EL_ROBOT_WHEEL)
14953     {
14954       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14955
14956       game.robot_wheel_x = x;
14957       game.robot_wheel_y = y;
14958       game.robot_wheel_active = TRUE;
14959
14960       TEST_DrawLevelField(x, y);
14961     }
14962     else if (element == EL_SP_TERMINAL)
14963     {
14964       int xx, yy;
14965
14966       SCAN_PLAYFIELD(xx, yy)
14967       {
14968         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14969         {
14970           Bang(xx, yy);
14971         }
14972         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14973         {
14974           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14975
14976           ResetGfxAnimation(xx, yy);
14977           TEST_DrawLevelField(xx, yy);
14978         }
14979       }
14980     }
14981     else if (IS_BELT_SWITCH(element))
14982     {
14983       ToggleBeltSwitch(x, y);
14984     }
14985     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14986              element == EL_SWITCHGATE_SWITCH_DOWN ||
14987              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14988              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14989     {
14990       ToggleSwitchgateSwitch();
14991     }
14992     else if (element == EL_LIGHT_SWITCH ||
14993              element == EL_LIGHT_SWITCH_ACTIVE)
14994     {
14995       ToggleLightSwitch(x, y);
14996     }
14997     else if (element == EL_TIMEGATE_SWITCH ||
14998              element == EL_DC_TIMEGATE_SWITCH)
14999     {
15000       ActivateTimegateSwitch(x, y);
15001     }
15002     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15003              element == EL_BALLOON_SWITCH_RIGHT ||
15004              element == EL_BALLOON_SWITCH_UP    ||
15005              element == EL_BALLOON_SWITCH_DOWN  ||
15006              element == EL_BALLOON_SWITCH_NONE  ||
15007              element == EL_BALLOON_SWITCH_ANY)
15008     {
15009       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15010                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15011                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15012                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15013                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15014                              move_direction);
15015     }
15016     else if (element == EL_LAMP)
15017     {
15018       Tile[x][y] = EL_LAMP_ACTIVE;
15019       game.lights_still_needed--;
15020
15021       ResetGfxAnimation(x, y);
15022       TEST_DrawLevelField(x, y);
15023     }
15024     else if (element == EL_TIME_ORB_FULL)
15025     {
15026       Tile[x][y] = EL_TIME_ORB_EMPTY;
15027
15028       if (level.time > 0 || level.use_time_orb_bug)
15029       {
15030         TimeLeft += level.time_orb_time;
15031         game.no_level_time_limit = FALSE;
15032
15033         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15034
15035         DisplayGameControlValues();
15036       }
15037
15038       ResetGfxAnimation(x, y);
15039       TEST_DrawLevelField(x, y);
15040     }
15041     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15042              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15043     {
15044       int xx, yy;
15045
15046       game.ball_active = !game.ball_active;
15047
15048       SCAN_PLAYFIELD(xx, yy)
15049       {
15050         int e = Tile[xx][yy];
15051
15052         if (game.ball_active)
15053         {
15054           if (e == EL_EMC_MAGIC_BALL)
15055             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15056           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15057             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15058         }
15059         else
15060         {
15061           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15062             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15063           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15064             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15065         }
15066       }
15067     }
15068
15069     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15070                                         player->index_bit, dig_side);
15071
15072     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15073                                         player->index_bit, dig_side);
15074
15075     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15076                                         player->index_bit, dig_side);
15077
15078     return MP_ACTION;
15079   }
15080   else
15081   {
15082     if (!PLAYER_SWITCHING(player, x, y))
15083     {
15084       player->is_switching = TRUE;
15085       player->switch_x = x;
15086       player->switch_y = y;
15087
15088       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15089                                  player->index_bit, dig_side);
15090       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15091                                           player->index_bit, dig_side);
15092
15093       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15094                                  player->index_bit, dig_side);
15095       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15096                                           player->index_bit, dig_side);
15097     }
15098
15099     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15100                                player->index_bit, dig_side);
15101     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15102                                         player->index_bit, dig_side);
15103
15104     return MP_NO_ACTION;
15105   }
15106
15107   player->push_delay = -1;
15108
15109   if (is_player)                // function can also be called by EL_PENGUIN
15110   {
15111     if (Tile[x][y] != element)          // really digged/collected something
15112     {
15113       player->is_collecting = !player->is_digging;
15114       player->is_active = TRUE;
15115
15116       player->last_removed_element = element;
15117     }
15118   }
15119
15120   return MP_MOVING;
15121 }
15122
15123 static boolean DigFieldByCE(int x, int y, int digging_element)
15124 {
15125   int element = Tile[x][y];
15126
15127   if (!IS_FREE(x, y))
15128   {
15129     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15130                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15131                   ACTION_BREAKING);
15132
15133     // no element can dig solid indestructible elements
15134     if (IS_INDESTRUCTIBLE(element) &&
15135         !IS_DIGGABLE(element) &&
15136         !IS_COLLECTIBLE(element))
15137       return FALSE;
15138
15139     if (AmoebaNr[x][y] &&
15140         (element == EL_AMOEBA_FULL ||
15141          element == EL_BD_AMOEBA ||
15142          element == EL_AMOEBA_GROWING))
15143     {
15144       AmoebaCnt[AmoebaNr[x][y]]--;
15145       AmoebaCnt2[AmoebaNr[x][y]]--;
15146     }
15147
15148     if (IS_MOVING(x, y))
15149       RemoveMovingField(x, y);
15150     else
15151     {
15152       RemoveField(x, y);
15153       TEST_DrawLevelField(x, y);
15154     }
15155
15156     // if digged element was about to explode, prevent the explosion
15157     ExplodeField[x][y] = EX_TYPE_NONE;
15158
15159     PlayLevelSoundAction(x, y, action);
15160   }
15161
15162   Store[x][y] = EL_EMPTY;
15163
15164   // this makes it possible to leave the removed element again
15165   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15166     Store[x][y] = element;
15167
15168   return TRUE;
15169 }
15170
15171 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15172 {
15173   int jx = player->jx, jy = player->jy;
15174   int x = jx + dx, y = jy + dy;
15175   int snap_direction = (dx == -1 ? MV_LEFT  :
15176                         dx == +1 ? MV_RIGHT :
15177                         dy == -1 ? MV_UP    :
15178                         dy == +1 ? MV_DOWN  : MV_NONE);
15179   boolean can_continue_snapping = (level.continuous_snapping &&
15180                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15181
15182   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15183     return FALSE;
15184
15185   if (!player->active || !IN_LEV_FIELD(x, y))
15186     return FALSE;
15187
15188   if (dx && dy)
15189     return FALSE;
15190
15191   if (!dx && !dy)
15192   {
15193     if (player->MovPos == 0)
15194       player->is_pushing = FALSE;
15195
15196     player->is_snapping = FALSE;
15197
15198     if (player->MovPos == 0)
15199     {
15200       player->is_moving = FALSE;
15201       player->is_digging = FALSE;
15202       player->is_collecting = FALSE;
15203     }
15204
15205     return FALSE;
15206   }
15207
15208   // prevent snapping with already pressed snap key when not allowed
15209   if (player->is_snapping && !can_continue_snapping)
15210     return FALSE;
15211
15212   player->MovDir = snap_direction;
15213
15214   if (player->MovPos == 0)
15215   {
15216     player->is_moving = FALSE;
15217     player->is_digging = FALSE;
15218     player->is_collecting = FALSE;
15219   }
15220
15221   player->is_dropping = FALSE;
15222   player->is_dropping_pressed = FALSE;
15223   player->drop_pressed_delay = 0;
15224
15225   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15226     return FALSE;
15227
15228   player->is_snapping = TRUE;
15229   player->is_active = TRUE;
15230
15231   if (player->MovPos == 0)
15232   {
15233     player->is_moving = FALSE;
15234     player->is_digging = FALSE;
15235     player->is_collecting = FALSE;
15236   }
15237
15238   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15239     TEST_DrawLevelField(player->last_jx, player->last_jy);
15240
15241   TEST_DrawLevelField(x, y);
15242
15243   return TRUE;
15244 }
15245
15246 static boolean DropElement(struct PlayerInfo *player)
15247 {
15248   int old_element, new_element;
15249   int dropx = player->jx, dropy = player->jy;
15250   int drop_direction = player->MovDir;
15251   int drop_side = drop_direction;
15252   int drop_element = get_next_dropped_element(player);
15253
15254   /* do not drop an element on top of another element; when holding drop key
15255      pressed without moving, dropped element must move away before the next
15256      element can be dropped (this is especially important if the next element
15257      is dynamite, which can be placed on background for historical reasons) */
15258   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15259     return MP_ACTION;
15260
15261   if (IS_THROWABLE(drop_element))
15262   {
15263     dropx += GET_DX_FROM_DIR(drop_direction);
15264     dropy += GET_DY_FROM_DIR(drop_direction);
15265
15266     if (!IN_LEV_FIELD(dropx, dropy))
15267       return FALSE;
15268   }
15269
15270   old_element = Tile[dropx][dropy];     // old element at dropping position
15271   new_element = drop_element;           // default: no change when dropping
15272
15273   // check if player is active, not moving and ready to drop
15274   if (!player->active || player->MovPos || player->drop_delay > 0)
15275     return FALSE;
15276
15277   // check if player has anything that can be dropped
15278   if (new_element == EL_UNDEFINED)
15279     return FALSE;
15280
15281   // only set if player has anything that can be dropped
15282   player->is_dropping_pressed = TRUE;
15283
15284   // check if drop key was pressed long enough for EM style dynamite
15285   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15286     return FALSE;
15287
15288   // check if anything can be dropped at the current position
15289   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15290     return FALSE;
15291
15292   // collected custom elements can only be dropped on empty fields
15293   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15294     return FALSE;
15295
15296   if (old_element != EL_EMPTY)
15297     Back[dropx][dropy] = old_element;   // store old element on this field
15298
15299   ResetGfxAnimation(dropx, dropy);
15300   ResetRandomAnimationValue(dropx, dropy);
15301
15302   if (player->inventory_size > 0 ||
15303       player->inventory_infinite_element != EL_UNDEFINED)
15304   {
15305     if (player->inventory_size > 0)
15306     {
15307       player->inventory_size--;
15308
15309       DrawGameDoorValues();
15310
15311       if (new_element == EL_DYNAMITE)
15312         new_element = EL_DYNAMITE_ACTIVE;
15313       else if (new_element == EL_EM_DYNAMITE)
15314         new_element = EL_EM_DYNAMITE_ACTIVE;
15315       else if (new_element == EL_SP_DISK_RED)
15316         new_element = EL_SP_DISK_RED_ACTIVE;
15317     }
15318
15319     Tile[dropx][dropy] = new_element;
15320
15321     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15322       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15323                           el2img(Tile[dropx][dropy]), 0);
15324
15325     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15326
15327     // needed if previous element just changed to "empty" in the last frame
15328     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15329
15330     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15331                                player->index_bit, drop_side);
15332     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15333                                         CE_PLAYER_DROPS_X,
15334                                         player->index_bit, drop_side);
15335
15336     TestIfElementTouchesCustomElement(dropx, dropy);
15337   }
15338   else          // player is dropping a dyna bomb
15339   {
15340     player->dynabombs_left--;
15341
15342     Tile[dropx][dropy] = new_element;
15343
15344     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15345       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15346                           el2img(Tile[dropx][dropy]), 0);
15347
15348     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15349   }
15350
15351   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15352     InitField_WithBug1(dropx, dropy, FALSE);
15353
15354   new_element = Tile[dropx][dropy];     // element might have changed
15355
15356   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15357       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15358   {
15359     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15360       MovDir[dropx][dropy] = drop_direction;
15361
15362     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15363
15364     // do not cause impact style collision by dropping elements that can fall
15365     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15366   }
15367
15368   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15369   player->is_dropping = TRUE;
15370
15371   player->drop_pressed_delay = 0;
15372   player->is_dropping_pressed = FALSE;
15373
15374   player->drop_x = dropx;
15375   player->drop_y = dropy;
15376
15377   return TRUE;
15378 }
15379
15380 // ----------------------------------------------------------------------------
15381 // game sound playing functions
15382 // ----------------------------------------------------------------------------
15383
15384 static int *loop_sound_frame = NULL;
15385 static int *loop_sound_volume = NULL;
15386
15387 void InitPlayLevelSound(void)
15388 {
15389   int num_sounds = getSoundListSize();
15390
15391   checked_free(loop_sound_frame);
15392   checked_free(loop_sound_volume);
15393
15394   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15395   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15396 }
15397
15398 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15399 {
15400   int sx = SCREENX(x), sy = SCREENY(y);
15401   int volume, stereo_position;
15402   int max_distance = 8;
15403   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15404
15405   if ((!setup.sound_simple && !is_loop_sound) ||
15406       (!setup.sound_loops && is_loop_sound))
15407     return;
15408
15409   if (!IN_LEV_FIELD(x, y) ||
15410       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15411       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15412     return;
15413
15414   volume = SOUND_MAX_VOLUME;
15415
15416   if (!IN_SCR_FIELD(sx, sy))
15417   {
15418     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15419     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15420
15421     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15422   }
15423
15424   stereo_position = (SOUND_MAX_LEFT +
15425                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15426                      (SCR_FIELDX + 2 * max_distance));
15427
15428   if (is_loop_sound)
15429   {
15430     /* This assures that quieter loop sounds do not overwrite louder ones,
15431        while restarting sound volume comparison with each new game frame. */
15432
15433     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15434       return;
15435
15436     loop_sound_volume[nr] = volume;
15437     loop_sound_frame[nr] = FrameCounter;
15438   }
15439
15440   PlaySoundExt(nr, volume, stereo_position, type);
15441 }
15442
15443 static void PlayLevelSound(int x, int y, int nr)
15444 {
15445   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15446 }
15447
15448 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15449 {
15450   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15451                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15452                  y < LEVELY(BY1) ? LEVELY(BY1) :
15453                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15454                  sound_action);
15455 }
15456
15457 static void PlayLevelSoundAction(int x, int y, int action)
15458 {
15459   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15460 }
15461
15462 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15463 {
15464   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15465
15466   if (sound_effect != SND_UNDEFINED)
15467     PlayLevelSound(x, y, sound_effect);
15468 }
15469
15470 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15471                                               int action)
15472 {
15473   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15474
15475   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15476     PlayLevelSound(x, y, sound_effect);
15477 }
15478
15479 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15480 {
15481   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15482
15483   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15484     PlayLevelSound(x, y, sound_effect);
15485 }
15486
15487 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15488 {
15489   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15490
15491   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15492     StopSound(sound_effect);
15493 }
15494
15495 static int getLevelMusicNr(void)
15496 {
15497   int level_pos = level_nr - leveldir_current->first_level;
15498
15499   if (levelset.music[level_nr] != MUS_UNDEFINED)
15500     return levelset.music[level_nr];            // from config file
15501   else
15502     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15503 }
15504
15505 static void FadeLevelSounds(void)
15506 {
15507   FadeSounds();
15508 }
15509
15510 static void FadeLevelMusic(void)
15511 {
15512   int music_nr = getLevelMusicNr();
15513   char *curr_music = getCurrentlyPlayingMusicFilename();
15514   char *next_music = getMusicInfoEntryFilename(music_nr);
15515
15516   if (!strEqual(curr_music, next_music))
15517     FadeMusic();
15518 }
15519
15520 void FadeLevelSoundsAndMusic(void)
15521 {
15522   FadeLevelSounds();
15523   FadeLevelMusic();
15524 }
15525
15526 static void PlayLevelMusic(void)
15527 {
15528   int music_nr = getLevelMusicNr();
15529   char *curr_music = getCurrentlyPlayingMusicFilename();
15530   char *next_music = getMusicInfoEntryFilename(music_nr);
15531
15532   if (!strEqual(curr_music, next_music))
15533     PlayMusicLoop(music_nr);
15534 }
15535
15536 static int getSoundAction_BD(int sample)
15537 {
15538   switch (sample)
15539   {
15540     case GD_S_STONE_PUSHING:
15541     case GD_S_MEGA_STONE_PUSHING:
15542     case GD_S_FLYING_STONE_PUSHING:
15543     case GD_S_WAITING_STONE_PUSHING:
15544     case GD_S_CHASING_STONE_PUSHING:
15545     case GD_S_NUT_PUSHING:
15546     case GD_S_NITRO_PACK_PUSHING:
15547     case GD_S_BLADDER_PUSHING:
15548     case GD_S_BOX_PUSHING:
15549       return ACTION_PUSHING;
15550
15551     case GD_S_STONE_FALLING:
15552     case GD_S_MEGA_STONE_FALLING:
15553     case GD_S_FLYING_STONE_FALLING:
15554     case GD_S_NUT_FALLING:
15555     case GD_S_DIRT_BALL_FALLING:
15556     case GD_S_DIRT_LOOSE_FALLING:
15557     case GD_S_NITRO_PACK_FALLING:
15558     case GD_S_FALLING_WALL_FALLING:
15559       return ACTION_FALLING;
15560
15561     case GD_S_STONE_IMPACT:
15562     case GD_S_MEGA_STONE_IMPACT:
15563     case GD_S_FLYING_STONE_IMPACT:
15564     case GD_S_NUT_IMPACT:
15565     case GD_S_DIRT_BALL_IMPACT:
15566     case GD_S_DIRT_LOOSE_IMPACT:
15567     case GD_S_NITRO_PACK_IMPACT:
15568     case GD_S_FALLING_WALL_IMPACT:
15569       return ACTION_IMPACT;
15570
15571     case GD_S_NUT_CRACKING:
15572       return ACTION_BREAKING;
15573
15574     case GD_S_EXPANDING_WALL:
15575     case GD_S_WALL_REAPPEARING:
15576     case GD_S_SLIME:
15577     case GD_S_LAVA:
15578     case GD_S_ACID_SPREADING:
15579       return ACTION_GROWING;
15580
15581     case GD_S_DIAMOND_COLLECTING:
15582     case GD_S_FLYING_DIAMOND_COLLECTING:
15583     case GD_S_SKELETON_COLLECTING:
15584     case GD_S_PNEUMATIC_COLLECTING:
15585     case GD_S_BOMB_COLLECTING:
15586     case GD_S_CLOCK_COLLECTING:
15587     case GD_S_SWEET_COLLECTING:
15588     case GD_S_KEY_COLLECTING:
15589     case GD_S_DIAMOND_KEY_COLLECTING:
15590       return ACTION_COLLECTING;
15591
15592     case GD_S_BOMB_PLACING:
15593     case GD_S_REPLICATOR:
15594       return ACTION_DROPPING;
15595
15596     case GD_S_BLADDER_MOVING:
15597       return ACTION_MOVING;
15598
15599     case GD_S_BLADDER_SPENDER:
15600     case GD_S_BLADDER_CONVERTING:
15601     case GD_S_GRAVITY_CHANGING:
15602       return ACTION_CHANGING;
15603
15604     case GD_S_BITER_EATING:
15605       return ACTION_EATING;
15606
15607     case GD_S_DOOR_OPENING:
15608     case GD_S_CRACKING:
15609       return ACTION_OPENING;
15610
15611     case GD_S_DIRT_WALKING:
15612       return ACTION_DIGGING;
15613
15614     case GD_S_EMPTY_WALKING:
15615       return ACTION_WALKING;
15616
15617     case GD_S_SWITCH_BITER:
15618     case GD_S_SWITCH_CREATURES:
15619     case GD_S_SWITCH_GRAVITY:
15620     case GD_S_SWITCH_EXPANDING:
15621     case GD_S_SWITCH_CONVEYOR:
15622     case GD_S_SWITCH_REPLICATOR:
15623     case GD_S_STIRRING:
15624       return ACTION_ACTIVATING;
15625
15626     case GD_S_TELEPORTER:
15627       return ACTION_PASSING;
15628
15629     case GD_S_EXPLODING:
15630     case GD_S_BOMB_EXPLODING:
15631     case GD_S_GHOST_EXPLODING:
15632     case GD_S_VOODOO_EXPLODING:
15633     case GD_S_NITRO_PACK_EXPLODING:
15634       return ACTION_EXPLODING;
15635
15636     case GD_S_COVERING:
15637     case GD_S_AMOEBA:
15638     case GD_S_MAGIC_WALL:
15639     case GD_S_PNEUMATIC_HAMMER:
15640     case GD_S_WATER:
15641       return ACTION_ACTIVE;
15642
15643     case GD_S_DIAMOND_FALLING_RANDOM:
15644     case GD_S_DIAMOND_FALLING_1:
15645     case GD_S_DIAMOND_FALLING_2:
15646     case GD_S_DIAMOND_FALLING_3:
15647     case GD_S_DIAMOND_FALLING_4:
15648     case GD_S_DIAMOND_FALLING_5:
15649     case GD_S_DIAMOND_FALLING_6:
15650     case GD_S_DIAMOND_FALLING_7:
15651     case GD_S_DIAMOND_FALLING_8:
15652     case GD_S_DIAMOND_IMPACT_RANDOM:
15653     case GD_S_DIAMOND_IMPACT_1:
15654     case GD_S_DIAMOND_IMPACT_2:
15655     case GD_S_DIAMOND_IMPACT_3:
15656     case GD_S_DIAMOND_IMPACT_4:
15657     case GD_S_DIAMOND_IMPACT_5:
15658     case GD_S_DIAMOND_IMPACT_6:
15659     case GD_S_DIAMOND_IMPACT_7:
15660     case GD_S_DIAMOND_IMPACT_8:
15661     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15662     case GD_S_FLYING_DIAMOND_FALLING_1:
15663     case GD_S_FLYING_DIAMOND_FALLING_2:
15664     case GD_S_FLYING_DIAMOND_FALLING_3:
15665     case GD_S_FLYING_DIAMOND_FALLING_4:
15666     case GD_S_FLYING_DIAMOND_FALLING_5:
15667     case GD_S_FLYING_DIAMOND_FALLING_6:
15668     case GD_S_FLYING_DIAMOND_FALLING_7:
15669     case GD_S_FLYING_DIAMOND_FALLING_8:
15670     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15671     case GD_S_FLYING_DIAMOND_IMPACT_1:
15672     case GD_S_FLYING_DIAMOND_IMPACT_2:
15673     case GD_S_FLYING_DIAMOND_IMPACT_3:
15674     case GD_S_FLYING_DIAMOND_IMPACT_4:
15675     case GD_S_FLYING_DIAMOND_IMPACT_5:
15676     case GD_S_FLYING_DIAMOND_IMPACT_6:
15677     case GD_S_FLYING_DIAMOND_IMPACT_7:
15678     case GD_S_FLYING_DIAMOND_IMPACT_8:
15679     case GD_S_TIMEOUT_0:
15680     case GD_S_TIMEOUT_1:
15681     case GD_S_TIMEOUT_2:
15682     case GD_S_TIMEOUT_3:
15683     case GD_S_TIMEOUT_4:
15684     case GD_S_TIMEOUT_5:
15685     case GD_S_TIMEOUT_6:
15686     case GD_S_TIMEOUT_7:
15687     case GD_S_TIMEOUT_8:
15688     case GD_S_TIMEOUT_9:
15689     case GD_S_TIMEOUT_10:
15690     case GD_S_BONUS_LIFE:
15691       // trigger special post-processing (and force sound to be non-looping)
15692       return ACTION_OTHER;
15693
15694     case GD_S_AMOEBA_MAGIC:
15695     case GD_S_FINISHED:
15696       // trigger special post-processing (and force sound to be looping)
15697       return ACTION_DEFAULT;
15698
15699     default:
15700       return ACTION_DEFAULT;
15701   }
15702 }
15703
15704 static int getSoundEffect_BD(int element_bd, int sample)
15705 {
15706   int sound_action = getSoundAction_BD(sample);
15707   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15708   int nr;
15709
15710   // standard sounds
15711   if (sound_action != ACTION_OTHER &&
15712       sound_action != ACTION_DEFAULT)
15713     return sound_effect;
15714
15715   // special post-processing for some sounds
15716   switch (sample)
15717   {
15718     case GD_S_DIAMOND_FALLING_RANDOM:
15719     case GD_S_DIAMOND_FALLING_1:
15720     case GD_S_DIAMOND_FALLING_2:
15721     case GD_S_DIAMOND_FALLING_3:
15722     case GD_S_DIAMOND_FALLING_4:
15723     case GD_S_DIAMOND_FALLING_5:
15724     case GD_S_DIAMOND_FALLING_6:
15725     case GD_S_DIAMOND_FALLING_7:
15726     case GD_S_DIAMOND_FALLING_8:
15727       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15728             sample - GD_S_DIAMOND_FALLING_1);
15729       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15730
15731       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15732         sound_effect = SND_BD_DIAMOND_FALLING;
15733       break;
15734
15735     case GD_S_DIAMOND_IMPACT_RANDOM:
15736     case GD_S_DIAMOND_IMPACT_1:
15737     case GD_S_DIAMOND_IMPACT_2:
15738     case GD_S_DIAMOND_IMPACT_3:
15739     case GD_S_DIAMOND_IMPACT_4:
15740     case GD_S_DIAMOND_IMPACT_5:
15741     case GD_S_DIAMOND_IMPACT_6:
15742     case GD_S_DIAMOND_IMPACT_7:
15743     case GD_S_DIAMOND_IMPACT_8:
15744       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15745             sample - GD_S_DIAMOND_IMPACT_1);
15746       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15747
15748       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15749         sound_effect = SND_BD_DIAMOND_IMPACT;
15750       break;
15751
15752     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15753     case GD_S_FLYING_DIAMOND_FALLING_1:
15754     case GD_S_FLYING_DIAMOND_FALLING_2:
15755     case GD_S_FLYING_DIAMOND_FALLING_3:
15756     case GD_S_FLYING_DIAMOND_FALLING_4:
15757     case GD_S_FLYING_DIAMOND_FALLING_5:
15758     case GD_S_FLYING_DIAMOND_FALLING_6:
15759     case GD_S_FLYING_DIAMOND_FALLING_7:
15760     case GD_S_FLYING_DIAMOND_FALLING_8:
15761       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15762             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15763       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15764
15765       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15766         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15767       break;
15768
15769     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15770     case GD_S_FLYING_DIAMOND_IMPACT_1:
15771     case GD_S_FLYING_DIAMOND_IMPACT_2:
15772     case GD_S_FLYING_DIAMOND_IMPACT_3:
15773     case GD_S_FLYING_DIAMOND_IMPACT_4:
15774     case GD_S_FLYING_DIAMOND_IMPACT_5:
15775     case GD_S_FLYING_DIAMOND_IMPACT_6:
15776     case GD_S_FLYING_DIAMOND_IMPACT_7:
15777     case GD_S_FLYING_DIAMOND_IMPACT_8:
15778       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15779             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15780       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15781
15782       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15783         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15784       break;
15785
15786     case GD_S_TIMEOUT_0:
15787     case GD_S_TIMEOUT_1:
15788     case GD_S_TIMEOUT_2:
15789     case GD_S_TIMEOUT_3:
15790     case GD_S_TIMEOUT_4:
15791     case GD_S_TIMEOUT_5:
15792     case GD_S_TIMEOUT_6:
15793     case GD_S_TIMEOUT_7:
15794     case GD_S_TIMEOUT_8:
15795     case GD_S_TIMEOUT_9:
15796     case GD_S_TIMEOUT_10:
15797       nr = sample - GD_S_TIMEOUT_0;
15798       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15799
15800       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15801         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15802       break;
15803
15804     case GD_S_BONUS_LIFE:
15805       sound_effect = SND_GAME_HEALTH_BONUS;
15806       break;
15807
15808     case GD_S_AMOEBA_MAGIC:
15809       sound_effect = SND_BD_AMOEBA_OTHER;
15810       break;
15811
15812     case GD_S_FINISHED:
15813       sound_effect = SND_GAME_LEVELTIME_BONUS;
15814       break;
15815
15816     default:
15817       sound_effect = SND_UNDEFINED;
15818       break;
15819   }
15820
15821   return sound_effect;
15822 }
15823
15824 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15825 {
15826   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15827   int sound_effect = getSoundEffect_BD(element, sample);
15828   int sound_action = getSoundAction_BD(sample);
15829   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15830   int offset = 0;
15831   int x = xx - offset;
15832   int y = yy - offset;
15833
15834   // some sound actions are always looping in native BD game engine
15835   if (sound_action == ACTION_DEFAULT)
15836     is_loop_sound = TRUE;
15837
15838   // some sound actions are always non-looping in native BD game engine
15839   if (sound_action == ACTION_FALLING ||
15840       sound_action == ACTION_MOVING ||
15841       sound_action == ACTION_OTHER)
15842     is_loop_sound = FALSE;
15843
15844   if (sound_effect != SND_UNDEFINED)
15845     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15846 }
15847
15848 void StopSound_BD(int element_bd, int sample)
15849 {
15850   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15851   int sound_effect = getSoundEffect_BD(element, sample);
15852
15853   if (sound_effect != SND_UNDEFINED)
15854     StopSound(sound_effect);
15855 }
15856
15857 boolean isSoundPlaying_BD(int element_bd, int sample)
15858 {
15859   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15860   int sound_effect = getSoundEffect_BD(element, sample);
15861
15862   if (sound_effect != SND_UNDEFINED)
15863     return isSoundPlaying(sound_effect);
15864
15865   return FALSE;
15866 }
15867
15868 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15869 {
15870   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15871   int offset = 0;
15872   int x = xx - offset;
15873   int y = yy - offset;
15874
15875   switch (sample)
15876   {
15877     case SOUND_blank:
15878       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15879       break;
15880
15881     case SOUND_roll:
15882       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15883       break;
15884
15885     case SOUND_stone:
15886       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15887       break;
15888
15889     case SOUND_nut:
15890       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15891       break;
15892
15893     case SOUND_crack:
15894       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15895       break;
15896
15897     case SOUND_bug:
15898       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15899       break;
15900
15901     case SOUND_tank:
15902       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15903       break;
15904
15905     case SOUND_android_clone:
15906       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15907       break;
15908
15909     case SOUND_android_move:
15910       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15911       break;
15912
15913     case SOUND_spring:
15914       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15915       break;
15916
15917     case SOUND_slurp:
15918       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15919       break;
15920
15921     case SOUND_eater:
15922       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15923       break;
15924
15925     case SOUND_eater_eat:
15926       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15927       break;
15928
15929     case SOUND_alien:
15930       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15931       break;
15932
15933     case SOUND_collect:
15934       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15935       break;
15936
15937     case SOUND_diamond:
15938       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15939       break;
15940
15941     case SOUND_squash:
15942       // !!! CHECK THIS !!!
15943 #if 1
15944       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15945 #else
15946       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15947 #endif
15948       break;
15949
15950     case SOUND_wonderfall:
15951       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15952       break;
15953
15954     case SOUND_drip:
15955       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15956       break;
15957
15958     case SOUND_push:
15959       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15960       break;
15961
15962     case SOUND_dirt:
15963       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15964       break;
15965
15966     case SOUND_acid:
15967       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15968       break;
15969
15970     case SOUND_ball:
15971       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15972       break;
15973
15974     case SOUND_slide:
15975       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15976       break;
15977
15978     case SOUND_wonder:
15979       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15980       break;
15981
15982     case SOUND_door:
15983       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15984       break;
15985
15986     case SOUND_exit_open:
15987       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15988       break;
15989
15990     case SOUND_exit_leave:
15991       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15992       break;
15993
15994     case SOUND_dynamite:
15995       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15996       break;
15997
15998     case SOUND_tick:
15999       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16000       break;
16001
16002     case SOUND_press:
16003       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16004       break;
16005
16006     case SOUND_wheel:
16007       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16008       break;
16009
16010     case SOUND_boom:
16011       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16012       break;
16013
16014     case SOUND_die:
16015       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16016       break;
16017
16018     case SOUND_time:
16019       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16020       break;
16021
16022     default:
16023       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16024       break;
16025   }
16026 }
16027
16028 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16029 {
16030   int element = map_element_SP_to_RND(element_sp);
16031   int action = map_action_SP_to_RND(action_sp);
16032   int offset = (setup.sp_show_border_elements ? 0 : 1);
16033   int x = xx - offset;
16034   int y = yy - offset;
16035
16036   PlayLevelSoundElementAction(x, y, element, action);
16037 }
16038
16039 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16040 {
16041   int element = map_element_MM_to_RND(element_mm);
16042   int action = map_action_MM_to_RND(action_mm);
16043   int offset = 0;
16044   int x = xx - offset;
16045   int y = yy - offset;
16046
16047   if (!IS_MM_ELEMENT(element))
16048     element = EL_MM_DEFAULT;
16049
16050   PlayLevelSoundElementAction(x, y, element, action);
16051 }
16052
16053 void PlaySound_MM(int sound_mm)
16054 {
16055   int sound = map_sound_MM_to_RND(sound_mm);
16056
16057   if (sound == SND_UNDEFINED)
16058     return;
16059
16060   PlaySound(sound);
16061 }
16062
16063 void PlaySoundLoop_MM(int sound_mm)
16064 {
16065   int sound = map_sound_MM_to_RND(sound_mm);
16066
16067   if (sound == SND_UNDEFINED)
16068     return;
16069
16070   PlaySoundLoop(sound);
16071 }
16072
16073 void StopSound_MM(int sound_mm)
16074 {
16075   int sound = map_sound_MM_to_RND(sound_mm);
16076
16077   if (sound == SND_UNDEFINED)
16078     return;
16079
16080   StopSound(sound);
16081 }
16082
16083 void RaiseScore(int value)
16084 {
16085   game.score += value;
16086
16087   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16088
16089   DisplayGameControlValues();
16090 }
16091
16092 void RaiseScoreElement(int element)
16093 {
16094   switch (element)
16095   {
16096     case EL_EMERALD:
16097     case EL_BD_DIAMOND:
16098     case EL_EMERALD_YELLOW:
16099     case EL_EMERALD_RED:
16100     case EL_EMERALD_PURPLE:
16101     case EL_SP_INFOTRON:
16102       RaiseScore(level.score[SC_EMERALD]);
16103       break;
16104     case EL_DIAMOND:
16105       RaiseScore(level.score[SC_DIAMOND]);
16106       break;
16107     case EL_CRYSTAL:
16108       RaiseScore(level.score[SC_CRYSTAL]);
16109       break;
16110     case EL_PEARL:
16111       RaiseScore(level.score[SC_PEARL]);
16112       break;
16113     case EL_BUG:
16114     case EL_BD_BUTTERFLY:
16115     case EL_SP_ELECTRON:
16116       RaiseScore(level.score[SC_BUG]);
16117       break;
16118     case EL_SPACESHIP:
16119     case EL_BD_FIREFLY:
16120     case EL_SP_SNIKSNAK:
16121       RaiseScore(level.score[SC_SPACESHIP]);
16122       break;
16123     case EL_YAMYAM:
16124     case EL_DARK_YAMYAM:
16125       RaiseScore(level.score[SC_YAMYAM]);
16126       break;
16127     case EL_ROBOT:
16128       RaiseScore(level.score[SC_ROBOT]);
16129       break;
16130     case EL_PACMAN:
16131       RaiseScore(level.score[SC_PACMAN]);
16132       break;
16133     case EL_NUT:
16134       RaiseScore(level.score[SC_NUT]);
16135       break;
16136     case EL_DYNAMITE:
16137     case EL_EM_DYNAMITE:
16138     case EL_SP_DISK_RED:
16139     case EL_DYNABOMB_INCREASE_NUMBER:
16140     case EL_DYNABOMB_INCREASE_SIZE:
16141     case EL_DYNABOMB_INCREASE_POWER:
16142       RaiseScore(level.score[SC_DYNAMITE]);
16143       break;
16144     case EL_SHIELD_NORMAL:
16145     case EL_SHIELD_DEADLY:
16146       RaiseScore(level.score[SC_SHIELD]);
16147       break;
16148     case EL_EXTRA_TIME:
16149       RaiseScore(level.extra_time_score);
16150       break;
16151     case EL_KEY_1:
16152     case EL_KEY_2:
16153     case EL_KEY_3:
16154     case EL_KEY_4:
16155     case EL_EM_KEY_1:
16156     case EL_EM_KEY_2:
16157     case EL_EM_KEY_3:
16158     case EL_EM_KEY_4:
16159     case EL_EMC_KEY_5:
16160     case EL_EMC_KEY_6:
16161     case EL_EMC_KEY_7:
16162     case EL_EMC_KEY_8:
16163     case EL_DC_KEY_WHITE:
16164       RaiseScore(level.score[SC_KEY]);
16165       break;
16166     default:
16167       RaiseScore(element_info[element].collect_score);
16168       break;
16169   }
16170 }
16171
16172 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16173 {
16174   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16175   {
16176     if (!quick_quit)
16177     {
16178       // prevent short reactivation of overlay buttons while closing door
16179       SetOverlayActive(FALSE);
16180       UnmapGameButtons();
16181
16182       // door may still be open due to skipped or envelope style request
16183       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16184     }
16185
16186     if (network.enabled)
16187     {
16188       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16189     }
16190     else
16191     {
16192       // when using BD game engine, cover screen before fading out
16193       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16194         game_bd.cover_screen = TRUE;
16195
16196       if (quick_quit)
16197         FadeSkipNextFadeIn();
16198
16199       SetGameStatus(GAME_MODE_MAIN);
16200
16201       DrawMainMenu();
16202     }
16203   }
16204   else          // continue playing the game
16205   {
16206     if (tape.playing && tape.deactivate_display)
16207       TapeDeactivateDisplayOff(TRUE);
16208
16209     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16210
16211     if (tape.playing && tape.deactivate_display)
16212       TapeDeactivateDisplayOn();
16213   }
16214 }
16215
16216 void RequestQuitGame(boolean escape_key_pressed)
16217 {
16218   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16219   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16220                         level_editor_test_game);
16221   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16222                           quick_quit || score_info_tape_play);
16223
16224   RequestQuitGameExt(skip_request, quick_quit,
16225                      "Do you really want to quit the game?");
16226 }
16227
16228 static char *getRestartGameMessage(void)
16229 {
16230   boolean play_again = hasStartedNetworkGame();
16231   static char message[MAX_OUTPUT_LINESIZE];
16232   char *game_over_text = "Game over!";
16233   char *play_again_text = " Play it again?";
16234
16235   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16236       game_mm.game_over_message != NULL)
16237     game_over_text = game_mm.game_over_message;
16238
16239   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16240            (play_again ? play_again_text : ""));
16241
16242   return message;
16243 }
16244
16245 static void RequestRestartGame(void)
16246 {
16247   char *message = getRestartGameMessage();
16248   boolean has_started_game = hasStartedNetworkGame();
16249   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16250   int door_state = DOOR_CLOSE_1;
16251
16252   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
16253   {
16254     CloseDoor(door_state);
16255
16256     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16257   }
16258   else
16259   {
16260     // if game was invoked from level editor, also close tape recorder door
16261     if (level_editor_test_game)
16262       door_state = DOOR_CLOSE_ALL;
16263
16264     CloseDoor(door_state);
16265
16266     SetGameStatus(GAME_MODE_MAIN);
16267
16268     DrawMainMenu();
16269   }
16270 }
16271
16272 boolean CheckRestartGame(void)
16273 {
16274   static int game_over_delay = 0;
16275   int game_over_delay_value = 50;
16276   boolean game_over = checkGameFailed();
16277
16278   if (!game_over)
16279   {
16280     game_over_delay = game_over_delay_value;
16281
16282     return FALSE;
16283   }
16284
16285   if (game_over_delay > 0)
16286   {
16287     if (game_over_delay == game_over_delay_value / 2)
16288       PlaySound(SND_GAME_LOSING);
16289
16290     game_over_delay--;
16291
16292     return FALSE;
16293   }
16294
16295   // do not ask to play again if request dialog is already active
16296   if (game.request_active)
16297     return FALSE;
16298
16299   // do not ask to play again if request dialog already handled
16300   if (game.RestartGameRequested)
16301     return FALSE;
16302
16303   // do not ask to play again if game was never actually played
16304   if (!game.GamePlayed)
16305     return FALSE;
16306
16307   // do not ask to play again if this was disabled in setup menu
16308   if (!setup.ask_on_game_over)
16309     return FALSE;
16310
16311   game.RestartGameRequested = TRUE;
16312
16313   RequestRestartGame();
16314
16315   return TRUE;
16316 }
16317
16318 boolean checkGameRunning(void)
16319 {
16320   if (game_status != GAME_MODE_PLAYING)
16321     return FALSE;
16322
16323   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16324     return FALSE;
16325
16326   return TRUE;
16327 }
16328
16329 boolean checkGamePlaying(void)
16330 {
16331   if (game_status != GAME_MODE_PLAYING)
16332     return FALSE;
16333
16334   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16335     return FALSE;
16336
16337   return TRUE;
16338 }
16339
16340 boolean checkGameSolved(void)
16341 {
16342   // set for all game engines if level was solved
16343   return game.LevelSolved_GameEnd;
16344 }
16345
16346 boolean checkGameFailed(void)
16347 {
16348   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16349     return (game_bd.game_over && !game_bd.level_solved);
16350   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16351     return (game_em.game_over && !game_em.level_solved);
16352   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16353     return (game_sp.game_over && !game_sp.level_solved);
16354   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16355     return (game_mm.game_over && !game_mm.level_solved);
16356   else                          // GAME_ENGINE_TYPE_RND
16357     return (game.GameOver && !game.LevelSolved);
16358 }
16359
16360 boolean checkGameEnded(void)
16361 {
16362   return (checkGameSolved() || checkGameFailed());
16363 }
16364
16365
16366 // ----------------------------------------------------------------------------
16367 // random generator functions
16368 // ----------------------------------------------------------------------------
16369
16370 unsigned int InitEngineRandom_RND(int seed)
16371 {
16372   game.num_random_calls = 0;
16373
16374   return InitEngineRandom(seed);
16375 }
16376
16377 unsigned int RND(int max)
16378 {
16379   if (max > 0)
16380   {
16381     game.num_random_calls++;
16382
16383     return GetEngineRandom(max);
16384   }
16385
16386   return 0;
16387 }
16388
16389
16390 // ----------------------------------------------------------------------------
16391 // game engine snapshot handling functions
16392 // ----------------------------------------------------------------------------
16393
16394 struct EngineSnapshotInfo
16395 {
16396   // runtime values for custom element collect score
16397   int collect_score[NUM_CUSTOM_ELEMENTS];
16398
16399   // runtime values for group element choice position
16400   int choice_pos[NUM_GROUP_ELEMENTS];
16401
16402   // runtime values for belt position animations
16403   int belt_graphic[4][NUM_BELT_PARTS];
16404   int belt_anim_mode[4][NUM_BELT_PARTS];
16405 };
16406
16407 static struct EngineSnapshotInfo engine_snapshot_rnd;
16408 static char *snapshot_level_identifier = NULL;
16409 static int snapshot_level_nr = -1;
16410
16411 static void SaveEngineSnapshotValues_RND(void)
16412 {
16413   static int belt_base_active_element[4] =
16414   {
16415     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16416     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16417     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16418     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16419   };
16420   int i, j;
16421
16422   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16423   {
16424     int element = EL_CUSTOM_START + i;
16425
16426     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16427   }
16428
16429   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16430   {
16431     int element = EL_GROUP_START + i;
16432
16433     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16434   }
16435
16436   for (i = 0; i < 4; i++)
16437   {
16438     for (j = 0; j < NUM_BELT_PARTS; j++)
16439     {
16440       int element = belt_base_active_element[i] + j;
16441       int graphic = el2img(element);
16442       int anim_mode = graphic_info[graphic].anim_mode;
16443
16444       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16445       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16446     }
16447   }
16448 }
16449
16450 static void LoadEngineSnapshotValues_RND(void)
16451 {
16452   unsigned int num_random_calls = game.num_random_calls;
16453   int i, j;
16454
16455   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16456   {
16457     int element = EL_CUSTOM_START + i;
16458
16459     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16460   }
16461
16462   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16463   {
16464     int element = EL_GROUP_START + i;
16465
16466     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16467   }
16468
16469   for (i = 0; i < 4; i++)
16470   {
16471     for (j = 0; j < NUM_BELT_PARTS; j++)
16472     {
16473       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16474       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16475
16476       graphic_info[graphic].anim_mode = anim_mode;
16477     }
16478   }
16479
16480   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16481   {
16482     InitRND(tape.random_seed);
16483     for (i = 0; i < num_random_calls; i++)
16484       RND(1);
16485   }
16486
16487   if (game.num_random_calls != num_random_calls)
16488   {
16489     Error("number of random calls out of sync");
16490     Error("number of random calls should be %d", num_random_calls);
16491     Error("number of random calls is %d", game.num_random_calls);
16492
16493     Fail("this should not happen -- please debug");
16494   }
16495 }
16496
16497 void FreeEngineSnapshotSingle(void)
16498 {
16499   FreeSnapshotSingle();
16500
16501   setString(&snapshot_level_identifier, NULL);
16502   snapshot_level_nr = -1;
16503 }
16504
16505 void FreeEngineSnapshotList(void)
16506 {
16507   FreeSnapshotList();
16508 }
16509
16510 static ListNode *SaveEngineSnapshotBuffers(void)
16511 {
16512   ListNode *buffers = NULL;
16513
16514   // copy some special values to a structure better suited for the snapshot
16515
16516   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16517     SaveEngineSnapshotValues_RND();
16518   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16519     SaveEngineSnapshotValues_EM();
16520   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16521     SaveEngineSnapshotValues_SP(&buffers);
16522   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16523     SaveEngineSnapshotValues_MM();
16524
16525   // save values stored in special snapshot structure
16526
16527   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16528     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16529   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16530     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16531   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16532     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16533   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16534     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16535
16536   // save further RND engine values
16537
16538   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16539   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16540   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16541
16542   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16543   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16544   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16545   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16546   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16547   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16548
16549   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16550   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16551   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16552
16553   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16554
16555   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16556   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16557
16558   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16559   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16560   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16561   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16562   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16572   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16576
16577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16578   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16579
16580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16583
16584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16586
16587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16593
16594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16596
16597 #if 0
16598   ListNode *node = engine_snapshot_list_rnd;
16599   int num_bytes = 0;
16600
16601   while (node != NULL)
16602   {
16603     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16604
16605     node = node->next;
16606   }
16607
16608   Debug("game:playing:SaveEngineSnapshotBuffers",
16609         "size of engine snapshot: %d bytes", num_bytes);
16610 #endif
16611
16612   return buffers;
16613 }
16614
16615 void SaveEngineSnapshotSingle(void)
16616 {
16617   ListNode *buffers = SaveEngineSnapshotBuffers();
16618
16619   // finally save all snapshot buffers to single snapshot
16620   SaveSnapshotSingle(buffers);
16621
16622   // save level identification information
16623   setString(&snapshot_level_identifier, leveldir_current->identifier);
16624   snapshot_level_nr = level_nr;
16625 }
16626
16627 boolean CheckSaveEngineSnapshotToList(void)
16628 {
16629   boolean save_snapshot =
16630     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16631      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16632       game.snapshot.changed_action) ||
16633      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16634       game.snapshot.collected_item));
16635
16636   game.snapshot.changed_action = FALSE;
16637   game.snapshot.collected_item = FALSE;
16638   game.snapshot.save_snapshot = save_snapshot;
16639
16640   return save_snapshot;
16641 }
16642
16643 void SaveEngineSnapshotToList(void)
16644 {
16645   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16646       tape.quick_resume)
16647     return;
16648
16649   ListNode *buffers = SaveEngineSnapshotBuffers();
16650
16651   // finally save all snapshot buffers to snapshot list
16652   SaveSnapshotToList(buffers);
16653 }
16654
16655 void SaveEngineSnapshotToListInitial(void)
16656 {
16657   FreeEngineSnapshotList();
16658
16659   SaveEngineSnapshotToList();
16660 }
16661
16662 static void LoadEngineSnapshotValues(void)
16663 {
16664   // restore special values from snapshot structure
16665
16666   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16667     LoadEngineSnapshotValues_RND();
16668   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16669     LoadEngineSnapshotValues_EM();
16670   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16671     LoadEngineSnapshotValues_SP();
16672   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16673     LoadEngineSnapshotValues_MM();
16674 }
16675
16676 void LoadEngineSnapshotSingle(void)
16677 {
16678   LoadSnapshotSingle();
16679
16680   LoadEngineSnapshotValues();
16681 }
16682
16683 static void LoadEngineSnapshot_Undo(int steps)
16684 {
16685   LoadSnapshotFromList_Older(steps);
16686
16687   LoadEngineSnapshotValues();
16688 }
16689
16690 static void LoadEngineSnapshot_Redo(int steps)
16691 {
16692   LoadSnapshotFromList_Newer(steps);
16693
16694   LoadEngineSnapshotValues();
16695 }
16696
16697 boolean CheckEngineSnapshotSingle(void)
16698 {
16699   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16700           snapshot_level_nr == level_nr);
16701 }
16702
16703 boolean CheckEngineSnapshotList(void)
16704 {
16705   return CheckSnapshotList();
16706 }
16707
16708
16709 // ---------- new game button stuff -------------------------------------------
16710
16711 static struct
16712 {
16713   int graphic;
16714   struct XY *pos;
16715   int gadget_id;
16716   boolean *setup_value;
16717   boolean allowed_on_tape;
16718   boolean is_touch_button;
16719   char *infotext;
16720 } gamebutton_info[NUM_GAME_BUTTONS] =
16721 {
16722   {
16723     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16724     GAME_CTRL_ID_STOP,                          NULL,
16725     TRUE, FALSE,                                "stop game"
16726   },
16727   {
16728     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16729     GAME_CTRL_ID_PAUSE,                         NULL,
16730     TRUE, FALSE,                                "pause game"
16731   },
16732   {
16733     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16734     GAME_CTRL_ID_PLAY,                          NULL,
16735     TRUE, FALSE,                                "play game"
16736   },
16737   {
16738     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16739     GAME_CTRL_ID_UNDO,                          NULL,
16740     TRUE, FALSE,                                "undo step"
16741   },
16742   {
16743     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16744     GAME_CTRL_ID_REDO,                          NULL,
16745     TRUE, FALSE,                                "redo step"
16746   },
16747   {
16748     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16749     GAME_CTRL_ID_SAVE,                          NULL,
16750     TRUE, FALSE,                                "save game"
16751   },
16752   {
16753     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16754     GAME_CTRL_ID_PAUSE2,                        NULL,
16755     TRUE, FALSE,                                "pause game"
16756   },
16757   {
16758     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16759     GAME_CTRL_ID_LOAD,                          NULL,
16760     TRUE, FALSE,                                "load game"
16761   },
16762   {
16763     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16764     GAME_CTRL_ID_RESTART,                       NULL,
16765     TRUE, FALSE,                                "restart game"
16766   },
16767   {
16768     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16769     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16770     FALSE, FALSE,                               "stop game"
16771   },
16772   {
16773     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16774     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16775     FALSE, FALSE,                               "pause game"
16776   },
16777   {
16778     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16779     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16780     FALSE, FALSE,                               "play game"
16781   },
16782   {
16783     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16784     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16785     FALSE, FALSE,                               "restart game"
16786   },
16787   {
16788     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16789     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16790     FALSE, TRUE,                                "stop game"
16791   },
16792   {
16793     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16794     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16795     FALSE, TRUE,                                "pause game"
16796   },
16797   {
16798     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16799     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16800     FALSE, TRUE,                                "restart game"
16801   },
16802   {
16803     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16804     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16805     TRUE, FALSE,                                "background music on/off"
16806   },
16807   {
16808     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16809     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16810     TRUE, FALSE,                                "sound loops on/off"
16811   },
16812   {
16813     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16814     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16815     TRUE, FALSE,                                "normal sounds on/off"
16816   },
16817   {
16818     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16819     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16820     FALSE, FALSE,                               "background music on/off"
16821   },
16822   {
16823     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16824     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16825     FALSE, FALSE,                               "sound loops on/off"
16826   },
16827   {
16828     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16829     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16830     FALSE, FALSE,                               "normal sounds on/off"
16831   }
16832 };
16833
16834 void CreateGameButtons(void)
16835 {
16836   int i;
16837
16838   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16839   {
16840     int graphic = gamebutton_info[i].graphic;
16841     struct GraphicInfo *gfx = &graphic_info[graphic];
16842     struct XY *pos = gamebutton_info[i].pos;
16843     struct GadgetInfo *gi;
16844     int button_type;
16845     boolean checked;
16846     unsigned int event_mask;
16847     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16848     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16849     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16850     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16851     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16852     int gd_x   = gfx->src_x;
16853     int gd_y   = gfx->src_y;
16854     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16855     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16856     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16857     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16858     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16859     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16860     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16861     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16862     int id = i;
16863
16864     // do not use touch buttons if overlay touch buttons are disabled
16865     if (is_touch_button && !setup.touch.overlay_buttons)
16866       continue;
16867
16868     if (gfx->bitmap == NULL)
16869     {
16870       game_gadget[id] = NULL;
16871
16872       continue;
16873     }
16874
16875     if (id == GAME_CTRL_ID_STOP ||
16876         id == GAME_CTRL_ID_PANEL_STOP ||
16877         id == GAME_CTRL_ID_TOUCH_STOP ||
16878         id == GAME_CTRL_ID_PLAY ||
16879         id == GAME_CTRL_ID_PANEL_PLAY ||
16880         id == GAME_CTRL_ID_SAVE ||
16881         id == GAME_CTRL_ID_LOAD ||
16882         id == GAME_CTRL_ID_RESTART ||
16883         id == GAME_CTRL_ID_PANEL_RESTART ||
16884         id == GAME_CTRL_ID_TOUCH_RESTART)
16885     {
16886       button_type = GD_TYPE_NORMAL_BUTTON;
16887       checked = FALSE;
16888       event_mask = GD_EVENT_RELEASED;
16889     }
16890     else if (id == GAME_CTRL_ID_UNDO ||
16891              id == GAME_CTRL_ID_REDO)
16892     {
16893       button_type = GD_TYPE_NORMAL_BUTTON;
16894       checked = FALSE;
16895       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16896     }
16897     else
16898     {
16899       button_type = GD_TYPE_CHECK_BUTTON;
16900       checked = (gamebutton_info[i].setup_value != NULL ?
16901                  *gamebutton_info[i].setup_value : FALSE);
16902       event_mask = GD_EVENT_PRESSED;
16903     }
16904
16905     gi = CreateGadget(GDI_CUSTOM_ID, id,
16906                       GDI_IMAGE_ID, graphic,
16907                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16908                       GDI_X, base_x + x,
16909                       GDI_Y, base_y + y,
16910                       GDI_WIDTH, gfx->width,
16911                       GDI_HEIGHT, gfx->height,
16912                       GDI_TYPE, button_type,
16913                       GDI_STATE, GD_BUTTON_UNPRESSED,
16914                       GDI_CHECKED, checked,
16915                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16916                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16917                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16918                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16919                       GDI_DIRECT_DRAW, FALSE,
16920                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16921                       GDI_EVENT_MASK, event_mask,
16922                       GDI_CALLBACK_ACTION, HandleGameButtons,
16923                       GDI_END);
16924
16925     if (gi == NULL)
16926       Fail("cannot create gadget");
16927
16928     game_gadget[id] = gi;
16929   }
16930 }
16931
16932 void FreeGameButtons(void)
16933 {
16934   int i;
16935
16936   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16937     FreeGadget(game_gadget[i]);
16938 }
16939
16940 static void UnmapGameButtonsAtSamePosition(int id)
16941 {
16942   int i;
16943
16944   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16945     if (i != id &&
16946         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16947         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16948       UnmapGadget(game_gadget[i]);
16949 }
16950
16951 static void UnmapGameButtonsAtSamePosition_All(void)
16952 {
16953   if (setup.show_load_save_buttons)
16954   {
16955     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16956     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16957     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16958   }
16959   else if (setup.show_undo_redo_buttons)
16960   {
16961     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16962     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16963     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16964   }
16965   else
16966   {
16967     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16968     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16969     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16970
16971     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16972     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16973     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16974   }
16975 }
16976
16977 void MapLoadSaveButtons(void)
16978 {
16979   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16980   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16981
16982   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16983   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16984 }
16985
16986 void MapUndoRedoButtons(void)
16987 {
16988   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16989   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16990
16991   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16992   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16993 }
16994
16995 void ModifyPauseButtons(void)
16996 {
16997   static int ids[] =
16998   {
16999     GAME_CTRL_ID_PAUSE,
17000     GAME_CTRL_ID_PAUSE2,
17001     GAME_CTRL_ID_PANEL_PAUSE,
17002     GAME_CTRL_ID_TOUCH_PAUSE,
17003     -1
17004   };
17005   int i;
17006
17007   // do not redraw pause button on closed door (may happen when restarting game)
17008   if (!(GetDoorState() & DOOR_OPEN_1))
17009     return;
17010
17011   for (i = 0; ids[i] > -1; i++)
17012     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17013 }
17014
17015 static void MapGameButtonsExt(boolean on_tape)
17016 {
17017   int i;
17018
17019   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17020   {
17021     if ((i == GAME_CTRL_ID_UNDO ||
17022          i == GAME_CTRL_ID_REDO) &&
17023         game_status != GAME_MODE_PLAYING)
17024       continue;
17025
17026     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17027       MapGadget(game_gadget[i]);
17028   }
17029
17030   UnmapGameButtonsAtSamePosition_All();
17031
17032   RedrawGameButtons();
17033 }
17034
17035 static void UnmapGameButtonsExt(boolean on_tape)
17036 {
17037   int i;
17038
17039   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17040     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17041       UnmapGadget(game_gadget[i]);
17042 }
17043
17044 static void RedrawGameButtonsExt(boolean on_tape)
17045 {
17046   int i;
17047
17048   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17049     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17050       RedrawGadget(game_gadget[i]);
17051 }
17052
17053 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17054 {
17055   if (gi == NULL)
17056     return;
17057
17058   gi->checked = state;
17059 }
17060
17061 static void RedrawSoundButtonGadget(int id)
17062 {
17063   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17064              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17065              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17066              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17067              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17068              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17069              id);
17070
17071   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17072   RedrawGadget(game_gadget[id2]);
17073 }
17074
17075 void MapGameButtons(void)
17076 {
17077   MapGameButtonsExt(FALSE);
17078 }
17079
17080 void UnmapGameButtons(void)
17081 {
17082   UnmapGameButtonsExt(FALSE);
17083 }
17084
17085 void RedrawGameButtons(void)
17086 {
17087   RedrawGameButtonsExt(FALSE);
17088 }
17089
17090 void MapGameButtonsOnTape(void)
17091 {
17092   MapGameButtonsExt(TRUE);
17093 }
17094
17095 void UnmapGameButtonsOnTape(void)
17096 {
17097   UnmapGameButtonsExt(TRUE);
17098 }
17099
17100 void RedrawGameButtonsOnTape(void)
17101 {
17102   RedrawGameButtonsExt(TRUE);
17103 }
17104
17105 static void GameUndoRedoExt(void)
17106 {
17107   ClearPlayerAction();
17108
17109   tape.pausing = TRUE;
17110
17111   RedrawPlayfield();
17112   UpdateAndDisplayGameControlValues();
17113
17114   DrawCompleteVideoDisplay();
17115   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17116   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17117   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17118
17119   ModifyPauseButtons();
17120
17121   BackToFront();
17122 }
17123
17124 static void GameUndo(int steps)
17125 {
17126   if (!CheckEngineSnapshotList())
17127     return;
17128
17129   int tape_property_bits = tape.property_bits;
17130
17131   LoadEngineSnapshot_Undo(steps);
17132
17133   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17134
17135   GameUndoRedoExt();
17136 }
17137
17138 static void GameRedo(int steps)
17139 {
17140   if (!CheckEngineSnapshotList())
17141     return;
17142
17143   int tape_property_bits = tape.property_bits;
17144
17145   LoadEngineSnapshot_Redo(steps);
17146
17147   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17148
17149   GameUndoRedoExt();
17150 }
17151
17152 static void HandleGameButtonsExt(int id, int button)
17153 {
17154   static boolean game_undo_executed = FALSE;
17155   int steps = BUTTON_STEPSIZE(button);
17156   boolean handle_game_buttons =
17157     (game_status == GAME_MODE_PLAYING ||
17158      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17159
17160   if (!handle_game_buttons)
17161     return;
17162
17163   switch (id)
17164   {
17165     case GAME_CTRL_ID_STOP:
17166     case GAME_CTRL_ID_PANEL_STOP:
17167     case GAME_CTRL_ID_TOUCH_STOP:
17168       TapeStopGame();
17169
17170       break;
17171
17172     case GAME_CTRL_ID_PAUSE:
17173     case GAME_CTRL_ID_PAUSE2:
17174     case GAME_CTRL_ID_PANEL_PAUSE:
17175     case GAME_CTRL_ID_TOUCH_PAUSE:
17176       if (network.enabled && game_status == GAME_MODE_PLAYING)
17177       {
17178         if (tape.pausing)
17179           SendToServer_ContinuePlaying();
17180         else
17181           SendToServer_PausePlaying();
17182       }
17183       else
17184         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17185
17186       game_undo_executed = FALSE;
17187
17188       break;
17189
17190     case GAME_CTRL_ID_PLAY:
17191     case GAME_CTRL_ID_PANEL_PLAY:
17192       if (game_status == GAME_MODE_MAIN)
17193       {
17194         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17195       }
17196       else if (tape.pausing)
17197       {
17198         if (network.enabled)
17199           SendToServer_ContinuePlaying();
17200         else
17201           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17202       }
17203       break;
17204
17205     case GAME_CTRL_ID_UNDO:
17206       // Important: When using "save snapshot when collecting an item" mode,
17207       // load last (current) snapshot for first "undo" after pressing "pause"
17208       // (else the last-but-one snapshot would be loaded, because the snapshot
17209       // pointer already points to the last snapshot when pressing "pause",
17210       // which is fine for "every step/move" mode, but not for "every collect")
17211       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17212           !game_undo_executed)
17213         steps--;
17214
17215       game_undo_executed = TRUE;
17216
17217       GameUndo(steps);
17218       break;
17219
17220     case GAME_CTRL_ID_REDO:
17221       GameRedo(steps);
17222       break;
17223
17224     case GAME_CTRL_ID_SAVE:
17225       TapeQuickSave();
17226       break;
17227
17228     case GAME_CTRL_ID_LOAD:
17229       TapeQuickLoad();
17230       break;
17231
17232     case GAME_CTRL_ID_RESTART:
17233     case GAME_CTRL_ID_PANEL_RESTART:
17234     case GAME_CTRL_ID_TOUCH_RESTART:
17235       TapeRestartGame();
17236
17237       break;
17238
17239     case SOUND_CTRL_ID_MUSIC:
17240     case SOUND_CTRL_ID_PANEL_MUSIC:
17241       if (setup.sound_music)
17242       { 
17243         setup.sound_music = FALSE;
17244
17245         FadeMusic();
17246       }
17247       else if (audio.music_available)
17248       { 
17249         setup.sound = setup.sound_music = TRUE;
17250
17251         SetAudioMode(setup.sound);
17252
17253         if (game_status == GAME_MODE_PLAYING)
17254           PlayLevelMusic();
17255       }
17256
17257       RedrawSoundButtonGadget(id);
17258
17259       break;
17260
17261     case SOUND_CTRL_ID_LOOPS:
17262     case SOUND_CTRL_ID_PANEL_LOOPS:
17263       if (setup.sound_loops)
17264         setup.sound_loops = FALSE;
17265       else if (audio.loops_available)
17266       {
17267         setup.sound = setup.sound_loops = TRUE;
17268
17269         SetAudioMode(setup.sound);
17270       }
17271
17272       RedrawSoundButtonGadget(id);
17273
17274       break;
17275
17276     case SOUND_CTRL_ID_SIMPLE:
17277     case SOUND_CTRL_ID_PANEL_SIMPLE:
17278       if (setup.sound_simple)
17279         setup.sound_simple = FALSE;
17280       else if (audio.sound_available)
17281       {
17282         setup.sound = setup.sound_simple = TRUE;
17283
17284         SetAudioMode(setup.sound);
17285       }
17286
17287       RedrawSoundButtonGadget(id);
17288
17289       break;
17290
17291     default:
17292       break;
17293   }
17294 }
17295
17296 static void HandleGameButtons(struct GadgetInfo *gi)
17297 {
17298   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17299 }
17300
17301 void HandleSoundButtonKeys(Key key)
17302 {
17303   if (key == setup.shortcut.sound_simple)
17304     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17305   else if (key == setup.shortcut.sound_loops)
17306     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17307   else if (key == setup.shortcut.sound_music)
17308     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17309 }