added support for native sounds of Boulder Dash game 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_TOTAL                   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_TOTAL,
250     &game.panel.gems_total,
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_EM ?
2266               game_em.lev->time :
2267               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2268               game_sp.time_played :
2269               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2270               game_mm.energy_left :
2271               game.no_level_time_limit ? TimePlayed : TimeLeft);
2272   int score = (game.LevelSolved ?
2273                game.LevelSolved_CountingScore :
2274                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2275                game_em.lev->score :
2276                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2277                game_sp.score :
2278                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2279                game_mm.score :
2280                game.score);
2281   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2282               game_em.lev->gems_needed :
2283               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2284               game_sp.infotrons_still_needed :
2285               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2286               game_mm.kettles_still_needed :
2287               game.gems_still_needed);
2288   int gems_total = level.gems_needed;
2289   int gems_collected = gems_total - gems;
2290   int gems_score = level.score[SC_EMERALD];
2291   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2292                      game_em.lev->gems_needed > 0 :
2293                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2294                      game_sp.infotrons_still_needed > 0 :
2295                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2296                      game_mm.kettles_still_needed > 0 ||
2297                      game_mm.lights_still_needed > 0 :
2298                      game.gems_still_needed > 0 ||
2299                      game.sokoban_fields_still_needed > 0 ||
2300                      game.sokoban_objects_still_needed > 0 ||
2301                      game.lights_still_needed > 0);
2302   int health = (game.LevelSolved ?
2303                 game.LevelSolved_CountingHealth :
2304                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2305                 MM_HEALTH(game_mm.laser_overload_value) :
2306                 game.health);
2307   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2308
2309   UpdatePlayfieldElementCount();
2310
2311   // update game panel control values
2312
2313   // used instead of "level_nr" (for network games)
2314   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2315   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2316   game_panel_controls[GAME_PANEL_GEMS_TOTAL].value = gems_total;
2317   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2318   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2319
2320   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2321   for (i = 0; i < MAX_NUM_KEYS; i++)
2322     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2323   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2324   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2325
2326   if (game.centered_player_nr == -1)
2327   {
2328     for (i = 0; i < MAX_PLAYERS; i++)
2329     {
2330       // only one player in Supaplex game engine
2331       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2332         break;
2333
2334       for (k = 0; k < MAX_NUM_KEYS; k++)
2335       {
2336         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2337         {
2338           if (game_em.ply[i]->keys & (1 << k))
2339             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2340               get_key_element_from_nr(k);
2341         }
2342         else if (stored_player[i].key[k])
2343           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2344             get_key_element_from_nr(k);
2345       }
2346
2347       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2348         getPlayerInventorySize(i);
2349
2350       if (stored_player[i].num_white_keys > 0)
2351         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2352           EL_DC_KEY_WHITE;
2353
2354       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355         stored_player[i].num_white_keys;
2356     }
2357   }
2358   else
2359   {
2360     int player_nr = game.centered_player_nr;
2361
2362     for (k = 0; k < MAX_NUM_KEYS; k++)
2363     {
2364       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2365       {
2366         if (game_em.ply[player_nr]->keys & (1 << k))
2367           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2368             get_key_element_from_nr(k);
2369       }
2370       else if (stored_player[player_nr].key[k])
2371         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2372           get_key_element_from_nr(k);
2373     }
2374
2375     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2376       getPlayerInventorySize(player_nr);
2377
2378     if (stored_player[player_nr].num_white_keys > 0)
2379       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2380
2381     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2382       stored_player[player_nr].num_white_keys;
2383   }
2384
2385   // re-arrange keys on game panel, if needed or if defined by style settings
2386   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2387   {
2388     int nr = GAME_PANEL_KEY_1 + i;
2389     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2390     struct TextPosInfo *pos = gpc->pos;
2391
2392     // skip check if key is not in the player's inventory
2393     if (gpc->value == EL_EMPTY)
2394       continue;
2395
2396     // check if keys should be arranged on panel from left to right
2397     if (pos->style == STYLE_LEFTMOST_POSITION)
2398     {
2399       // check previous key positions (left from current key)
2400       for (k = 0; k < i; k++)
2401       {
2402         int nr_new = GAME_PANEL_KEY_1 + k;
2403
2404         if (game_panel_controls[nr_new].value == EL_EMPTY)
2405         {
2406           game_panel_controls[nr_new].value = gpc->value;
2407           gpc->value = EL_EMPTY;
2408
2409           break;
2410         }
2411       }
2412     }
2413
2414     // check if "undefined" keys can be placed at some other position
2415     if (pos->x == -1 && pos->y == -1)
2416     {
2417       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2418
2419       // 1st try: display key at the same position as normal or EM keys
2420       if (game_panel_controls[nr_new].value == EL_EMPTY)
2421       {
2422         game_panel_controls[nr_new].value = gpc->value;
2423       }
2424       else
2425       {
2426         // 2nd try: display key at the next free position in the key panel
2427         for (k = 0; k < STD_NUM_KEYS; k++)
2428         {
2429           nr_new = GAME_PANEL_KEY_1 + k;
2430
2431           if (game_panel_controls[nr_new].value == EL_EMPTY)
2432           {
2433             game_panel_controls[nr_new].value = gpc->value;
2434
2435             break;
2436           }
2437         }
2438       }
2439     }
2440   }
2441
2442   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2443   {
2444     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2445       get_inventory_element_from_pos(local_player, i);
2446     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2447       get_inventory_element_from_pos(local_player, -i - 1);
2448   }
2449
2450   game_panel_controls[GAME_PANEL_SCORE].value = score;
2451   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2452
2453   game_panel_controls[GAME_PANEL_TIME].value = time;
2454
2455   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2456   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2457   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2458
2459   if (level.time == 0)
2460     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2461   else
2462     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2463
2464   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2465   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2466
2467   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2468
2469   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2470     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2471      EL_EMPTY);
2472   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2473     local_player->shield_normal_time_left;
2474   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2475     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2476      EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2478     local_player->shield_deadly_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EXIT].value =
2481     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2482
2483   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2484     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2485   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2486     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2487      EL_EMC_MAGIC_BALL_SWITCH);
2488
2489   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2490     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2491   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2492     game.light_time_left;
2493
2494   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2495     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2496   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2497     game.timegate_time_left;
2498
2499   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2500     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2501
2502   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2503     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2504   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2505     game.lenses_time_left;
2506
2507   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2508     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2509   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2510     game.magnify_time_left;
2511
2512   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2513     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2514      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2515      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2516      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2517      EL_BALLOON_SWITCH_NONE);
2518
2519   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2520     local_player->dynabomb_count;
2521   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2522     local_player->dynabomb_size;
2523   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2524     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2525
2526   game_panel_controls[GAME_PANEL_PENGUINS].value =
2527     game.friends_still_needed;
2528
2529   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2530     game.sokoban_objects_still_needed;
2531   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2532     game.sokoban_fields_still_needed;
2533
2534   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2535     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2536
2537   for (i = 0; i < NUM_BELTS; i++)
2538   {
2539     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2540       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2541        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2542     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2543       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2544   }
2545
2546   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2547     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2548   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2549     game.magic_wall_time_left;
2550
2551   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2552     local_player->gravity;
2553
2554   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2555     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2556
2557   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2558     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2559       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2560        game.panel.element[i].id : EL_UNDEFINED);
2561
2562   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2563     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2564       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2565        element_info[game.panel.element_count[i].id].element_count : 0);
2566
2567   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2568     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2569       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2570        element_info[game.panel.ce_score[i].id].collect_score : 0);
2571
2572   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2573     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2574       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2575        element_info[game.panel.ce_score_element[i].id].collect_score :
2576        EL_UNDEFINED);
2577
2578   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2579   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2580   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2581
2582   // update game panel control frames
2583
2584   for (i = 0; game_panel_controls[i].nr != -1; i++)
2585   {
2586     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2587
2588     if (gpc->type == TYPE_ELEMENT)
2589     {
2590       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2591       {
2592         int last_anim_random_frame = gfx.anim_random_frame;
2593         int element = gpc->value;
2594         int graphic = el2panelimg(element);
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame :
2597                                graphic_info[graphic].anim_global_anim_sync ?
2598                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2599
2600         if (gpc->value != gpc->last_value)
2601         {
2602           gpc->gfx_frame = 0;
2603           gpc->gfx_random = init_gfx_random;
2604         }
2605         else
2606         {
2607           gpc->gfx_frame++;
2608
2609           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2610               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2611             gpc->gfx_random = init_gfx_random;
2612         }
2613
2614         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2615           gfx.anim_random_frame = gpc->gfx_random;
2616
2617         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2618           gpc->gfx_frame = element_info[element].collect_score;
2619
2620         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2621
2622         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2623           gfx.anim_random_frame = last_anim_random_frame;
2624       }
2625     }
2626     else if (gpc->type == TYPE_GRAPHIC)
2627     {
2628       if (gpc->graphic != IMG_UNDEFINED)
2629       {
2630         int last_anim_random_frame = gfx.anim_random_frame;
2631         int graphic = gpc->graphic;
2632         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2633                                sync_random_frame :
2634                                graphic_info[graphic].anim_global_anim_sync ?
2635                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2636
2637         if (gpc->value != gpc->last_value)
2638         {
2639           gpc->gfx_frame = 0;
2640           gpc->gfx_random = init_gfx_random;
2641         }
2642         else
2643         {
2644           gpc->gfx_frame++;
2645
2646           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2647               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2648             gpc->gfx_random = init_gfx_random;
2649         }
2650
2651         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2652           gfx.anim_random_frame = gpc->gfx_random;
2653
2654         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2655
2656         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2657           gfx.anim_random_frame = last_anim_random_frame;
2658       }
2659     }
2660   }
2661 }
2662
2663 static void DisplayGameControlValues(void)
2664 {
2665   boolean redraw_panel = FALSE;
2666   int i;
2667
2668   for (i = 0; game_panel_controls[i].nr != -1; i++)
2669   {
2670     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2671
2672     if (PANEL_DEACTIVATED(gpc->pos))
2673       continue;
2674
2675     if (gpc->value == gpc->last_value &&
2676         gpc->frame == gpc->last_frame)
2677       continue;
2678
2679     redraw_panel = TRUE;
2680   }
2681
2682   if (!redraw_panel)
2683     return;
2684
2685   // copy default game door content to main double buffer
2686
2687   // !!! CHECK AGAIN !!!
2688   SetPanelBackground();
2689   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2690   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2691
2692   // redraw game control buttons
2693   RedrawGameButtons();
2694
2695   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2696
2697   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2698   {
2699     int nr = game_panel_order[i].nr;
2700     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2701     struct TextPosInfo *pos = gpc->pos;
2702     int type = gpc->type;
2703     int value = gpc->value;
2704     int frame = gpc->frame;
2705     int size = pos->size;
2706     int font = pos->font;
2707     boolean draw_masked = pos->draw_masked;
2708     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2709
2710     if (PANEL_DEACTIVATED(pos))
2711       continue;
2712
2713     if (pos->class == get_hash_from_key("extra_panel_items") &&
2714         !setup.prefer_extra_panel_items)
2715       continue;
2716
2717     gpc->last_value = value;
2718     gpc->last_frame = frame;
2719
2720     if (type == TYPE_INTEGER)
2721     {
2722       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2723           nr == GAME_PANEL_INVENTORY_COUNT ||
2724           nr == GAME_PANEL_SCORE ||
2725           nr == GAME_PANEL_HIGHSCORE ||
2726           nr == GAME_PANEL_TIME)
2727       {
2728         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2729
2730         if (use_dynamic_size)           // use dynamic number of digits
2731         {
2732           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2733                               nr == GAME_PANEL_INVENTORY_COUNT ||
2734                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2735           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2736                           nr == GAME_PANEL_INVENTORY_COUNT ||
2737                           nr == GAME_PANEL_TIME ? 1 : 2);
2738           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2739                        nr == GAME_PANEL_INVENTORY_COUNT ||
2740                        nr == GAME_PANEL_TIME ? 3 : 5);
2741           int size2 = size1 + size_add;
2742           int font1 = pos->font;
2743           int font2 = pos->font_alt;
2744
2745           size = (value < value_change ? size1 : size2);
2746           font = (value < value_change ? font1 : font2);
2747         }
2748       }
2749
2750       // correct text size if "digits" is zero or less
2751       if (size <= 0)
2752         size = strlen(int2str(value, size));
2753
2754       // dynamically correct text alignment
2755       pos->width = size * getFontWidth(font);
2756
2757       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2758                   int2str(value, size), font, mask_mode);
2759     }
2760     else if (type == TYPE_ELEMENT)
2761     {
2762       int element, graphic;
2763       Bitmap *src_bitmap;
2764       int src_x, src_y;
2765       int width, height;
2766       int dst_x = PANEL_XPOS(pos);
2767       int dst_y = PANEL_YPOS(pos);
2768
2769       if (value != EL_UNDEFINED && value != EL_EMPTY)
2770       {
2771         element = value;
2772         graphic = el2panelimg(value);
2773
2774 #if 0
2775         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2776               element, EL_NAME(element), size);
2777 #endif
2778
2779         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2780           size = TILESIZE;
2781
2782         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2783                               &src_x, &src_y);
2784
2785         width  = graphic_info[graphic].width  * size / TILESIZE;
2786         height = graphic_info[graphic].height * size / TILESIZE;
2787
2788         if (draw_masked)
2789           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2790                            dst_x, dst_y);
2791         else
2792           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2793                      dst_x, dst_y);
2794       }
2795     }
2796     else if (type == TYPE_GRAPHIC)
2797     {
2798       int graphic        = gpc->graphic;
2799       int graphic_active = gpc->graphic_active;
2800       Bitmap *src_bitmap;
2801       int src_x, src_y;
2802       int width, height;
2803       int dst_x = PANEL_XPOS(pos);
2804       int dst_y = PANEL_YPOS(pos);
2805       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2806                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2807
2808       if (graphic != IMG_UNDEFINED && !skip)
2809       {
2810         if (pos->style == STYLE_REVERSE)
2811           value = 100 - value;
2812
2813         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2814
2815         if (pos->direction & MV_HORIZONTAL)
2816         {
2817           width  = graphic_info[graphic_active].width * value / 100;
2818           height = graphic_info[graphic_active].height;
2819
2820           if (pos->direction == MV_LEFT)
2821           {
2822             src_x += graphic_info[graphic_active].width - width;
2823             dst_x += graphic_info[graphic_active].width - width;
2824           }
2825         }
2826         else
2827         {
2828           width  = graphic_info[graphic_active].width;
2829           height = graphic_info[graphic_active].height * value / 100;
2830
2831           if (pos->direction == MV_UP)
2832           {
2833             src_y += graphic_info[graphic_active].height - height;
2834             dst_y += graphic_info[graphic_active].height - height;
2835           }
2836         }
2837
2838         if (draw_masked)
2839           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2840                            dst_x, dst_y);
2841         else
2842           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2843                      dst_x, dst_y);
2844
2845         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2846
2847         if (pos->direction & MV_HORIZONTAL)
2848         {
2849           if (pos->direction == MV_RIGHT)
2850           {
2851             src_x += width;
2852             dst_x += width;
2853           }
2854           else
2855           {
2856             dst_x = PANEL_XPOS(pos);
2857           }
2858
2859           width = graphic_info[graphic].width - width;
2860         }
2861         else
2862         {
2863           if (pos->direction == MV_DOWN)
2864           {
2865             src_y += height;
2866             dst_y += height;
2867           }
2868           else
2869           {
2870             dst_y = PANEL_YPOS(pos);
2871           }
2872
2873           height = graphic_info[graphic].height - height;
2874         }
2875
2876         if (draw_masked)
2877           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2878                            dst_x, dst_y);
2879         else
2880           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2881                      dst_x, dst_y);
2882       }
2883     }
2884     else if (type == TYPE_STRING)
2885     {
2886       boolean active = (value != 0);
2887       char *state_normal = "off";
2888       char *state_active = "on";
2889       char *state = (active ? state_active : state_normal);
2890       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2891                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2892                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2893                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2894
2895       if (nr == GAME_PANEL_GRAVITY_STATE)
2896       {
2897         int font1 = pos->font;          // (used for normal state)
2898         int font2 = pos->font_alt;      // (used for active state)
2899
2900         font = (active ? font2 : font1);
2901       }
2902
2903       if (s != NULL)
2904       {
2905         char *s_cut;
2906
2907         if (size <= 0)
2908         {
2909           // don't truncate output if "chars" is zero or less
2910           size = strlen(s);
2911
2912           // dynamically correct text alignment
2913           pos->width = size * getFontWidth(font);
2914         }
2915
2916         s_cut = getStringCopyN(s, size);
2917
2918         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2919                     s_cut, font, mask_mode);
2920
2921         free(s_cut);
2922       }
2923     }
2924
2925     redraw_mask |= REDRAW_DOOR_1;
2926   }
2927
2928   SetGameStatus(GAME_MODE_PLAYING);
2929 }
2930
2931 void UpdateAndDisplayGameControlValues(void)
2932 {
2933   if (tape.deactivate_display)
2934     return;
2935
2936   UpdateGameControlValues();
2937   DisplayGameControlValues();
2938 }
2939
2940 void UpdateGameDoorValues(void)
2941 {
2942   UpdateGameControlValues();
2943 }
2944
2945 void DrawGameDoorValues(void)
2946 {
2947   DisplayGameControlValues();
2948 }
2949
2950
2951 // ============================================================================
2952 // InitGameEngine()
2953 // ----------------------------------------------------------------------------
2954 // initialize game engine due to level / tape version number
2955 // ============================================================================
2956
2957 static void InitGameEngine(void)
2958 {
2959   int i, j, k, l, x, y;
2960
2961   // set game engine from tape file when re-playing, else from level file
2962   game.engine_version = (tape.playing ? tape.engine_version :
2963                          level.game_version);
2964
2965   // set single or multi-player game mode (needed for re-playing tapes)
2966   game.team_mode = setup.team_mode;
2967
2968   if (tape.playing)
2969   {
2970     int num_players = 0;
2971
2972     for (i = 0; i < MAX_PLAYERS; i++)
2973       if (tape.player_participates[i])
2974         num_players++;
2975
2976     // multi-player tapes contain input data for more than one player
2977     game.team_mode = (num_players > 1);
2978   }
2979
2980 #if 0
2981   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2982         level.game_version);
2983   Debug("game:init:level", "          tape.file_version   == %06d",
2984         tape.file_version);
2985   Debug("game:init:level", "          tape.game_version   == %06d",
2986         tape.game_version);
2987   Debug("game:init:level", "          tape.engine_version == %06d",
2988         tape.engine_version);
2989   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2990         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2991 #endif
2992
2993   // --------------------------------------------------------------------------
2994   // set flags for bugs and changes according to active game engine version
2995   // --------------------------------------------------------------------------
2996
2997   /*
2998     Summary of bugfix:
2999     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3000
3001     Bug was introduced in version:
3002     2.0.1
3003
3004     Bug was fixed in version:
3005     4.2.0.0
3006
3007     Description:
3008     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3009     but the property "can fall" was missing, which caused some levels to be
3010     unsolvable. This was fixed in version 4.2.0.0.
3011
3012     Affected levels/tapes:
3013     An example for a tape that was fixed by this bugfix is tape 029 from the
3014     level set "rnd_sam_bateman".
3015     The wrong behaviour will still be used for all levels or tapes that were
3016     created/recorded with it. An example for this is tape 023 from the level
3017     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3018   */
3019
3020   boolean use_amoeba_dropping_cannot_fall_bug =
3021     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3022       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3023      (tape.playing &&
3024       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3025       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3026
3027   /*
3028     Summary of bugfix/change:
3029     Fixed move speed of elements entering or leaving magic wall.
3030
3031     Fixed/changed in version:
3032     2.0.1
3033
3034     Description:
3035     Before 2.0.1, move speed of elements entering or leaving magic wall was
3036     twice as fast as it is now.
3037     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3038
3039     Affected levels/tapes:
3040     The first condition is generally needed for all levels/tapes before version
3041     2.0.1, which might use the old behaviour before it was changed; known tapes
3042     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3043     The second condition is an exception from the above case and is needed for
3044     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3045     above, but before it was known that this change would break tapes like the
3046     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3047     although the engine version while recording maybe was before 2.0.1. There
3048     are a lot of tapes that are affected by this exception, like tape 006 from
3049     the level set "rnd_conor_mancone".
3050   */
3051
3052   boolean use_old_move_stepsize_for_magic_wall =
3053     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3054      !(tape.playing &&
3055        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3056        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3057
3058   /*
3059     Summary of bugfix/change:
3060     Fixed handling for custom elements that change when pushed by the player.
3061
3062     Fixed/changed in version:
3063     3.1.0
3064
3065     Description:
3066     Before 3.1.0, custom elements that "change when pushing" changed directly
3067     after the player started pushing them (until then handled in "DigField()").
3068     Since 3.1.0, these custom elements are not changed until the "pushing"
3069     move of the element is finished (now handled in "ContinueMoving()").
3070
3071     Affected levels/tapes:
3072     The first condition is generally needed for all levels/tapes before version
3073     3.1.0, which might use the old behaviour before it was changed; known tapes
3074     that are affected are some tapes from the level set "Walpurgis Gardens" by
3075     Jamie Cullen.
3076     The second condition is an exception from the above case and is needed for
3077     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3078     above (including some development versions of 3.1.0), but before it was
3079     known that this change would break tapes like the above and was fixed in
3080     3.1.1, so that the changed behaviour was active although the engine version
3081     while recording maybe was before 3.1.0. There is at least one tape that is
3082     affected by this exception, which is the tape for the one-level set "Bug
3083     Machine" by Juergen Bonhagen.
3084   */
3085
3086   game.use_change_when_pushing_bug =
3087     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3088      !(tape.playing &&
3089        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3090        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3091
3092   /*
3093     Summary of bugfix/change:
3094     Fixed handling for blocking the field the player leaves when moving.
3095
3096     Fixed/changed in version:
3097     3.1.1
3098
3099     Description:
3100     Before 3.1.1, when "block last field when moving" was enabled, the field
3101     the player is leaving when moving was blocked for the time of the move,
3102     and was directly unblocked afterwards. This resulted in the last field
3103     being blocked for exactly one less than the number of frames of one player
3104     move. Additionally, even when blocking was disabled, the last field was
3105     blocked for exactly one frame.
3106     Since 3.1.1, due to changes in player movement handling, the last field
3107     is not blocked at all when blocking is disabled. When blocking is enabled,
3108     the last field is blocked for exactly the number of frames of one player
3109     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3110     last field is blocked for exactly one more than the number of frames of
3111     one player move.
3112
3113     Affected levels/tapes:
3114     (!!! yet to be determined -- probably many !!!)
3115   */
3116
3117   game.use_block_last_field_bug =
3118     (game.engine_version < VERSION_IDENT(3,1,1,0));
3119
3120   /* various special flags and settings for native Emerald Mine game engine */
3121
3122   game_em.use_single_button =
3123     (game.engine_version > VERSION_IDENT(4,0,0,2));
3124
3125   game_em.use_push_delay =
3126     (game.engine_version > VERSION_IDENT(4,3,7,1));
3127
3128   game_em.use_snap_key_bug =
3129     (game.engine_version < VERSION_IDENT(4,0,1,0));
3130
3131   game_em.use_random_bug =
3132     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3133
3134   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3135
3136   game_em.use_old_explosions            = use_old_em_engine;
3137   game_em.use_old_android               = use_old_em_engine;
3138   game_em.use_old_push_elements         = use_old_em_engine;
3139   game_em.use_old_push_into_acid        = use_old_em_engine;
3140
3141   game_em.use_wrap_around               = !use_old_em_engine;
3142
3143   // --------------------------------------------------------------------------
3144
3145   // set maximal allowed number of custom element changes per game frame
3146   game.max_num_changes_per_frame = 1;
3147
3148   // default scan direction: scan playfield from top/left to bottom/right
3149   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3150
3151   // dynamically adjust element properties according to game engine version
3152   InitElementPropertiesEngine(game.engine_version);
3153
3154   // ---------- initialize special element properties -------------------------
3155
3156   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3157   if (use_amoeba_dropping_cannot_fall_bug)
3158     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3159
3160   // ---------- initialize player's initial move delay ------------------------
3161
3162   // dynamically adjust player properties according to level information
3163   for (i = 0; i < MAX_PLAYERS; i++)
3164     game.initial_move_delay_value[i] =
3165       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3166
3167   // dynamically adjust player properties according to game engine version
3168   for (i = 0; i < MAX_PLAYERS; i++)
3169     game.initial_move_delay[i] =
3170       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3171        game.initial_move_delay_value[i] : 0);
3172
3173   // ---------- initialize player's initial push delay ------------------------
3174
3175   // dynamically adjust player properties according to game engine version
3176   game.initial_push_delay_value =
3177     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3178
3179   // ---------- initialize changing elements ----------------------------------
3180
3181   // initialize changing elements information
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     struct ElementInfo *ei = &element_info[i];
3185
3186     // this pointer might have been changed in the level editor
3187     ei->change = &ei->change_page[0];
3188
3189     if (!IS_CUSTOM_ELEMENT(i))
3190     {
3191       ei->change->target_element = EL_EMPTY_SPACE;
3192       ei->change->delay_fixed = 0;
3193       ei->change->delay_random = 0;
3194       ei->change->delay_frames = 1;
3195     }
3196
3197     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3198     {
3199       ei->has_change_event[j] = FALSE;
3200
3201       ei->event_page_nr[j] = 0;
3202       ei->event_page[j] = &ei->change_page[0];
3203     }
3204   }
3205
3206   // add changing elements from pre-defined list
3207   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3208   {
3209     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3210     struct ElementInfo *ei = &element_info[ch_delay->element];
3211
3212     ei->change->target_element       = ch_delay->target_element;
3213     ei->change->delay_fixed          = ch_delay->change_delay;
3214
3215     ei->change->pre_change_function  = ch_delay->pre_change_function;
3216     ei->change->change_function      = ch_delay->change_function;
3217     ei->change->post_change_function = ch_delay->post_change_function;
3218
3219     ei->change->can_change = TRUE;
3220     ei->change->can_change_or_has_action = TRUE;
3221
3222     ei->has_change_event[CE_DELAY] = TRUE;
3223
3224     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3225     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3226   }
3227
3228   // ---------- initialize if element can trigger global animations -----------
3229
3230   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3231   {
3232     struct ElementInfo *ei = &element_info[i];
3233
3234     ei->has_anim_event = FALSE;
3235   }
3236
3237   InitGlobalAnimEventsForCustomElements();
3238
3239   // ---------- initialize internal run-time variables ------------------------
3240
3241   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3242   {
3243     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3244
3245     for (j = 0; j < ei->num_change_pages; j++)
3246     {
3247       ei->change_page[j].can_change_or_has_action =
3248         (ei->change_page[j].can_change |
3249          ei->change_page[j].has_action);
3250     }
3251   }
3252
3253   // add change events from custom element configuration
3254   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3255   {
3256     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3257
3258     for (j = 0; j < ei->num_change_pages; j++)
3259     {
3260       if (!ei->change_page[j].can_change_or_has_action)
3261         continue;
3262
3263       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3264       {
3265         // only add event page for the first page found with this event
3266         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3267         {
3268           ei->has_change_event[k] = TRUE;
3269
3270           ei->event_page_nr[k] = j;
3271           ei->event_page[k] = &ei->change_page[j];
3272         }
3273       }
3274     }
3275   }
3276
3277   // ---------- initialize reference elements in change conditions ------------
3278
3279   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3280   {
3281     int element = EL_CUSTOM_START + i;
3282     struct ElementInfo *ei = &element_info[element];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       int trigger_element = ei->change_page[j].initial_trigger_element;
3287
3288       if (trigger_element >= EL_PREV_CE_8 &&
3289           trigger_element <= EL_NEXT_CE_8)
3290         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3291
3292       ei->change_page[j].trigger_element = trigger_element;
3293     }
3294   }
3295
3296   // ---------- initialize run-time trigger player and element ----------------
3297
3298   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3299   {
3300     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3301
3302     for (j = 0; j < ei->num_change_pages; j++)
3303     {
3304       struct ElementChangeInfo *change = &ei->change_page[j];
3305
3306       change->actual_trigger_element = EL_EMPTY;
3307       change->actual_trigger_player = EL_EMPTY;
3308       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3309       change->actual_trigger_side = CH_SIDE_NONE;
3310       change->actual_trigger_ce_value = 0;
3311       change->actual_trigger_ce_score = 0;
3312       change->actual_trigger_x = -1;
3313       change->actual_trigger_y = -1;
3314     }
3315   }
3316
3317   // ---------- initialize trigger events -------------------------------------
3318
3319   // initialize trigger events information
3320   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3321     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3322       trigger_events[i][j] = FALSE;
3323
3324   // add trigger events from element change event properties
3325   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3326   {
3327     struct ElementInfo *ei = &element_info[i];
3328
3329     for (j = 0; j < ei->num_change_pages; j++)
3330     {
3331       struct ElementChangeInfo *change = &ei->change_page[j];
3332
3333       if (!change->can_change_or_has_action)
3334         continue;
3335
3336       if (change->has_event[CE_BY_OTHER_ACTION])
3337       {
3338         int trigger_element = change->trigger_element;
3339
3340         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3341         {
3342           if (change->has_event[k])
3343           {
3344             if (IS_GROUP_ELEMENT(trigger_element))
3345             {
3346               struct ElementGroupInfo *group =
3347                 element_info[trigger_element].group;
3348
3349               for (l = 0; l < group->num_elements_resolved; l++)
3350                 trigger_events[group->element_resolved[l]][k] = TRUE;
3351             }
3352             else if (trigger_element == EL_ANY_ELEMENT)
3353               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3354                 trigger_events[l][k] = TRUE;
3355             else
3356               trigger_events[trigger_element][k] = TRUE;
3357           }
3358         }
3359       }
3360     }
3361   }
3362
3363   // ---------- initialize push delay -----------------------------------------
3364
3365   // initialize push delay values to default
3366   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3367   {
3368     if (!IS_CUSTOM_ELEMENT(i))
3369     {
3370       // set default push delay values (corrected since version 3.0.7-1)
3371       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3372       {
3373         element_info[i].push_delay_fixed = 2;
3374         element_info[i].push_delay_random = 8;
3375       }
3376       else
3377       {
3378         element_info[i].push_delay_fixed = 8;
3379         element_info[i].push_delay_random = 8;
3380       }
3381     }
3382   }
3383
3384   // set push delay value for certain elements from pre-defined list
3385   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = push_delay_list[i].element;
3388
3389     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3390     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3391   }
3392
3393   // set push delay value for Supaplex elements for newer engine versions
3394   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3395   {
3396     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3397     {
3398       if (IS_SP_ELEMENT(i))
3399       {
3400         // set SP push delay to just enough to push under a falling zonk
3401         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3402
3403         element_info[i].push_delay_fixed  = delay;
3404         element_info[i].push_delay_random = 0;
3405       }
3406     }
3407   }
3408
3409   // ---------- initialize move stepsize --------------------------------------
3410
3411   // initialize move stepsize values to default
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3415
3416   // set move stepsize value for certain elements from pre-defined list
3417   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3418   {
3419     int e = move_stepsize_list[i].element;
3420
3421     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3422
3423     // set move stepsize value for certain elements for older engine versions
3424     if (use_old_move_stepsize_for_magic_wall)
3425     {
3426       if (e == EL_MAGIC_WALL_FILLING ||
3427           e == EL_MAGIC_WALL_EMPTYING ||
3428           e == EL_BD_MAGIC_WALL_FILLING ||
3429           e == EL_BD_MAGIC_WALL_EMPTYING)
3430         element_info[e].move_stepsize *= 2;
3431     }
3432   }
3433
3434   // ---------- initialize collect score --------------------------------------
3435
3436   // initialize collect score values for custom elements from initial value
3437   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3438     if (IS_CUSTOM_ELEMENT(i))
3439       element_info[i].collect_score = element_info[i].collect_score_initial;
3440
3441   // ---------- initialize collect count --------------------------------------
3442
3443   // initialize collect count values for non-custom elements
3444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445     if (!IS_CUSTOM_ELEMENT(i))
3446       element_info[i].collect_count_initial = 0;
3447
3448   // add collect count values for all elements from pre-defined list
3449   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3450     element_info[collect_count_list[i].element].collect_count_initial =
3451       collect_count_list[i].count;
3452
3453   // ---------- initialize access direction -----------------------------------
3454
3455   // initialize access direction values to default (access from every side)
3456   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457     if (!IS_CUSTOM_ELEMENT(i))
3458       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3459
3460   // set access direction value for certain elements from pre-defined list
3461   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3462     element_info[access_direction_list[i].element].access_direction =
3463       access_direction_list[i].direction;
3464
3465   // ---------- initialize explosion content ----------------------------------
3466   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3467   {
3468     if (IS_CUSTOM_ELEMENT(i))
3469       continue;
3470
3471     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3472     {
3473       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3474
3475       element_info[i].content.e[x][y] =
3476         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3477          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3478          i == EL_PLAYER_3 ? EL_EMERALD :
3479          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3480          i == EL_MOLE ? EL_EMERALD_RED :
3481          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3482          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3483          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3484          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3485          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3486          i == EL_WALL_EMERALD ? EL_EMERALD :
3487          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3488          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3489          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3490          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3491          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3492          i == EL_WALL_PEARL ? EL_PEARL :
3493          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3494          EL_EMPTY);
3495     }
3496   }
3497
3498   // ---------- initialize recursion detection --------------------------------
3499   recursion_loop_depth = 0;
3500   recursion_loop_detected = FALSE;
3501   recursion_loop_element = EL_UNDEFINED;
3502
3503   // ---------- initialize graphics engine ------------------------------------
3504   game.scroll_delay_value =
3505     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3506      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3507      !setup.forced_scroll_delay           ? 0 :
3508      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3509   if (game.forced_scroll_delay_value == -1)
3510     game.scroll_delay_value =
3511       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3512
3513   // ---------- initialize game engine snapshots ------------------------------
3514   for (i = 0; i < MAX_PLAYERS; i++)
3515     game.snapshot.last_action[i] = 0;
3516   game.snapshot.changed_action = FALSE;
3517   game.snapshot.collected_item = FALSE;
3518   game.snapshot.mode =
3519     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3520      SNAPSHOT_MODE_EVERY_STEP :
3521      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3522      SNAPSHOT_MODE_EVERY_MOVE :
3523      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3524      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3525   game.snapshot.save_snapshot = FALSE;
3526
3527   // ---------- initialize level time for Supaplex engine ---------------------
3528   // Supaplex levels with time limit currently unsupported -- should be added
3529   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3530     level.time = 0;
3531
3532   // ---------- initialize flags for handling game actions --------------------
3533
3534   // set flags for game actions to default values
3535   game.use_key_actions = TRUE;
3536   game.use_mouse_actions = FALSE;
3537
3538   // when using Mirror Magic game engine, handle mouse events only
3539   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3540   {
3541     game.use_key_actions = FALSE;
3542     game.use_mouse_actions = TRUE;
3543   }
3544
3545   // check for custom elements with mouse click events
3546   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3547   {
3548     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3549     {
3550       int element = EL_CUSTOM_START + i;
3551
3552       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3553           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3554           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3555           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3556         game.use_mouse_actions = TRUE;
3557     }
3558   }
3559 }
3560
3561 static int get_num_special_action(int element, int action_first,
3562                                   int action_last)
3563 {
3564   int num_special_action = 0;
3565   int i, j;
3566
3567   for (i = action_first; i <= action_last; i++)
3568   {
3569     boolean found = FALSE;
3570
3571     for (j = 0; j < NUM_DIRECTIONS; j++)
3572       if (el_act_dir2img(element, i, j) !=
3573           el_act_dir2img(element, ACTION_DEFAULT, j))
3574         found = TRUE;
3575
3576     if (found)
3577       num_special_action++;
3578     else
3579       break;
3580   }
3581
3582   return num_special_action;
3583 }
3584
3585
3586 // ============================================================================
3587 // InitGame()
3588 // ----------------------------------------------------------------------------
3589 // initialize and start new game
3590 // ============================================================================
3591
3592 #if DEBUG_INIT_PLAYER
3593 static void DebugPrintPlayerStatus(char *message)
3594 {
3595   int i;
3596
3597   if (!options.debug)
3598     return;
3599
3600   Debug("game:init:player", "%s:", message);
3601
3602   for (i = 0; i < MAX_PLAYERS; i++)
3603   {
3604     struct PlayerInfo *player = &stored_player[i];
3605
3606     Debug("game:init:player",
3607           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3608           i + 1,
3609           player->present,
3610           player->connected,
3611           player->connected_locally,
3612           player->connected_network,
3613           player->active,
3614           (local_player == player ? " (local player)" : ""));
3615   }
3616 }
3617 #endif
3618
3619 void InitGame(void)
3620 {
3621   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3622   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3623   int fade_mask = REDRAW_FIELD;
3624   boolean restarting = (game_status == GAME_MODE_PLAYING);
3625   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3626   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3627   int initial_move_dir = MV_DOWN;
3628   int i, j, x, y;
3629
3630   // required here to update video display before fading (FIX THIS)
3631   DrawMaskedBorder(REDRAW_DOOR_2);
3632
3633   if (!game.restart_level)
3634     CloseDoor(DOOR_CLOSE_1);
3635
3636   if (restarting)
3637   {
3638     // force fading out global animations displayed during game play
3639     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3640   }
3641   else
3642   {
3643     SetGameStatus(GAME_MODE_PLAYING);
3644   }
3645
3646   if (level_editor_test_game)
3647     FadeSkipNextFadeOut();
3648   else
3649     FadeSetEnterScreen();
3650
3651   if (CheckFadeAll())
3652     fade_mask = REDRAW_ALL;
3653
3654   FadeLevelSoundsAndMusic();
3655
3656   ExpireSoundLoops(TRUE);
3657
3658   FadeOut(fade_mask);
3659
3660   if (restarting)
3661   {
3662     // force restarting global animations displayed during game play
3663     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3664
3665     // this is required for "transforming" fade modes like cross-fading
3666     // (else global animations will be stopped, but not restarted here)
3667     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3668
3669     SetGameStatus(GAME_MODE_PLAYING);
3670   }
3671
3672   if (level_editor_test_game)
3673     FadeSkipNextFadeIn();
3674
3675   // needed if different viewport properties defined for playing
3676   ChangeViewportPropertiesIfNeeded();
3677
3678   ClearField();
3679
3680   DrawCompleteVideoDisplay();
3681
3682   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3683
3684   InitGameEngine();
3685   InitGameControlValues();
3686
3687   if (tape.recording)
3688   {
3689     // initialize tape actions from game when recording tape
3690     tape.use_key_actions   = game.use_key_actions;
3691     tape.use_mouse_actions = game.use_mouse_actions;
3692
3693     // initialize visible playfield size when recording tape (for team mode)
3694     tape.scr_fieldx = SCR_FIELDX;
3695     tape.scr_fieldy = SCR_FIELDY;
3696   }
3697
3698   // don't play tapes over network
3699   network_playing = (network.enabled && !tape.playing);
3700
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     struct PlayerInfo *player = &stored_player[i];
3704
3705     player->index_nr = i;
3706     player->index_bit = (1 << i);
3707     player->element_nr = EL_PLAYER_1 + i;
3708
3709     player->present = FALSE;
3710     player->active = FALSE;
3711     player->mapped = FALSE;
3712
3713     player->killed = FALSE;
3714     player->reanimated = FALSE;
3715     player->buried = FALSE;
3716
3717     player->action = 0;
3718     player->effective_action = 0;
3719     player->programmed_action = 0;
3720     player->snap_action = 0;
3721
3722     player->mouse_action.lx = 0;
3723     player->mouse_action.ly = 0;
3724     player->mouse_action.button = 0;
3725     player->mouse_action.button_hint = 0;
3726
3727     player->effective_mouse_action.lx = 0;
3728     player->effective_mouse_action.ly = 0;
3729     player->effective_mouse_action.button = 0;
3730     player->effective_mouse_action.button_hint = 0;
3731
3732     for (j = 0; j < MAX_NUM_KEYS; j++)
3733       player->key[j] = FALSE;
3734
3735     player->num_white_keys = 0;
3736
3737     player->dynabomb_count = 0;
3738     player->dynabomb_size = 1;
3739     player->dynabombs_left = 0;
3740     player->dynabomb_xl = FALSE;
3741
3742     player->MovDir = initial_move_dir;
3743     player->MovPos = 0;
3744     player->GfxPos = 0;
3745     player->GfxDir = initial_move_dir;
3746     player->GfxAction = ACTION_DEFAULT;
3747     player->Frame = 0;
3748     player->StepFrame = 0;
3749
3750     player->initial_element = player->element_nr;
3751     player->artwork_element =
3752       (level.use_artwork_element[i] ? level.artwork_element[i] :
3753        player->element_nr);
3754     player->use_murphy = FALSE;
3755
3756     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3757     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3758
3759     player->gravity = level.initial_player_gravity[i];
3760
3761     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3762
3763     player->actual_frame_counter.count = 0;
3764     player->actual_frame_counter.value = 1;
3765
3766     player->step_counter = 0;
3767
3768     player->last_move_dir = initial_move_dir;
3769
3770     player->is_active = FALSE;
3771
3772     player->is_waiting = FALSE;
3773     player->is_moving = FALSE;
3774     player->is_auto_moving = FALSE;
3775     player->is_digging = FALSE;
3776     player->is_snapping = FALSE;
3777     player->is_collecting = FALSE;
3778     player->is_pushing = FALSE;
3779     player->is_switching = FALSE;
3780     player->is_dropping = FALSE;
3781     player->is_dropping_pressed = FALSE;
3782
3783     player->is_bored = FALSE;
3784     player->is_sleeping = FALSE;
3785
3786     player->was_waiting = TRUE;
3787     player->was_moving = FALSE;
3788     player->was_snapping = FALSE;
3789     player->was_dropping = FALSE;
3790
3791     player->force_dropping = FALSE;
3792
3793     player->frame_counter_bored = -1;
3794     player->frame_counter_sleeping = -1;
3795
3796     player->anim_delay_counter = 0;
3797     player->post_delay_counter = 0;
3798
3799     player->dir_waiting = initial_move_dir;
3800     player->action_waiting = ACTION_DEFAULT;
3801     player->last_action_waiting = ACTION_DEFAULT;
3802     player->special_action_bored = ACTION_DEFAULT;
3803     player->special_action_sleeping = ACTION_DEFAULT;
3804
3805     player->switch_x = -1;
3806     player->switch_y = -1;
3807
3808     player->drop_x = -1;
3809     player->drop_y = -1;
3810
3811     player->show_envelope = 0;
3812
3813     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3814
3815     player->push_delay       = -1;      // initialized when pushing starts
3816     player->push_delay_value = game.initial_push_delay_value;
3817
3818     player->drop_delay = 0;
3819     player->drop_pressed_delay = 0;
3820
3821     player->last_jx = -1;
3822     player->last_jy = -1;
3823     player->jx = -1;
3824     player->jy = -1;
3825
3826     player->shield_normal_time_left = 0;
3827     player->shield_deadly_time_left = 0;
3828
3829     player->last_removed_element = EL_UNDEFINED;
3830
3831     player->inventory_infinite_element = EL_UNDEFINED;
3832     player->inventory_size = 0;
3833
3834     if (level.use_initial_inventory[i])
3835     {
3836       for (j = 0; j < level.initial_inventory_size[i]; j++)
3837       {
3838         int element = level.initial_inventory_content[i][j];
3839         int collect_count = element_info[element].collect_count_initial;
3840         int k;
3841
3842         if (!IS_CUSTOM_ELEMENT(element))
3843           collect_count = 1;
3844
3845         if (collect_count == 0)
3846           player->inventory_infinite_element = element;
3847         else
3848           for (k = 0; k < collect_count; k++)
3849             if (player->inventory_size < MAX_INVENTORY_SIZE)
3850               player->inventory_element[player->inventory_size++] = element;
3851       }
3852     }
3853
3854     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3855     SnapField(player, 0, 0);
3856
3857     map_player_action[i] = i;
3858   }
3859
3860   network_player_action_received = FALSE;
3861
3862   // initial null action
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865
3866   FrameCounter = 0;
3867   TimeFrames = 0;
3868   TimePlayed = 0;
3869   TimeLeft = level.time;
3870
3871   TapeTimeFrames = 0;
3872   TapeTime = 0;
3873
3874   ScreenMovDir = MV_NONE;
3875   ScreenMovPos = 0;
3876   ScreenGfxPos = 0;
3877
3878   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3879
3880   game.robot_wheel_x = -1;
3881   game.robot_wheel_y = -1;
3882
3883   game.exit_x = -1;
3884   game.exit_y = -1;
3885
3886   game.all_players_gone = FALSE;
3887
3888   game.LevelSolved = FALSE;
3889   game.GameOver = FALSE;
3890
3891   game.GamePlayed = !tape.playing;
3892
3893   game.LevelSolved_GameWon = FALSE;
3894   game.LevelSolved_GameEnd = FALSE;
3895   game.LevelSolved_SaveTape = FALSE;
3896   game.LevelSolved_SaveScore = FALSE;
3897
3898   game.LevelSolved_CountingTime = 0;
3899   game.LevelSolved_CountingScore = 0;
3900   game.LevelSolved_CountingHealth = 0;
3901
3902   game.RestartGameRequested = FALSE;
3903
3904   game.panel.active = TRUE;
3905
3906   game.no_level_time_limit = (level.time == 0);
3907   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3908
3909   game.yamyam_content_nr = 0;
3910   game.robot_wheel_active = FALSE;
3911   game.magic_wall_active = FALSE;
3912   game.magic_wall_time_left = 0;
3913   game.light_time_left = 0;
3914   game.timegate_time_left = 0;
3915   game.switchgate_pos = 0;
3916   game.wind_direction = level.wind_direction_initial;
3917
3918   game.time_final = 0;
3919   game.score_time_final = 0;
3920
3921   game.score = 0;
3922   game.score_final = 0;
3923
3924   game.health = MAX_HEALTH;
3925   game.health_final = MAX_HEALTH;
3926
3927   game.gems_still_needed = level.gems_needed;
3928   game.sokoban_fields_still_needed = 0;
3929   game.sokoban_objects_still_needed = 0;
3930   game.lights_still_needed = 0;
3931   game.players_still_needed = 0;
3932   game.friends_still_needed = 0;
3933
3934   game.lenses_time_left = 0;
3935   game.magnify_time_left = 0;
3936
3937   game.ball_active = level.ball_active_initial;
3938   game.ball_content_nr = 0;
3939
3940   game.explosions_delayed = TRUE;
3941
3942   game.envelope_active = FALSE;
3943
3944   // special case: set custom artwork setting to initial value
3945   game.use_masked_elements = game.use_masked_elements_initial;
3946
3947   for (i = 0; i < NUM_BELTS; i++)
3948   {
3949     game.belt_dir[i] = MV_NONE;
3950     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3951   }
3952
3953   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3954     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3955
3956 #if DEBUG_INIT_PLAYER
3957   DebugPrintPlayerStatus("Player status at level initialization");
3958 #endif
3959
3960   SCAN_PLAYFIELD(x, y)
3961   {
3962     Tile[x][y] = Last[x][y] = level.field[x][y];
3963     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3964     ChangeDelay[x][y] = 0;
3965     ChangePage[x][y] = -1;
3966     CustomValue[x][y] = 0;              // initialized in InitField()
3967     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3968     AmoebaNr[x][y] = 0;
3969     WasJustMoving[x][y] = 0;
3970     WasJustFalling[x][y] = 0;
3971     CheckCollision[x][y] = 0;
3972     CheckImpact[x][y] = 0;
3973     Stop[x][y] = FALSE;
3974     Pushed[x][y] = FALSE;
3975
3976     ChangeCount[x][y] = 0;
3977     ChangeEvent[x][y] = -1;
3978
3979     ExplodePhase[x][y] = 0;
3980     ExplodeDelay[x][y] = 0;
3981     ExplodeField[x][y] = EX_TYPE_NONE;
3982
3983     RunnerVisit[x][y] = 0;
3984     PlayerVisit[x][y] = 0;
3985
3986     GfxFrame[x][y] = 0;
3987     GfxRandom[x][y] = INIT_GFX_RANDOM();
3988     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3989     GfxElement[x][y] = EL_UNDEFINED;
3990     GfxElementEmpty[x][y] = EL_EMPTY;
3991     GfxAction[x][y] = ACTION_DEFAULT;
3992     GfxDir[x][y] = MV_NONE;
3993     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3994   }
3995
3996   SCAN_PLAYFIELD(x, y)
3997   {
3998     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3999       emulate_bd = FALSE;
4000     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4001       emulate_sp = FALSE;
4002
4003     InitField(x, y, TRUE);
4004
4005     ResetGfxAnimation(x, y);
4006   }
4007
4008   InitBeltMovement();
4009
4010   // required if level does not contain any "empty space" element
4011   if (element_info[EL_EMPTY].use_gfx_element)
4012     game.use_masked_elements = TRUE;
4013
4014   for (i = 0; i < MAX_PLAYERS; i++)
4015   {
4016     struct PlayerInfo *player = &stored_player[i];
4017
4018     // set number of special actions for bored and sleeping animation
4019     player->num_special_action_bored =
4020       get_num_special_action(player->artwork_element,
4021                              ACTION_BORING_1, ACTION_BORING_LAST);
4022     player->num_special_action_sleeping =
4023       get_num_special_action(player->artwork_element,
4024                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4025   }
4026
4027   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4028                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4029
4030   // initialize type of slippery elements
4031   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4032   {
4033     if (!IS_CUSTOM_ELEMENT(i))
4034     {
4035       // default: elements slip down either to the left or right randomly
4036       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4037
4038       // SP style elements prefer to slip down on the left side
4039       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4040         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4041
4042       // BD style elements prefer to slip down on the left side
4043       if (game.emulation == EMU_BOULDERDASH)
4044         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4045     }
4046   }
4047
4048   // initialize explosion and ignition delay
4049   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4050   {
4051     if (!IS_CUSTOM_ELEMENT(i))
4052     {
4053       int num_phase = 8;
4054       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4055                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4056                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4057       int last_phase = (num_phase + 1) * delay;
4058       int half_phase = (num_phase / 2) * delay;
4059
4060       element_info[i].explosion_delay = last_phase - 1;
4061       element_info[i].ignition_delay = half_phase;
4062
4063       if (i == EL_BLACK_ORB)
4064         element_info[i].ignition_delay = 1;
4065     }
4066   }
4067
4068   // correct non-moving belts to start moving left
4069   for (i = 0; i < NUM_BELTS; i++)
4070     if (game.belt_dir[i] == MV_NONE)
4071       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4072
4073 #if USE_NEW_PLAYER_ASSIGNMENTS
4074   // use preferred player also in local single-player mode
4075   if (!network.enabled && !game.team_mode)
4076   {
4077     int new_index_nr = setup.network_player_nr;
4078
4079     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4080     {
4081       for (i = 0; i < MAX_PLAYERS; i++)
4082         stored_player[i].connected_locally = FALSE;
4083
4084       stored_player[new_index_nr].connected_locally = TRUE;
4085     }
4086   }
4087
4088   for (i = 0; i < MAX_PLAYERS; i++)
4089   {
4090     stored_player[i].connected = FALSE;
4091
4092     // in network game mode, the local player might not be the first player
4093     if (stored_player[i].connected_locally)
4094       local_player = &stored_player[i];
4095   }
4096
4097   if (!network.enabled)
4098     local_player->connected = TRUE;
4099
4100   if (tape.playing)
4101   {
4102     for (i = 0; i < MAX_PLAYERS; i++)
4103       stored_player[i].connected = tape.player_participates[i];
4104   }
4105   else if (network.enabled)
4106   {
4107     // add team mode players connected over the network (needed for correct
4108     // assignment of player figures from level to locally playing players)
4109
4110     for (i = 0; i < MAX_PLAYERS; i++)
4111       if (stored_player[i].connected_network)
4112         stored_player[i].connected = TRUE;
4113   }
4114   else if (game.team_mode)
4115   {
4116     // try to guess locally connected team mode players (needed for correct
4117     // assignment of player figures from level to locally playing players)
4118
4119     for (i = 0; i < MAX_PLAYERS; i++)
4120       if (setup.input[i].use_joystick ||
4121           setup.input[i].key.left != KSYM_UNDEFINED)
4122         stored_player[i].connected = TRUE;
4123   }
4124
4125 #if DEBUG_INIT_PLAYER
4126   DebugPrintPlayerStatus("Player status after level initialization");
4127 #endif
4128
4129 #if DEBUG_INIT_PLAYER
4130   Debug("game:init:player", "Reassigning players ...");
4131 #endif
4132
4133   // check if any connected player was not found in playfield
4134   for (i = 0; i < MAX_PLAYERS; i++)
4135   {
4136     struct PlayerInfo *player = &stored_player[i];
4137
4138     if (player->connected && !player->present)
4139     {
4140       struct PlayerInfo *field_player = NULL;
4141
4142 #if DEBUG_INIT_PLAYER
4143       Debug("game:init:player",
4144             "- looking for field player for player %d ...", i + 1);
4145 #endif
4146
4147       // assign first free player found that is present in the playfield
4148
4149       // first try: look for unmapped playfield player that is not connected
4150       for (j = 0; j < MAX_PLAYERS; j++)
4151         if (field_player == NULL &&
4152             stored_player[j].present &&
4153             !stored_player[j].mapped &&
4154             !stored_player[j].connected)
4155           field_player = &stored_player[j];
4156
4157       // second try: look for *any* unmapped playfield player
4158       for (j = 0; j < MAX_PLAYERS; j++)
4159         if (field_player == NULL &&
4160             stored_player[j].present &&
4161             !stored_player[j].mapped)
4162           field_player = &stored_player[j];
4163
4164       if (field_player != NULL)
4165       {
4166         int jx = field_player->jx, jy = field_player->jy;
4167
4168 #if DEBUG_INIT_PLAYER
4169         Debug("game:init:player", "- found player %d",
4170               field_player->index_nr + 1);
4171 #endif
4172
4173         player->present = FALSE;
4174         player->active = FALSE;
4175
4176         field_player->present = TRUE;
4177         field_player->active = TRUE;
4178
4179         /*
4180         player->initial_element = field_player->initial_element;
4181         player->artwork_element = field_player->artwork_element;
4182
4183         player->block_last_field       = field_player->block_last_field;
4184         player->block_delay_adjustment = field_player->block_delay_adjustment;
4185         */
4186
4187         StorePlayer[jx][jy] = field_player->element_nr;
4188
4189         field_player->jx = field_player->last_jx = jx;
4190         field_player->jy = field_player->last_jy = jy;
4191
4192         if (local_player == player)
4193           local_player = field_player;
4194
4195         map_player_action[field_player->index_nr] = i;
4196
4197         field_player->mapped = TRUE;
4198
4199 #if DEBUG_INIT_PLAYER
4200         Debug("game:init:player", "- map_player_action[%d] == %d",
4201               field_player->index_nr + 1, i + 1);
4202 #endif
4203       }
4204     }
4205
4206     if (player->connected && player->present)
4207       player->mapped = TRUE;
4208   }
4209
4210 #if DEBUG_INIT_PLAYER
4211   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4212 #endif
4213
4214 #else
4215
4216   // check if any connected player was not found in playfield
4217   for (i = 0; i < MAX_PLAYERS; i++)
4218   {
4219     struct PlayerInfo *player = &stored_player[i];
4220
4221     if (player->connected && !player->present)
4222     {
4223       for (j = 0; j < MAX_PLAYERS; j++)
4224       {
4225         struct PlayerInfo *field_player = &stored_player[j];
4226         int jx = field_player->jx, jy = field_player->jy;
4227
4228         // assign first free player found that is present in the playfield
4229         if (field_player->present && !field_player->connected)
4230         {
4231           player->present = TRUE;
4232           player->active = TRUE;
4233
4234           field_player->present = FALSE;
4235           field_player->active = FALSE;
4236
4237           player->initial_element = field_player->initial_element;
4238           player->artwork_element = field_player->artwork_element;
4239
4240           player->block_last_field       = field_player->block_last_field;
4241           player->block_delay_adjustment = field_player->block_delay_adjustment;
4242
4243           StorePlayer[jx][jy] = player->element_nr;
4244
4245           player->jx = player->last_jx = jx;
4246           player->jy = player->last_jy = jy;
4247
4248           break;
4249         }
4250       }
4251     }
4252   }
4253 #endif
4254
4255 #if 0
4256   Debug("game:init:player", "local_player->present == %d",
4257         local_player->present);
4258 #endif
4259
4260   // set focus to local player for network games, else to all players
4261   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4262   game.centered_player_nr_next = game.centered_player_nr;
4263   game.set_centered_player = FALSE;
4264   game.set_centered_player_wrap = FALSE;
4265
4266   if (network_playing && tape.recording)
4267   {
4268     // store client dependent player focus when recording network games
4269     tape.centered_player_nr_next = game.centered_player_nr_next;
4270     tape.set_centered_player = TRUE;
4271   }
4272
4273   if (tape.playing)
4274   {
4275     // when playing a tape, eliminate all players who do not participate
4276
4277 #if USE_NEW_PLAYER_ASSIGNMENTS
4278
4279     if (!game.team_mode)
4280     {
4281       for (i = 0; i < MAX_PLAYERS; i++)
4282       {
4283         if (stored_player[i].active &&
4284             !tape.player_participates[map_player_action[i]])
4285         {
4286           struct PlayerInfo *player = &stored_player[i];
4287           int jx = player->jx, jy = player->jy;
4288
4289 #if DEBUG_INIT_PLAYER
4290           Debug("game:init:player", "Removing player %d at (%d, %d)",
4291                 i + 1, jx, jy);
4292 #endif
4293
4294           player->active = FALSE;
4295           StorePlayer[jx][jy] = 0;
4296           Tile[jx][jy] = EL_EMPTY;
4297         }
4298       }
4299     }
4300
4301 #else
4302
4303     for (i = 0; i < MAX_PLAYERS; i++)
4304     {
4305       if (stored_player[i].active &&
4306           !tape.player_participates[i])
4307       {
4308         struct PlayerInfo *player = &stored_player[i];
4309         int jx = player->jx, jy = player->jy;
4310
4311         player->active = FALSE;
4312         StorePlayer[jx][jy] = 0;
4313         Tile[jx][jy] = EL_EMPTY;
4314       }
4315     }
4316 #endif
4317   }
4318   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4319   {
4320     // when in single player mode, eliminate all but the local player
4321
4322     for (i = 0; i < MAX_PLAYERS; i++)
4323     {
4324       struct PlayerInfo *player = &stored_player[i];
4325
4326       if (player->active && player != local_player)
4327       {
4328         int jx = player->jx, jy = player->jy;
4329
4330         player->active = FALSE;
4331         player->present = FALSE;
4332
4333         StorePlayer[jx][jy] = 0;
4334         Tile[jx][jy] = EL_EMPTY;
4335       }
4336     }
4337   }
4338
4339   for (i = 0; i < MAX_PLAYERS; i++)
4340     if (stored_player[i].active)
4341       game.players_still_needed++;
4342
4343   if (level.solved_by_one_player)
4344     game.players_still_needed = 1;
4345
4346   // when recording the game, store which players take part in the game
4347   if (tape.recording)
4348   {
4349 #if USE_NEW_PLAYER_ASSIGNMENTS
4350     for (i = 0; i < MAX_PLAYERS; i++)
4351       if (stored_player[i].connected)
4352         tape.player_participates[i] = TRUE;
4353 #else
4354     for (i = 0; i < MAX_PLAYERS; i++)
4355       if (stored_player[i].active)
4356         tape.player_participates[i] = TRUE;
4357 #endif
4358   }
4359
4360 #if DEBUG_INIT_PLAYER
4361   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4362 #endif
4363
4364   if (BorderElement == EL_EMPTY)
4365   {
4366     SBX_Left = 0;
4367     SBX_Right = lev_fieldx - SCR_FIELDX;
4368     SBY_Upper = 0;
4369     SBY_Lower = lev_fieldy - SCR_FIELDY;
4370   }
4371   else
4372   {
4373     SBX_Left = -1;
4374     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4375     SBY_Upper = -1;
4376     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4377   }
4378
4379   if (full_lev_fieldx <= SCR_FIELDX)
4380     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4381   if (full_lev_fieldy <= SCR_FIELDY)
4382     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4383
4384   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4385     SBX_Left--;
4386   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4387     SBY_Upper--;
4388
4389   // if local player not found, look for custom element that might create
4390   // the player (make some assumptions about the right custom element)
4391   if (!local_player->present)
4392   {
4393     int start_x = 0, start_y = 0;
4394     int found_rating = 0;
4395     int found_element = EL_UNDEFINED;
4396     int player_nr = local_player->index_nr;
4397
4398     SCAN_PLAYFIELD(x, y)
4399     {
4400       int element = Tile[x][y];
4401       int content;
4402       int xx, yy;
4403       boolean is_player;
4404
4405       if (level.use_start_element[player_nr] &&
4406           level.start_element[player_nr] == element &&
4407           found_rating < 4)
4408       {
4409         start_x = x;
4410         start_y = y;
4411
4412         found_rating = 4;
4413         found_element = element;
4414       }
4415
4416       if (!IS_CUSTOM_ELEMENT(element))
4417         continue;
4418
4419       if (CAN_CHANGE(element))
4420       {
4421         for (i = 0; i < element_info[element].num_change_pages; i++)
4422         {
4423           // check for player created from custom element as single target
4424           content = element_info[element].change_page[i].target_element;
4425           is_player = IS_PLAYER_ELEMENT(content);
4426
4427           if (is_player && (found_rating < 3 ||
4428                             (found_rating == 3 && element < found_element)))
4429           {
4430             start_x = x;
4431             start_y = y;
4432
4433             found_rating = 3;
4434             found_element = element;
4435           }
4436         }
4437       }
4438
4439       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4440       {
4441         // check for player created from custom element as explosion content
4442         content = element_info[element].content.e[xx][yy];
4443         is_player = IS_PLAYER_ELEMENT(content);
4444
4445         if (is_player && (found_rating < 2 ||
4446                           (found_rating == 2 && element < found_element)))
4447         {
4448           start_x = x + xx - 1;
4449           start_y = y + yy - 1;
4450
4451           found_rating = 2;
4452           found_element = element;
4453         }
4454
4455         if (!CAN_CHANGE(element))
4456           continue;
4457
4458         for (i = 0; i < element_info[element].num_change_pages; i++)
4459         {
4460           // check for player created from custom element as extended target
4461           content =
4462             element_info[element].change_page[i].target_content.e[xx][yy];
4463
4464           is_player = IS_PLAYER_ELEMENT(content);
4465
4466           if (is_player && (found_rating < 1 ||
4467                             (found_rating == 1 && element < found_element)))
4468           {
4469             start_x = x + xx - 1;
4470             start_y = y + yy - 1;
4471
4472             found_rating = 1;
4473             found_element = element;
4474           }
4475         }
4476       }
4477     }
4478
4479     scroll_x = SCROLL_POSITION_X(start_x);
4480     scroll_y = SCROLL_POSITION_Y(start_y);
4481   }
4482   else
4483   {
4484     scroll_x = SCROLL_POSITION_X(local_player->jx);
4485     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4486   }
4487
4488   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4489     scroll_x = game.forced_scroll_x;
4490   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4491     scroll_y = game.forced_scroll_y;
4492
4493   // !!! FIX THIS (START) !!!
4494   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4495   {
4496     InitGameEngine_EM();
4497   }
4498   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4499   {
4500     InitGameEngine_SP();
4501   }
4502   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4503   {
4504     InitGameEngine_MM();
4505   }
4506   else
4507   {
4508     DrawLevel(REDRAW_FIELD);
4509     DrawAllPlayers();
4510
4511     // after drawing the level, correct some elements
4512     if (game.timegate_time_left == 0)
4513       CloseAllOpenTimegates();
4514   }
4515
4516   // blit playfield from scroll buffer to normal back buffer for fading in
4517   BlitScreenToBitmap(backbuffer);
4518   // !!! FIX THIS (END) !!!
4519
4520   DrawMaskedBorder(fade_mask);
4521
4522   FadeIn(fade_mask);
4523
4524 #if 1
4525   // full screen redraw is required at this point in the following cases:
4526   // - special editor door undrawn when game was started from level editor
4527   // - drawing area (playfield) was changed and has to be removed completely
4528   redraw_mask = REDRAW_ALL;
4529   BackToFront();
4530 #endif
4531
4532   if (!game.restart_level)
4533   {
4534     // copy default game door content to main double buffer
4535
4536     // !!! CHECK AGAIN !!!
4537     SetPanelBackground();
4538     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4539     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4540   }
4541
4542   SetPanelBackground();
4543   SetDrawBackgroundMask(REDRAW_DOOR_1);
4544
4545   UpdateAndDisplayGameControlValues();
4546
4547   if (!game.restart_level)
4548   {
4549     UnmapGameButtons();
4550     UnmapTapeButtons();
4551
4552     FreeGameButtons();
4553     CreateGameButtons();
4554
4555     MapGameButtons();
4556     MapTapeButtons();
4557
4558     // copy actual game door content to door double buffer for OpenDoor()
4559     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4560
4561     OpenDoor(DOOR_OPEN_ALL);
4562
4563     KeyboardAutoRepeatOffUnlessAutoplay();
4564
4565 #if DEBUG_INIT_PLAYER
4566     DebugPrintPlayerStatus("Player status (final)");
4567 #endif
4568   }
4569
4570   UnmapAllGadgets();
4571
4572   MapGameButtons();
4573   MapTapeButtons();
4574
4575   if (!game.restart_level && !tape.playing)
4576   {
4577     LevelStats_incPlayed(level_nr);
4578
4579     SaveLevelSetup_SeriesInfo();
4580   }
4581
4582   game.restart_level = FALSE;
4583   game.request_active = FALSE;
4584
4585   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4586     InitGameActions_MM();
4587
4588   SaveEngineSnapshotToListInitial();
4589
4590   if (!game.restart_level)
4591   {
4592     PlaySound(SND_GAME_STARTING);
4593
4594     if (setup.sound_music)
4595       PlayLevelMusic();
4596   }
4597
4598   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4599 }
4600
4601 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4602                         int actual_player_x, int actual_player_y)
4603 {
4604   // this is used for non-R'n'D game engines to update certain engine values
4605
4606   // needed to determine if sounds are played within the visible screen area
4607   scroll_x = actual_scroll_x;
4608   scroll_y = actual_scroll_y;
4609
4610   // needed to get player position for "follow finger" playing input method
4611   local_player->jx = actual_player_x;
4612   local_player->jy = actual_player_y;
4613 }
4614
4615 void InitMovDir(int x, int y)
4616 {
4617   int i, element = Tile[x][y];
4618   static int xy[4][2] =
4619   {
4620     {  0, +1 },
4621     { +1,  0 },
4622     {  0, -1 },
4623     { -1,  0 }
4624   };
4625   static int direction[3][4] =
4626   {
4627     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4628     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4629     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4630   };
4631
4632   switch (element)
4633   {
4634     case EL_BUG_RIGHT:
4635     case EL_BUG_UP:
4636     case EL_BUG_LEFT:
4637     case EL_BUG_DOWN:
4638       Tile[x][y] = EL_BUG;
4639       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4640       break;
4641
4642     case EL_SPACESHIP_RIGHT:
4643     case EL_SPACESHIP_UP:
4644     case EL_SPACESHIP_LEFT:
4645     case EL_SPACESHIP_DOWN:
4646       Tile[x][y] = EL_SPACESHIP;
4647       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4648       break;
4649
4650     case EL_BD_BUTTERFLY_RIGHT:
4651     case EL_BD_BUTTERFLY_UP:
4652     case EL_BD_BUTTERFLY_LEFT:
4653     case EL_BD_BUTTERFLY_DOWN:
4654       Tile[x][y] = EL_BD_BUTTERFLY;
4655       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4656       break;
4657
4658     case EL_BD_FIREFLY_RIGHT:
4659     case EL_BD_FIREFLY_UP:
4660     case EL_BD_FIREFLY_LEFT:
4661     case EL_BD_FIREFLY_DOWN:
4662       Tile[x][y] = EL_BD_FIREFLY;
4663       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4664       break;
4665
4666     case EL_PACMAN_RIGHT:
4667     case EL_PACMAN_UP:
4668     case EL_PACMAN_LEFT:
4669     case EL_PACMAN_DOWN:
4670       Tile[x][y] = EL_PACMAN;
4671       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4672       break;
4673
4674     case EL_YAMYAM_LEFT:
4675     case EL_YAMYAM_RIGHT:
4676     case EL_YAMYAM_UP:
4677     case EL_YAMYAM_DOWN:
4678       Tile[x][y] = EL_YAMYAM;
4679       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4680       break;
4681
4682     case EL_SP_SNIKSNAK:
4683       MovDir[x][y] = MV_UP;
4684       break;
4685
4686     case EL_SP_ELECTRON:
4687       MovDir[x][y] = MV_LEFT;
4688       break;
4689
4690     case EL_MOLE_LEFT:
4691     case EL_MOLE_RIGHT:
4692     case EL_MOLE_UP:
4693     case EL_MOLE_DOWN:
4694       Tile[x][y] = EL_MOLE;
4695       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4696       break;
4697
4698     case EL_SPRING_LEFT:
4699     case EL_SPRING_RIGHT:
4700       Tile[x][y] = EL_SPRING;
4701       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4702       break;
4703
4704     default:
4705       if (IS_CUSTOM_ELEMENT(element))
4706       {
4707         struct ElementInfo *ei = &element_info[element];
4708         int move_direction_initial = ei->move_direction_initial;
4709         int move_pattern = ei->move_pattern;
4710
4711         if (move_direction_initial == MV_START_PREVIOUS)
4712         {
4713           if (MovDir[x][y] != MV_NONE)
4714             return;
4715
4716           move_direction_initial = MV_START_AUTOMATIC;
4717         }
4718
4719         if (move_direction_initial == MV_START_RANDOM)
4720           MovDir[x][y] = 1 << RND(4);
4721         else if (move_direction_initial & MV_ANY_DIRECTION)
4722           MovDir[x][y] = move_direction_initial;
4723         else if (move_pattern == MV_ALL_DIRECTIONS ||
4724                  move_pattern == MV_TURNING_LEFT ||
4725                  move_pattern == MV_TURNING_RIGHT ||
4726                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4727                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4728                  move_pattern == MV_TURNING_RANDOM)
4729           MovDir[x][y] = 1 << RND(4);
4730         else if (move_pattern == MV_HORIZONTAL)
4731           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4732         else if (move_pattern == MV_VERTICAL)
4733           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4734         else if (move_pattern & MV_ANY_DIRECTION)
4735           MovDir[x][y] = element_info[element].move_pattern;
4736         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4737                  move_pattern == MV_ALONG_RIGHT_SIDE)
4738         {
4739           // use random direction as default start direction
4740           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4741             MovDir[x][y] = 1 << RND(4);
4742
4743           for (i = 0; i < NUM_DIRECTIONS; i++)
4744           {
4745             int x1 = x + xy[i][0];
4746             int y1 = y + xy[i][1];
4747
4748             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4749             {
4750               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4751                 MovDir[x][y] = direction[0][i];
4752               else
4753                 MovDir[x][y] = direction[1][i];
4754
4755               break;
4756             }
4757           }
4758         }                
4759       }
4760       else
4761       {
4762         MovDir[x][y] = 1 << RND(4);
4763
4764         if (element != EL_BUG &&
4765             element != EL_SPACESHIP &&
4766             element != EL_BD_BUTTERFLY &&
4767             element != EL_BD_FIREFLY)
4768           break;
4769
4770         for (i = 0; i < NUM_DIRECTIONS; i++)
4771         {
4772           int x1 = x + xy[i][0];
4773           int y1 = y + xy[i][1];
4774
4775           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4776           {
4777             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4778             {
4779               MovDir[x][y] = direction[0][i];
4780               break;
4781             }
4782             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4783                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4784             {
4785               MovDir[x][y] = direction[1][i];
4786               break;
4787             }
4788           }
4789         }
4790       }
4791       break;
4792   }
4793
4794   GfxDir[x][y] = MovDir[x][y];
4795 }
4796
4797 void InitAmoebaNr(int x, int y)
4798 {
4799   int i;
4800   int group_nr = AmoebaNeighbourNr(x, y);
4801
4802   if (group_nr == 0)
4803   {
4804     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4805     {
4806       if (AmoebaCnt[i] == 0)
4807       {
4808         group_nr = i;
4809         break;
4810       }
4811     }
4812   }
4813
4814   AmoebaNr[x][y] = group_nr;
4815   AmoebaCnt[group_nr]++;
4816   AmoebaCnt2[group_nr]++;
4817 }
4818
4819 static void LevelSolved_SetFinalGameValues(void)
4820 {
4821   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4822   game.score_time_final = (level.use_step_counter ? TimePlayed :
4823                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4824
4825   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4826                       game_em.lev->score :
4827                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4828                       game_mm.score :
4829                       game.score);
4830
4831   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4832                        MM_HEALTH(game_mm.laser_overload_value) :
4833                        game.health);
4834
4835   game.LevelSolved_CountingTime = game.time_final;
4836   game.LevelSolved_CountingScore = game.score_final;
4837   game.LevelSolved_CountingHealth = game.health_final;
4838 }
4839
4840 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4841 {
4842   game.LevelSolved_CountingTime = time;
4843   game.LevelSolved_CountingScore = score;
4844   game.LevelSolved_CountingHealth = health;
4845
4846   game_panel_controls[GAME_PANEL_TIME].value = time;
4847   game_panel_controls[GAME_PANEL_SCORE].value = score;
4848   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4849
4850   DisplayGameControlValues();
4851 }
4852
4853 static void LevelSolved(void)
4854 {
4855   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4856       game.players_still_needed > 0)
4857     return;
4858
4859   game.LevelSolved = TRUE;
4860   game.GameOver = TRUE;
4861
4862   tape.solved = TRUE;
4863
4864   // needed here to display correct panel values while player walks into exit
4865   LevelSolved_SetFinalGameValues();
4866 }
4867
4868 void GameWon(void)
4869 {
4870   static int time_count_steps;
4871   static int time, time_final;
4872   static float score, score_final; // needed for time score < 10 for 10 seconds
4873   static int health, health_final;
4874   static int game_over_delay_1 = 0;
4875   static int game_over_delay_2 = 0;
4876   static int game_over_delay_3 = 0;
4877   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4878   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4879
4880   if (!game.LevelSolved_GameWon)
4881   {
4882     int i;
4883
4884     // do not start end game actions before the player stops moving (to exit)
4885     if (local_player->active && local_player->MovPos)
4886       return;
4887
4888     // calculate final game values after player finished walking into exit
4889     LevelSolved_SetFinalGameValues();
4890
4891     game.LevelSolved_GameWon = TRUE;
4892     game.LevelSolved_SaveTape = tape.recording;
4893     game.LevelSolved_SaveScore = !tape.playing;
4894
4895     if (!tape.playing)
4896     {
4897       LevelStats_incSolved(level_nr);
4898
4899       SaveLevelSetup_SeriesInfo();
4900     }
4901
4902     if (tape.auto_play)         // tape might already be stopped here
4903       tape.auto_play_level_solved = TRUE;
4904
4905     TapeStop();
4906
4907     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4908     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4909     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4910
4911     time = time_final = game.time_final;
4912     score = score_final = game.score_final;
4913     health = health_final = game.health_final;
4914
4915     // update game panel values before (delayed) counting of score (if any)
4916     LevelSolved_DisplayFinalGameValues(time, score, health);
4917
4918     // if level has time score defined, calculate new final game values
4919     if (time_score > 0)
4920     {
4921       int time_final_max = 999;
4922       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4923       int time_frames = 0;
4924       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4925       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4926
4927       if (TimeLeft > 0)
4928       {
4929         time_final = 0;
4930         time_frames = time_frames_left;
4931       }
4932       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4933       {
4934         time_final = time_final_max;
4935         time_frames = time_frames_final_max - time_frames_played;
4936       }
4937
4938       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4939
4940       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4941
4942       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4943       {
4944         health_final = 0;
4945         score_final += health * time_score;
4946       }
4947
4948       game.score_final = score_final;
4949       game.health_final = health_final;
4950     }
4951
4952     // if not counting score after game, immediately update game panel values
4953     if (level_editor_test_game || !setup.count_score_after_game)
4954     {
4955       time = time_final;
4956       score = score_final;
4957
4958       LevelSolved_DisplayFinalGameValues(time, score, health);
4959     }
4960
4961     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4962     {
4963       // check if last player has left the level
4964       if (game.exit_x >= 0 &&
4965           game.exit_y >= 0)
4966       {
4967         int x = game.exit_x;
4968         int y = game.exit_y;
4969         int element = Tile[x][y];
4970
4971         // close exit door after last player
4972         if ((game.all_players_gone &&
4973              (element == EL_EXIT_OPEN ||
4974               element == EL_SP_EXIT_OPEN ||
4975               element == EL_STEEL_EXIT_OPEN)) ||
4976             element == EL_EM_EXIT_OPEN ||
4977             element == EL_EM_STEEL_EXIT_OPEN)
4978         {
4979
4980           Tile[x][y] =
4981             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4982              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4983              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4984              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4985              EL_EM_STEEL_EXIT_CLOSING);
4986
4987           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4988         }
4989
4990         // player disappears
4991         DrawLevelField(x, y);
4992       }
4993
4994       for (i = 0; i < MAX_PLAYERS; i++)
4995       {
4996         struct PlayerInfo *player = &stored_player[i];
4997
4998         if (player->present)
4999         {
5000           RemovePlayer(player);
5001
5002           // player disappears
5003           DrawLevelField(player->jx, player->jy);
5004         }
5005       }
5006     }
5007
5008     PlaySound(SND_GAME_WINNING);
5009   }
5010
5011   if (setup.count_score_after_game)
5012   {
5013     if (time != time_final)
5014     {
5015       if (game_over_delay_1 > 0)
5016       {
5017         game_over_delay_1--;
5018
5019         return;
5020       }
5021
5022       int time_to_go = ABS(time_final - time);
5023       int time_count_dir = (time < time_final ? +1 : -1);
5024
5025       if (time_to_go < time_count_steps)
5026         time_count_steps = 1;
5027
5028       time  += time_count_steps * time_count_dir;
5029       score += time_count_steps * time_score;
5030
5031       // set final score to correct rounding differences after counting score
5032       if (time == time_final)
5033         score = score_final;
5034
5035       LevelSolved_DisplayFinalGameValues(time, score, health);
5036
5037       if (time == time_final)
5038         StopSound(SND_GAME_LEVELTIME_BONUS);
5039       else if (setup.sound_loops)
5040         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5041       else
5042         PlaySound(SND_GAME_LEVELTIME_BONUS);
5043
5044       return;
5045     }
5046
5047     if (health != health_final)
5048     {
5049       if (game_over_delay_2 > 0)
5050       {
5051         game_over_delay_2--;
5052
5053         return;
5054       }
5055
5056       int health_count_dir = (health < health_final ? +1 : -1);
5057
5058       health += health_count_dir;
5059       score  += time_score;
5060
5061       LevelSolved_DisplayFinalGameValues(time, score, health);
5062
5063       if (health == health_final)
5064         StopSound(SND_GAME_LEVELTIME_BONUS);
5065       else if (setup.sound_loops)
5066         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5067       else
5068         PlaySound(SND_GAME_LEVELTIME_BONUS);
5069
5070       return;
5071     }
5072   }
5073
5074   game.panel.active = FALSE;
5075
5076   if (game_over_delay_3 > 0)
5077   {
5078     game_over_delay_3--;
5079
5080     return;
5081   }
5082
5083   GameEnd();
5084 }
5085
5086 void GameEnd(void)
5087 {
5088   // used instead of "level_nr" (needed for network games)
5089   int last_level_nr = levelset.level_nr;
5090   boolean tape_saved = FALSE;
5091
5092   game.LevelSolved_GameEnd = TRUE;
5093
5094   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5095   {
5096     // make sure that request dialog to save tape does not open door again
5097     if (!global.use_envelope_request)
5098       CloseDoor(DOOR_CLOSE_1);
5099
5100     // ask to save tape
5101     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5102
5103     // set unique basename for score tape (also saved in high score table)
5104     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5105   }
5106
5107   // if no tape is to be saved, close both doors simultaneously
5108   CloseDoor(DOOR_CLOSE_ALL);
5109
5110   if (level_editor_test_game || score_info_tape_play)
5111   {
5112     SetGameStatus(GAME_MODE_MAIN);
5113
5114     DrawMainMenu();
5115
5116     return;
5117   }
5118
5119   if (!game.LevelSolved_SaveScore)
5120   {
5121     SetGameStatus(GAME_MODE_MAIN);
5122
5123     DrawMainMenu();
5124
5125     return;
5126   }
5127
5128   if (level_nr == leveldir_current->handicap_level)
5129   {
5130     leveldir_current->handicap_level++;
5131
5132     SaveLevelSetup_SeriesInfo();
5133   }
5134
5135   // save score and score tape before potentially erasing tape below
5136   NewHighScore(last_level_nr, tape_saved);
5137
5138   if (setup.increment_levels &&
5139       level_nr < leveldir_current->last_level &&
5140       !network_playing)
5141   {
5142     level_nr++;         // advance to next level
5143     TapeErase();        // start with empty tape
5144
5145     if (setup.auto_play_next_level)
5146     {
5147       scores.continue_playing = TRUE;
5148       scores.next_level_nr = level_nr;
5149
5150       LoadLevel(level_nr);
5151
5152       SaveLevelSetup_SeriesInfo();
5153     }
5154   }
5155
5156   if (scores.last_added >= 0 && setup.show_scores_after_game)
5157   {
5158     SetGameStatus(GAME_MODE_SCORES);
5159
5160     DrawHallOfFame(last_level_nr);
5161   }
5162   else if (scores.continue_playing)
5163   {
5164     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5165   }
5166   else
5167   {
5168     SetGameStatus(GAME_MODE_MAIN);
5169
5170     DrawMainMenu();
5171   }
5172 }
5173
5174 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5175                          boolean one_score_entry_per_name)
5176 {
5177   int i;
5178
5179   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5180     return -1;
5181
5182   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5183   {
5184     struct ScoreEntry *entry = &list->entry[i];
5185     boolean score_is_better = (new_entry->score >  entry->score);
5186     boolean score_is_equal  = (new_entry->score == entry->score);
5187     boolean time_is_better  = (new_entry->time  <  entry->time);
5188     boolean time_is_equal   = (new_entry->time  == entry->time);
5189     boolean better_by_score = (score_is_better ||
5190                                (score_is_equal && time_is_better));
5191     boolean better_by_time  = (time_is_better ||
5192                                (time_is_equal && score_is_better));
5193     boolean is_better = (level.rate_time_over_score ? better_by_time :
5194                          better_by_score);
5195     boolean entry_is_empty = (entry->score == 0 &&
5196                               entry->time == 0);
5197
5198     // prevent adding server score entries if also existing in local score file
5199     // (special case: historic score entries have an empty tape basename entry)
5200     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5201         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5202     {
5203       // add fields from server score entry not stored in local score entry
5204       // (currently, this means setting platform, version and country fields;
5205       // in rare cases, this may also correct an invalid score value, as
5206       // historic scores might have been truncated to 16-bit values locally)
5207       *entry = *new_entry;
5208
5209       return -1;
5210     }
5211
5212     if (is_better || entry_is_empty)
5213     {
5214       // player has made it to the hall of fame
5215
5216       if (i < MAX_SCORE_ENTRIES - 1)
5217       {
5218         int m = MAX_SCORE_ENTRIES - 1;
5219         int l;
5220
5221         if (one_score_entry_per_name)
5222         {
5223           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5224             if (strEqual(list->entry[l].name, new_entry->name))
5225               m = l;
5226
5227           if (m == i)   // player's new highscore overwrites his old one
5228             goto put_into_list;
5229         }
5230
5231         for (l = m; l > i; l--)
5232           list->entry[l] = list->entry[l - 1];
5233       }
5234
5235       put_into_list:
5236
5237       *entry = *new_entry;
5238
5239       return i;
5240     }
5241     else if (one_score_entry_per_name &&
5242              strEqual(entry->name, new_entry->name))
5243     {
5244       // player already in high score list with better score or time
5245
5246       return -1;
5247     }
5248   }
5249
5250   // special case: new score is beyond the last high score list position
5251   return MAX_SCORE_ENTRIES;
5252 }
5253
5254 void NewHighScore(int level_nr, boolean tape_saved)
5255 {
5256   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5257   boolean one_per_name = FALSE;
5258
5259   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5260   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5261
5262   new_entry.score = game.score_final;
5263   new_entry.time = game.score_time_final;
5264
5265   LoadScore(level_nr);
5266
5267   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5268
5269   if (scores.last_added >= MAX_SCORE_ENTRIES)
5270   {
5271     scores.last_added = MAX_SCORE_ENTRIES - 1;
5272     scores.force_last_added = TRUE;
5273
5274     scores.entry[scores.last_added] = new_entry;
5275
5276     // store last added local score entry (before merging server scores)
5277     scores.last_added_local = scores.last_added;
5278
5279     return;
5280   }
5281
5282   if (scores.last_added < 0)
5283     return;
5284
5285   SaveScore(level_nr);
5286
5287   // store last added local score entry (before merging server scores)
5288   scores.last_added_local = scores.last_added;
5289
5290   if (!game.LevelSolved_SaveTape)
5291     return;
5292
5293   SaveScoreTape(level_nr);
5294
5295   if (setup.ask_for_using_api_server)
5296   {
5297     setup.use_api_server =
5298       Request("Upload your score and tape to the high score server?", REQ_ASK);
5299
5300     if (!setup.use_api_server)
5301       Request("Not using high score server! Use setup menu to enable again!",
5302               REQ_CONFIRM);
5303
5304     runtime.use_api_server = setup.use_api_server;
5305
5306     // after asking for using API server once, do not ask again
5307     setup.ask_for_using_api_server = FALSE;
5308
5309     SaveSetup_ServerSetup();
5310   }
5311
5312   SaveServerScore(level_nr, tape_saved);
5313 }
5314
5315 void MergeServerScore(void)
5316 {
5317   struct ScoreEntry last_added_entry;
5318   boolean one_per_name = FALSE;
5319   int i;
5320
5321   if (scores.last_added >= 0)
5322     last_added_entry = scores.entry[scores.last_added];
5323
5324   for (i = 0; i < server_scores.num_entries; i++)
5325   {
5326     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5327
5328     if (pos >= 0 && pos <= scores.last_added)
5329       scores.last_added++;
5330   }
5331
5332   if (scores.last_added >= MAX_SCORE_ENTRIES)
5333   {
5334     scores.last_added = MAX_SCORE_ENTRIES - 1;
5335     scores.force_last_added = TRUE;
5336
5337     scores.entry[scores.last_added] = last_added_entry;
5338   }
5339 }
5340
5341 static int getElementMoveStepsizeExt(int x, int y, int direction)
5342 {
5343   int element = Tile[x][y];
5344   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5345   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5346   int horiz_move = (dx != 0);
5347   int sign = (horiz_move ? dx : dy);
5348   int step = sign * element_info[element].move_stepsize;
5349
5350   // special values for move stepsize for spring and things on conveyor belt
5351   if (horiz_move)
5352   {
5353     if (CAN_FALL(element) &&
5354         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5355       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5356     else if (element == EL_SPRING)
5357       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5358   }
5359
5360   return step;
5361 }
5362
5363 static int getElementMoveStepsize(int x, int y)
5364 {
5365   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5366 }
5367
5368 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5369 {
5370   if (player->GfxAction != action || player->GfxDir != dir)
5371   {
5372     player->GfxAction = action;
5373     player->GfxDir = dir;
5374     player->Frame = 0;
5375     player->StepFrame = 0;
5376   }
5377 }
5378
5379 static void ResetGfxFrame(int x, int y)
5380 {
5381   // profiling showed that "autotest" spends 10~20% of its time in this function
5382   if (DrawingDeactivatedField())
5383     return;
5384
5385   int element = Tile[x][y];
5386   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5387
5388   if (graphic_info[graphic].anim_global_sync)
5389     GfxFrame[x][y] = FrameCounter;
5390   else if (graphic_info[graphic].anim_global_anim_sync)
5391     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5392   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5393     GfxFrame[x][y] = CustomValue[x][y];
5394   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5395     GfxFrame[x][y] = element_info[element].collect_score;
5396   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5397     GfxFrame[x][y] = ChangeDelay[x][y];
5398 }
5399
5400 static void ResetGfxAnimation(int x, int y)
5401 {
5402   GfxAction[x][y] = ACTION_DEFAULT;
5403   GfxDir[x][y] = MovDir[x][y];
5404   GfxFrame[x][y] = 0;
5405
5406   ResetGfxFrame(x, y);
5407 }
5408
5409 static void ResetRandomAnimationValue(int x, int y)
5410 {
5411   GfxRandom[x][y] = INIT_GFX_RANDOM();
5412 }
5413
5414 static void InitMovingField(int x, int y, int direction)
5415 {
5416   int element = Tile[x][y];
5417   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5418   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5419   int newx = x + dx;
5420   int newy = y + dy;
5421   boolean is_moving_before, is_moving_after;
5422
5423   // check if element was/is moving or being moved before/after mode change
5424   is_moving_before = (WasJustMoving[x][y] != 0);
5425   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5426
5427   // reset animation only for moving elements which change direction of moving
5428   // or which just started or stopped moving
5429   // (else CEs with property "can move" / "not moving" are reset each frame)
5430   if (is_moving_before != is_moving_after ||
5431       direction != MovDir[x][y])
5432     ResetGfxAnimation(x, y);
5433
5434   MovDir[x][y] = direction;
5435   GfxDir[x][y] = direction;
5436
5437   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5438                      direction == MV_DOWN && CAN_FALL(element) ?
5439                      ACTION_FALLING : ACTION_MOVING);
5440
5441   // this is needed for CEs with property "can move" / "not moving"
5442
5443   if (is_moving_after)
5444   {
5445     if (Tile[newx][newy] == EL_EMPTY)
5446       Tile[newx][newy] = EL_BLOCKED;
5447
5448     MovDir[newx][newy] = MovDir[x][y];
5449
5450     CustomValue[newx][newy] = CustomValue[x][y];
5451
5452     GfxFrame[newx][newy] = GfxFrame[x][y];
5453     GfxRandom[newx][newy] = GfxRandom[x][y];
5454     GfxAction[newx][newy] = GfxAction[x][y];
5455     GfxDir[newx][newy] = GfxDir[x][y];
5456   }
5457 }
5458
5459 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5460 {
5461   int direction = MovDir[x][y];
5462   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5463   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5464
5465   *goes_to_x = newx;
5466   *goes_to_y = newy;
5467 }
5468
5469 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5470 {
5471   int direction = MovDir[x][y];
5472   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5473   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5474
5475   *comes_from_x = oldx;
5476   *comes_from_y = oldy;
5477 }
5478
5479 static int MovingOrBlocked2Element(int x, int y)
5480 {
5481   int element = Tile[x][y];
5482
5483   if (element == EL_BLOCKED)
5484   {
5485     int oldx, oldy;
5486
5487     Blocked2Moving(x, y, &oldx, &oldy);
5488
5489     return Tile[oldx][oldy];
5490   }
5491
5492   return element;
5493 }
5494
5495 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5496 {
5497   // like MovingOrBlocked2Element(), but if element is moving
5498   // and (x, y) is the field the moving element is just leaving,
5499   // return EL_BLOCKED instead of the element value
5500   int element = Tile[x][y];
5501
5502   if (IS_MOVING(x, y))
5503   {
5504     if (element == EL_BLOCKED)
5505     {
5506       int oldx, oldy;
5507
5508       Blocked2Moving(x, y, &oldx, &oldy);
5509       return Tile[oldx][oldy];
5510     }
5511     else
5512       return EL_BLOCKED;
5513   }
5514   else
5515     return element;
5516 }
5517
5518 static void RemoveField(int x, int y)
5519 {
5520   Tile[x][y] = EL_EMPTY;
5521
5522   MovPos[x][y] = 0;
5523   MovDir[x][y] = 0;
5524   MovDelay[x][y] = 0;
5525
5526   CustomValue[x][y] = 0;
5527
5528   AmoebaNr[x][y] = 0;
5529   ChangeDelay[x][y] = 0;
5530   ChangePage[x][y] = -1;
5531   Pushed[x][y] = FALSE;
5532
5533   GfxElement[x][y] = EL_UNDEFINED;
5534   GfxAction[x][y] = ACTION_DEFAULT;
5535   GfxDir[x][y] = MV_NONE;
5536 }
5537
5538 static void RemoveMovingField(int x, int y)
5539 {
5540   int oldx = x, oldy = y, newx = x, newy = y;
5541   int element = Tile[x][y];
5542   int next_element = EL_UNDEFINED;
5543
5544   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5545     return;
5546
5547   if (IS_MOVING(x, y))
5548   {
5549     Moving2Blocked(x, y, &newx, &newy);
5550
5551     if (Tile[newx][newy] != EL_BLOCKED)
5552     {
5553       // element is moving, but target field is not free (blocked), but
5554       // already occupied by something different (example: acid pool);
5555       // in this case, only remove the moving field, but not the target
5556
5557       RemoveField(oldx, oldy);
5558
5559       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5560
5561       TEST_DrawLevelField(oldx, oldy);
5562
5563       return;
5564     }
5565   }
5566   else if (element == EL_BLOCKED)
5567   {
5568     Blocked2Moving(x, y, &oldx, &oldy);
5569     if (!IS_MOVING(oldx, oldy))
5570       return;
5571   }
5572
5573   if (element == EL_BLOCKED &&
5574       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5575        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5576        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5577        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5578        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5579        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5580     next_element = get_next_element(Tile[oldx][oldy]);
5581
5582   RemoveField(oldx, oldy);
5583   RemoveField(newx, newy);
5584
5585   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5586
5587   if (next_element != EL_UNDEFINED)
5588     Tile[oldx][oldy] = next_element;
5589
5590   TEST_DrawLevelField(oldx, oldy);
5591   TEST_DrawLevelField(newx, newy);
5592 }
5593
5594 void DrawDynamite(int x, int y)
5595 {
5596   int sx = SCREENX(x), sy = SCREENY(y);
5597   int graphic = el2img(Tile[x][y]);
5598   int frame;
5599
5600   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5601     return;
5602
5603   if (IS_WALKABLE_INSIDE(Back[x][y]))
5604     return;
5605
5606   if (Back[x][y])
5607     DrawLevelElement(x, y, Back[x][y]);
5608   else if (Store[x][y])
5609     DrawLevelElement(x, y, Store[x][y]);
5610   else if (game.use_masked_elements)
5611     DrawLevelElement(x, y, EL_EMPTY);
5612
5613   frame = getGraphicAnimationFrameXY(graphic, x, y);
5614
5615   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5616     DrawGraphicThruMask(sx, sy, graphic, frame);
5617   else
5618     DrawGraphic(sx, sy, graphic, frame);
5619 }
5620
5621 static void CheckDynamite(int x, int y)
5622 {
5623   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5624   {
5625     MovDelay[x][y]--;
5626
5627     if (MovDelay[x][y] != 0)
5628     {
5629       DrawDynamite(x, y);
5630       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5631
5632       return;
5633     }
5634   }
5635
5636   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5637
5638   Bang(x, y);
5639 }
5640
5641 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5642 {
5643   boolean num_checked_players = 0;
5644   int i;
5645
5646   for (i = 0; i < MAX_PLAYERS; i++)
5647   {
5648     if (stored_player[i].active)
5649     {
5650       int sx = stored_player[i].jx;
5651       int sy = stored_player[i].jy;
5652
5653       if (num_checked_players == 0)
5654       {
5655         *sx1 = *sx2 = sx;
5656         *sy1 = *sy2 = sy;
5657       }
5658       else
5659       {
5660         *sx1 = MIN(*sx1, sx);
5661         *sy1 = MIN(*sy1, sy);
5662         *sx2 = MAX(*sx2, sx);
5663         *sy2 = MAX(*sy2, sy);
5664       }
5665
5666       num_checked_players++;
5667     }
5668   }
5669 }
5670
5671 static boolean checkIfAllPlayersFitToScreen_RND(void)
5672 {
5673   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5674
5675   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5676
5677   return (sx2 - sx1 < SCR_FIELDX &&
5678           sy2 - sy1 < SCR_FIELDY);
5679 }
5680
5681 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5682 {
5683   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5684
5685   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5686
5687   *sx = (sx1 + sx2) / 2;
5688   *sy = (sy1 + sy2) / 2;
5689 }
5690
5691 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5692                                boolean center_screen, boolean quick_relocation)
5693 {
5694   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5695   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5696   boolean no_delay = (tape.warp_forward);
5697   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5698   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5699   int new_scroll_x, new_scroll_y;
5700
5701   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5702   {
5703     // case 1: quick relocation inside visible screen (without scrolling)
5704
5705     RedrawPlayfield();
5706
5707     return;
5708   }
5709
5710   if (!level.shifted_relocation || center_screen)
5711   {
5712     // relocation _with_ centering of screen
5713
5714     new_scroll_x = SCROLL_POSITION_X(x);
5715     new_scroll_y = SCROLL_POSITION_Y(y);
5716   }
5717   else
5718   {
5719     // relocation _without_ centering of screen
5720
5721     // apply distance between old and new player position to scroll position
5722     int shifted_scroll_x = scroll_x + (x - old_x);
5723     int shifted_scroll_y = scroll_y + (y - old_y);
5724
5725     // make sure that shifted scroll position does not scroll beyond screen
5726     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5727     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5728
5729     // special case for teleporting from one end of the playfield to the other
5730     // (this kludge prevents the destination area to be shifted by half a tile
5731     // against the source destination for even screen width or screen height;
5732     // probably most useful when used with high "game.forced_scroll_delay_value"
5733     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5734     if (quick_relocation)
5735     {
5736       if (EVEN(SCR_FIELDX))
5737       {
5738         // relocate (teleport) between left and right border (half or full)
5739         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5740           new_scroll_x = SBX_Right;
5741         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5742           new_scroll_x = SBX_Right - 1;
5743         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5744           new_scroll_x = SBX_Left;
5745         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5746           new_scroll_x = SBX_Left + 1;
5747       }
5748
5749       if (EVEN(SCR_FIELDY))
5750       {
5751         // relocate (teleport) between top and bottom border (half or full)
5752         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5753           new_scroll_y = SBY_Lower;
5754         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5755           new_scroll_y = SBY_Lower - 1;
5756         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5757           new_scroll_y = SBY_Upper;
5758         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5759           new_scroll_y = SBY_Upper + 1;
5760       }
5761     }
5762   }
5763
5764   if (quick_relocation)
5765   {
5766     // case 2: quick relocation (redraw without visible scrolling)
5767
5768     scroll_x = new_scroll_x;
5769     scroll_y = new_scroll_y;
5770
5771     RedrawPlayfield();
5772
5773     return;
5774   }
5775
5776   // case 3: visible relocation (with scrolling to new position)
5777
5778   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5779
5780   SetVideoFrameDelay(wait_delay_value);
5781
5782   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5783   {
5784     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5785     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5786
5787     if (dx == 0 && dy == 0)             // no scrolling needed at all
5788       break;
5789
5790     scroll_x -= dx;
5791     scroll_y -= dy;
5792
5793     // set values for horizontal/vertical screen scrolling (half tile size)
5794     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5795     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5796     int pos_x = dx * TILEX / 2;
5797     int pos_y = dy * TILEY / 2;
5798     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5799     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5800
5801     ScrollLevel(dx, dy);
5802     DrawAllPlayers();
5803
5804     // scroll in two steps of half tile size to make things smoother
5805     BlitScreenToBitmapExt_RND(window, fx, fy);
5806
5807     // scroll second step to align at full tile size
5808     BlitScreenToBitmap(window);
5809   }
5810
5811   DrawAllPlayers();
5812   BackToFront();
5813
5814   SetVideoFrameDelay(frame_delay_value_old);
5815 }
5816
5817 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5818 {
5819   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5820   int player_nr = GET_PLAYER_NR(el_player);
5821   struct PlayerInfo *player = &stored_player[player_nr];
5822   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5823   boolean no_delay = (tape.warp_forward);
5824   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5825   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5826   int old_jx = player->jx;
5827   int old_jy = player->jy;
5828   int old_element = Tile[old_jx][old_jy];
5829   int element = Tile[jx][jy];
5830   boolean player_relocated = (old_jx != jx || old_jy != jy);
5831
5832   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5833   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5834   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5835   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5836   int leave_side_horiz = move_dir_horiz;
5837   int leave_side_vert  = move_dir_vert;
5838   int enter_side = enter_side_horiz | enter_side_vert;
5839   int leave_side = leave_side_horiz | leave_side_vert;
5840
5841   if (player->buried)           // do not reanimate dead player
5842     return;
5843
5844   if (!player_relocated)        // no need to relocate the player
5845     return;
5846
5847   if (IS_PLAYER(jx, jy))        // player already placed at new position
5848   {
5849     RemoveField(jx, jy);        // temporarily remove newly placed player
5850     DrawLevelField(jx, jy);
5851   }
5852
5853   if (player->present)
5854   {
5855     while (player->MovPos)
5856     {
5857       ScrollPlayer(player, SCROLL_GO_ON);
5858       ScrollScreen(NULL, SCROLL_GO_ON);
5859
5860       AdvanceFrameAndPlayerCounters(player->index_nr);
5861
5862       DrawPlayer(player);
5863
5864       BackToFront_WithFrameDelay(wait_delay_value);
5865     }
5866
5867     DrawPlayer(player);         // needed here only to cleanup last field
5868     DrawLevelField(player->jx, player->jy);     // remove player graphic
5869
5870     player->is_moving = FALSE;
5871   }
5872
5873   if (IS_CUSTOM_ELEMENT(old_element))
5874     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5875                                CE_LEFT_BY_PLAYER,
5876                                player->index_bit, leave_side);
5877
5878   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5879                                       CE_PLAYER_LEAVES_X,
5880                                       player->index_bit, leave_side);
5881
5882   Tile[jx][jy] = el_player;
5883   InitPlayerField(jx, jy, el_player, TRUE);
5884
5885   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5886      possible that the relocation target field did not contain a player element,
5887      but a walkable element, to which the new player was relocated -- in this
5888      case, restore that (already initialized!) element on the player field */
5889   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5890   {
5891     Tile[jx][jy] = element;     // restore previously existing element
5892   }
5893
5894   // only visually relocate centered player
5895   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5896                      FALSE, level.instant_relocation);
5897
5898   TestIfPlayerTouchesBadThing(jx, jy);
5899   TestIfPlayerTouchesCustomElement(jx, jy);
5900
5901   if (IS_CUSTOM_ELEMENT(element))
5902     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5903                                player->index_bit, enter_side);
5904
5905   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5906                                       player->index_bit, enter_side);
5907
5908   if (player->is_switching)
5909   {
5910     /* ensure that relocation while still switching an element does not cause
5911        a new element to be treated as also switched directly after relocation
5912        (this is important for teleporter switches that teleport the player to
5913        a place where another teleporter switch is in the same direction, which
5914        would then incorrectly be treated as immediately switched before the
5915        direction key that caused the switch was released) */
5916
5917     player->switch_x += jx - old_jx;
5918     player->switch_y += jy - old_jy;
5919   }
5920 }
5921
5922 static void Explode(int ex, int ey, int phase, int mode)
5923 {
5924   int x, y;
5925   int last_phase;
5926   int border_element;
5927
5928   if (game.explosions_delayed)
5929   {
5930     ExplodeField[ex][ey] = mode;
5931     return;
5932   }
5933
5934   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5935   {
5936     int center_element = Tile[ex][ey];
5937     int ce_value = CustomValue[ex][ey];
5938     int ce_score = element_info[center_element].collect_score;
5939     int artwork_element, explosion_element;     // set these values later
5940
5941     // remove things displayed in background while burning dynamite
5942     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5943       Back[ex][ey] = 0;
5944
5945     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5946     {
5947       // put moving element to center field (and let it explode there)
5948       center_element = MovingOrBlocked2Element(ex, ey);
5949       RemoveMovingField(ex, ey);
5950       Tile[ex][ey] = center_element;
5951     }
5952
5953     // now "center_element" is finally determined -- set related values now
5954     artwork_element = center_element;           // for custom player artwork
5955     explosion_element = center_element;         // for custom player artwork
5956
5957     if (IS_PLAYER(ex, ey))
5958     {
5959       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5960
5961       artwork_element = stored_player[player_nr].artwork_element;
5962
5963       if (level.use_explosion_element[player_nr])
5964       {
5965         explosion_element = level.explosion_element[player_nr];
5966         artwork_element = explosion_element;
5967       }
5968     }
5969
5970     if (mode == EX_TYPE_NORMAL ||
5971         mode == EX_TYPE_CENTER ||
5972         mode == EX_TYPE_CROSS)
5973       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5974
5975     last_phase = element_info[explosion_element].explosion_delay + 1;
5976
5977     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5978     {
5979       int xx = x - ex + 1;
5980       int yy = y - ey + 1;
5981       int element;
5982
5983       if (!IN_LEV_FIELD(x, y) ||
5984           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5985           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5986         continue;
5987
5988       element = Tile[x][y];
5989
5990       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5991       {
5992         element = MovingOrBlocked2Element(x, y);
5993
5994         if (!IS_EXPLOSION_PROOF(element))
5995           RemoveMovingField(x, y);
5996       }
5997
5998       // indestructible elements can only explode in center (but not flames)
5999       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6000                                            mode == EX_TYPE_BORDER)) ||
6001           element == EL_FLAMES)
6002         continue;
6003
6004       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6005          behaviour, for example when touching a yamyam that explodes to rocks
6006          with active deadly shield, a rock is created under the player !!! */
6007       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6008 #if 0
6009       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6010           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6011            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6012 #else
6013       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6014 #endif
6015       {
6016         if (IS_ACTIVE_BOMB(element))
6017         {
6018           // re-activate things under the bomb like gate or penguin
6019           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6020           Back[x][y] = 0;
6021         }
6022
6023         continue;
6024       }
6025
6026       // save walkable background elements while explosion on same tile
6027       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6028           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6029         Back[x][y] = element;
6030
6031       // ignite explodable elements reached by other explosion
6032       if (element == EL_EXPLOSION)
6033         element = Store2[x][y];
6034
6035       if (AmoebaNr[x][y] &&
6036           (element == EL_AMOEBA_FULL ||
6037            element == EL_BD_AMOEBA ||
6038            element == EL_AMOEBA_GROWING))
6039       {
6040         AmoebaCnt[AmoebaNr[x][y]]--;
6041         AmoebaCnt2[AmoebaNr[x][y]]--;
6042       }
6043
6044       RemoveField(x, y);
6045
6046       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6047       {
6048         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6049
6050         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6051
6052         if (PLAYERINFO(ex, ey)->use_murphy)
6053           Store[x][y] = EL_EMPTY;
6054       }
6055
6056       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6057       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6058       else if (IS_PLAYER_ELEMENT(center_element))
6059         Store[x][y] = EL_EMPTY;
6060       else if (center_element == EL_YAMYAM)
6061         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6062       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6063         Store[x][y] = element_info[center_element].content.e[xx][yy];
6064 #if 1
6065       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6066       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6067       // otherwise) -- FIX THIS !!!
6068       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6069         Store[x][y] = element_info[element].content.e[1][1];
6070 #else
6071       else if (!CAN_EXPLODE(element))
6072         Store[x][y] = element_info[element].content.e[1][1];
6073 #endif
6074       else
6075         Store[x][y] = EL_EMPTY;
6076
6077       if (IS_CUSTOM_ELEMENT(center_element))
6078         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6079                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6080                        Store[x][y] >= EL_PREV_CE_8 &&
6081                        Store[x][y] <= EL_NEXT_CE_8 ?
6082                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6083                        Store[x][y]);
6084
6085       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6086           center_element == EL_AMOEBA_TO_DIAMOND)
6087         Store2[x][y] = element;
6088
6089       Tile[x][y] = EL_EXPLOSION;
6090       GfxElement[x][y] = artwork_element;
6091
6092       ExplodePhase[x][y] = 1;
6093       ExplodeDelay[x][y] = last_phase;
6094
6095       Stop[x][y] = TRUE;
6096     }
6097
6098     if (center_element == EL_YAMYAM)
6099       game.yamyam_content_nr =
6100         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6101
6102     return;
6103   }
6104
6105   if (Stop[ex][ey])
6106     return;
6107
6108   x = ex;
6109   y = ey;
6110
6111   if (phase == 1)
6112     GfxFrame[x][y] = 0;         // restart explosion animation
6113
6114   last_phase = ExplodeDelay[x][y];
6115
6116   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6117
6118   // this can happen if the player leaves an explosion just in time
6119   if (GfxElement[x][y] == EL_UNDEFINED)
6120     GfxElement[x][y] = EL_EMPTY;
6121
6122   border_element = Store2[x][y];
6123   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6124     border_element = StorePlayer[x][y];
6125
6126   if (phase == element_info[border_element].ignition_delay ||
6127       phase == last_phase)
6128   {
6129     boolean border_explosion = FALSE;
6130
6131     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6132         !PLAYER_EXPLOSION_PROTECTED(x, y))
6133     {
6134       KillPlayerUnlessExplosionProtected(x, y);
6135       border_explosion = TRUE;
6136     }
6137     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6138     {
6139       Tile[x][y] = Store2[x][y];
6140       Store2[x][y] = 0;
6141       Bang(x, y);
6142       border_explosion = TRUE;
6143     }
6144     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6145     {
6146       AmoebaToDiamond(x, y);
6147       Store2[x][y] = 0;
6148       border_explosion = TRUE;
6149     }
6150
6151     // if an element just explodes due to another explosion (chain-reaction),
6152     // do not immediately end the new explosion when it was the last frame of
6153     // the explosion (as it would be done in the following "if"-statement!)
6154     if (border_explosion && phase == last_phase)
6155       return;
6156   }
6157
6158   // this can happen if the player was just killed by an explosion
6159   if (GfxElement[x][y] == EL_UNDEFINED)
6160     GfxElement[x][y] = EL_EMPTY;
6161
6162   if (phase == last_phase)
6163   {
6164     int element;
6165
6166     element = Tile[x][y] = Store[x][y];
6167     Store[x][y] = Store2[x][y] = 0;
6168     GfxElement[x][y] = EL_UNDEFINED;
6169
6170     // player can escape from explosions and might therefore be still alive
6171     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6172         element <= EL_PLAYER_IS_EXPLODING_4)
6173     {
6174       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6175       int explosion_element = EL_PLAYER_1 + player_nr;
6176       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6177       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6178
6179       if (level.use_explosion_element[player_nr])
6180         explosion_element = level.explosion_element[player_nr];
6181
6182       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6183                     element_info[explosion_element].content.e[xx][yy]);
6184     }
6185
6186     // restore probably existing indestructible background element
6187     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6188       element = Tile[x][y] = Back[x][y];
6189     Back[x][y] = 0;
6190
6191     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6192     GfxDir[x][y] = MV_NONE;
6193     ChangeDelay[x][y] = 0;
6194     ChangePage[x][y] = -1;
6195
6196     CustomValue[x][y] = 0;
6197
6198     InitField_WithBug2(x, y, FALSE);
6199
6200     TEST_DrawLevelField(x, y);
6201
6202     TestIfElementTouchesCustomElement(x, y);
6203
6204     if (GFX_CRUMBLED(element))
6205       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6206
6207     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6208       StorePlayer[x][y] = 0;
6209
6210     if (IS_PLAYER_ELEMENT(element))
6211       RelocatePlayer(x, y, element);
6212   }
6213   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6214   {
6215     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6216     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6217
6218     if (phase == 1)
6219       TEST_DrawLevelFieldCrumbled(x, y);
6220
6221     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6222     {
6223       DrawLevelElement(x, y, Back[x][y]);
6224       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6225     }
6226     else if (IS_WALKABLE_UNDER(Back[x][y]))
6227     {
6228       DrawLevelGraphic(x, y, graphic, frame);
6229       DrawLevelElementThruMask(x, y, Back[x][y]);
6230     }
6231     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6232       DrawLevelGraphic(x, y, graphic, frame);
6233   }
6234 }
6235
6236 static void DynaExplode(int ex, int ey)
6237 {
6238   int i, j;
6239   int dynabomb_element = Tile[ex][ey];
6240   int dynabomb_size = 1;
6241   boolean dynabomb_xl = FALSE;
6242   struct PlayerInfo *player;
6243   struct XY *xy = xy_topdown;
6244
6245   if (IS_ACTIVE_BOMB(dynabomb_element))
6246   {
6247     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6248     dynabomb_size = player->dynabomb_size;
6249     dynabomb_xl = player->dynabomb_xl;
6250     player->dynabombs_left++;
6251   }
6252
6253   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6254
6255   for (i = 0; i < NUM_DIRECTIONS; i++)
6256   {
6257     for (j = 1; j <= dynabomb_size; j++)
6258     {
6259       int x = ex + j * xy[i].x;
6260       int y = ey + j * xy[i].y;
6261       int element;
6262
6263       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6264         break;
6265
6266       element = Tile[x][y];
6267
6268       // do not restart explosions of fields with active bombs
6269       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6270         continue;
6271
6272       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6273
6274       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6275           !IS_DIGGABLE(element) && !dynabomb_xl)
6276         break;
6277     }
6278   }
6279 }
6280
6281 void Bang(int x, int y)
6282 {
6283   int element = MovingOrBlocked2Element(x, y);
6284   int explosion_type = EX_TYPE_NORMAL;
6285
6286   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6287   {
6288     struct PlayerInfo *player = PLAYERINFO(x, y);
6289
6290     element = Tile[x][y] = player->initial_element;
6291
6292     if (level.use_explosion_element[player->index_nr])
6293     {
6294       int explosion_element = level.explosion_element[player->index_nr];
6295
6296       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6297         explosion_type = EX_TYPE_CROSS;
6298       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6299         explosion_type = EX_TYPE_CENTER;
6300     }
6301   }
6302
6303   switch (element)
6304   {
6305     case EL_BUG:
6306     case EL_SPACESHIP:
6307     case EL_BD_BUTTERFLY:
6308     case EL_BD_FIREFLY:
6309     case EL_YAMYAM:
6310     case EL_DARK_YAMYAM:
6311     case EL_ROBOT:
6312     case EL_PACMAN:
6313     case EL_MOLE:
6314       RaiseScoreElement(element);
6315       break;
6316
6317     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6318     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6319     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6320     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6321     case EL_DYNABOMB_INCREASE_NUMBER:
6322     case EL_DYNABOMB_INCREASE_SIZE:
6323     case EL_DYNABOMB_INCREASE_POWER:
6324       explosion_type = EX_TYPE_DYNA;
6325       break;
6326
6327     case EL_DC_LANDMINE:
6328       explosion_type = EX_TYPE_CENTER;
6329       break;
6330
6331     case EL_PENGUIN:
6332     case EL_LAMP:
6333     case EL_LAMP_ACTIVE:
6334     case EL_AMOEBA_TO_DIAMOND:
6335       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6336         explosion_type = EX_TYPE_CENTER;
6337       break;
6338
6339     default:
6340       if (element_info[element].explosion_type == EXPLODES_CROSS)
6341         explosion_type = EX_TYPE_CROSS;
6342       else if (element_info[element].explosion_type == EXPLODES_1X1)
6343         explosion_type = EX_TYPE_CENTER;
6344       break;
6345   }
6346
6347   if (explosion_type == EX_TYPE_DYNA)
6348     DynaExplode(x, y);
6349   else
6350     Explode(x, y, EX_PHASE_START, explosion_type);
6351
6352   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6353 }
6354
6355 static void SplashAcid(int x, int y)
6356 {
6357   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6358       (!IN_LEV_FIELD(x - 1, y - 2) ||
6359        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6360     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6361
6362   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6363       (!IN_LEV_FIELD(x + 1, y - 2) ||
6364        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6365     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6366
6367   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6368 }
6369
6370 static void InitBeltMovement(void)
6371 {
6372   static int belt_base_element[4] =
6373   {
6374     EL_CONVEYOR_BELT_1_LEFT,
6375     EL_CONVEYOR_BELT_2_LEFT,
6376     EL_CONVEYOR_BELT_3_LEFT,
6377     EL_CONVEYOR_BELT_4_LEFT
6378   };
6379   static int belt_base_active_element[4] =
6380   {
6381     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6382     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6383     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6384     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6385   };
6386
6387   int x, y, i, j;
6388
6389   // set frame order for belt animation graphic according to belt direction
6390   for (i = 0; i < NUM_BELTS; i++)
6391   {
6392     int belt_nr = i;
6393
6394     for (j = 0; j < NUM_BELT_PARTS; j++)
6395     {
6396       int element = belt_base_active_element[belt_nr] + j;
6397       int graphic_1 = el2img(element);
6398       int graphic_2 = el2panelimg(element);
6399
6400       if (game.belt_dir[i] == MV_LEFT)
6401       {
6402         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6403         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6404       }
6405       else
6406       {
6407         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6408         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6409       }
6410     }
6411   }
6412
6413   SCAN_PLAYFIELD(x, y)
6414   {
6415     int element = Tile[x][y];
6416
6417     for (i = 0; i < NUM_BELTS; i++)
6418     {
6419       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6420       {
6421         int e_belt_nr = getBeltNrFromBeltElement(element);
6422         int belt_nr = i;
6423
6424         if (e_belt_nr == belt_nr)
6425         {
6426           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6427
6428           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6429         }
6430       }
6431     }
6432   }
6433 }
6434
6435 static void ToggleBeltSwitch(int x, int y)
6436 {
6437   static int belt_base_element[4] =
6438   {
6439     EL_CONVEYOR_BELT_1_LEFT,
6440     EL_CONVEYOR_BELT_2_LEFT,
6441     EL_CONVEYOR_BELT_3_LEFT,
6442     EL_CONVEYOR_BELT_4_LEFT
6443   };
6444   static int belt_base_active_element[4] =
6445   {
6446     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6447     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6448     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6449     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6450   };
6451   static int belt_base_switch_element[4] =
6452   {
6453     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6454     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6455     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6456     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6457   };
6458   static int belt_move_dir[4] =
6459   {
6460     MV_LEFT,
6461     MV_NONE,
6462     MV_RIGHT,
6463     MV_NONE,
6464   };
6465
6466   int element = Tile[x][y];
6467   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6468   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6469   int belt_dir = belt_move_dir[belt_dir_nr];
6470   int xx, yy, i;
6471
6472   if (!IS_BELT_SWITCH(element))
6473     return;
6474
6475   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6476   game.belt_dir[belt_nr] = belt_dir;
6477
6478   if (belt_dir_nr == 3)
6479     belt_dir_nr = 1;
6480
6481   // set frame order for belt animation graphic according to belt direction
6482   for (i = 0; i < NUM_BELT_PARTS; i++)
6483   {
6484     int element = belt_base_active_element[belt_nr] + i;
6485     int graphic_1 = el2img(element);
6486     int graphic_2 = el2panelimg(element);
6487
6488     if (belt_dir == MV_LEFT)
6489     {
6490       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6491       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6492     }
6493     else
6494     {
6495       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6496       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6497     }
6498   }
6499
6500   SCAN_PLAYFIELD(xx, yy)
6501   {
6502     int element = Tile[xx][yy];
6503
6504     if (IS_BELT_SWITCH(element))
6505     {
6506       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6507
6508       if (e_belt_nr == belt_nr)
6509       {
6510         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6511         TEST_DrawLevelField(xx, yy);
6512       }
6513     }
6514     else if (IS_BELT(element) && belt_dir != MV_NONE)
6515     {
6516       int e_belt_nr = getBeltNrFromBeltElement(element);
6517
6518       if (e_belt_nr == belt_nr)
6519       {
6520         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6521
6522         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6523         TEST_DrawLevelField(xx, yy);
6524       }
6525     }
6526     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6527     {
6528       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6529
6530       if (e_belt_nr == belt_nr)
6531       {
6532         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6533
6534         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6535         TEST_DrawLevelField(xx, yy);
6536       }
6537     }
6538   }
6539 }
6540
6541 static void ToggleSwitchgateSwitch(void)
6542 {
6543   int xx, yy;
6544
6545   game.switchgate_pos = !game.switchgate_pos;
6546
6547   SCAN_PLAYFIELD(xx, yy)
6548   {
6549     int element = Tile[xx][yy];
6550
6551     if (element == EL_SWITCHGATE_SWITCH_UP)
6552     {
6553       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6554       TEST_DrawLevelField(xx, yy);
6555     }
6556     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6557     {
6558       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6559       TEST_DrawLevelField(xx, yy);
6560     }
6561     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6562     {
6563       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6564       TEST_DrawLevelField(xx, yy);
6565     }
6566     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6567     {
6568       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6569       TEST_DrawLevelField(xx, yy);
6570     }
6571     else if (element == EL_SWITCHGATE_OPEN ||
6572              element == EL_SWITCHGATE_OPENING)
6573     {
6574       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6575
6576       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6577     }
6578     else if (element == EL_SWITCHGATE_CLOSED ||
6579              element == EL_SWITCHGATE_CLOSING)
6580     {
6581       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6582
6583       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6584     }
6585   }
6586 }
6587
6588 static int getInvisibleActiveFromInvisibleElement(int element)
6589 {
6590   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6591           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6592           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6593           element);
6594 }
6595
6596 static int getInvisibleFromInvisibleActiveElement(int element)
6597 {
6598   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6599           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6600           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6601           element);
6602 }
6603
6604 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6605 {
6606   int x, y;
6607
6608   SCAN_PLAYFIELD(x, y)
6609   {
6610     int element = Tile[x][y];
6611
6612     if (element == EL_LIGHT_SWITCH &&
6613         game.light_time_left > 0)
6614     {
6615       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6616       TEST_DrawLevelField(x, y);
6617     }
6618     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6619              game.light_time_left == 0)
6620     {
6621       Tile[x][y] = EL_LIGHT_SWITCH;
6622       TEST_DrawLevelField(x, y);
6623     }
6624     else if (element == EL_EMC_DRIPPER &&
6625              game.light_time_left > 0)
6626     {
6627       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6628       TEST_DrawLevelField(x, y);
6629     }
6630     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6631              game.light_time_left == 0)
6632     {
6633       Tile[x][y] = EL_EMC_DRIPPER;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_INVISIBLE_STEELWALL ||
6637              element == EL_INVISIBLE_WALL ||
6638              element == EL_INVISIBLE_SAND)
6639     {
6640       if (game.light_time_left > 0)
6641         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6642
6643       TEST_DrawLevelField(x, y);
6644
6645       // uncrumble neighbour fields, if needed
6646       if (element == EL_INVISIBLE_SAND)
6647         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6648     }
6649     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6650              element == EL_INVISIBLE_WALL_ACTIVE ||
6651              element == EL_INVISIBLE_SAND_ACTIVE)
6652     {
6653       if (game.light_time_left == 0)
6654         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6655
6656       TEST_DrawLevelField(x, y);
6657
6658       // re-crumble neighbour fields, if needed
6659       if (element == EL_INVISIBLE_SAND)
6660         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6661     }
6662   }
6663 }
6664
6665 static void RedrawAllInvisibleElementsForLenses(void)
6666 {
6667   int x, y;
6668
6669   SCAN_PLAYFIELD(x, y)
6670   {
6671     int element = Tile[x][y];
6672
6673     if (element == EL_EMC_DRIPPER &&
6674         game.lenses_time_left > 0)
6675     {
6676       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6677       TEST_DrawLevelField(x, y);
6678     }
6679     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6680              game.lenses_time_left == 0)
6681     {
6682       Tile[x][y] = EL_EMC_DRIPPER;
6683       TEST_DrawLevelField(x, y);
6684     }
6685     else if (element == EL_INVISIBLE_STEELWALL ||
6686              element == EL_INVISIBLE_WALL ||
6687              element == EL_INVISIBLE_SAND)
6688     {
6689       if (game.lenses_time_left > 0)
6690         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6691
6692       TEST_DrawLevelField(x, y);
6693
6694       // uncrumble neighbour fields, if needed
6695       if (element == EL_INVISIBLE_SAND)
6696         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6697     }
6698     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6699              element == EL_INVISIBLE_WALL_ACTIVE ||
6700              element == EL_INVISIBLE_SAND_ACTIVE)
6701     {
6702       if (game.lenses_time_left == 0)
6703         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6704
6705       TEST_DrawLevelField(x, y);
6706
6707       // re-crumble neighbour fields, if needed
6708       if (element == EL_INVISIBLE_SAND)
6709         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6710     }
6711   }
6712 }
6713
6714 static void RedrawAllInvisibleElementsForMagnifier(void)
6715 {
6716   int x, y;
6717
6718   SCAN_PLAYFIELD(x, y)
6719   {
6720     int element = Tile[x][y];
6721
6722     if (element == EL_EMC_FAKE_GRASS &&
6723         game.magnify_time_left > 0)
6724     {
6725       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6726       TEST_DrawLevelField(x, y);
6727     }
6728     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6729              game.magnify_time_left == 0)
6730     {
6731       Tile[x][y] = EL_EMC_FAKE_GRASS;
6732       TEST_DrawLevelField(x, y);
6733     }
6734     else if (IS_GATE_GRAY(element) &&
6735              game.magnify_time_left > 0)
6736     {
6737       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6738                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6739                     IS_EM_GATE_GRAY(element) ?
6740                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6741                     IS_EMC_GATE_GRAY(element) ?
6742                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6743                     IS_DC_GATE_GRAY(element) ?
6744                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6745                     element);
6746       TEST_DrawLevelField(x, y);
6747     }
6748     else if (IS_GATE_GRAY_ACTIVE(element) &&
6749              game.magnify_time_left == 0)
6750     {
6751       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6752                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6753                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6754                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6755                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6756                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6757                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6758                     EL_DC_GATE_WHITE_GRAY :
6759                     element);
6760       TEST_DrawLevelField(x, y);
6761     }
6762   }
6763 }
6764
6765 static void ToggleLightSwitch(int x, int y)
6766 {
6767   int element = Tile[x][y];
6768
6769   game.light_time_left =
6770     (element == EL_LIGHT_SWITCH ?
6771      level.time_light * FRAMES_PER_SECOND : 0);
6772
6773   RedrawAllLightSwitchesAndInvisibleElements();
6774 }
6775
6776 static void ActivateTimegateSwitch(int x, int y)
6777 {
6778   int xx, yy;
6779
6780   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6781
6782   SCAN_PLAYFIELD(xx, yy)
6783   {
6784     int element = Tile[xx][yy];
6785
6786     if (element == EL_TIMEGATE_CLOSED ||
6787         element == EL_TIMEGATE_CLOSING)
6788     {
6789       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6790       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6791     }
6792
6793     /*
6794     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6795     {
6796       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6797       TEST_DrawLevelField(xx, yy);
6798     }
6799     */
6800
6801   }
6802
6803   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6804                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6805 }
6806
6807 static void Impact(int x, int y)
6808 {
6809   boolean last_line = (y == lev_fieldy - 1);
6810   boolean object_hit = FALSE;
6811   boolean impact = (last_line || object_hit);
6812   int element = Tile[x][y];
6813   int smashed = EL_STEELWALL;
6814
6815   if (!last_line)       // check if element below was hit
6816   {
6817     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6818       return;
6819
6820     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6821                                          MovDir[x][y + 1] != MV_DOWN ||
6822                                          MovPos[x][y + 1] <= TILEY / 2));
6823
6824     // do not smash moving elements that left the smashed field in time
6825     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6826         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6827       object_hit = FALSE;
6828
6829 #if USE_QUICKSAND_IMPACT_BUGFIX
6830     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6831     {
6832       RemoveMovingField(x, y + 1);
6833       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6834       Tile[x][y + 2] = EL_ROCK;
6835       TEST_DrawLevelField(x, y + 2);
6836
6837       object_hit = TRUE;
6838     }
6839
6840     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6841     {
6842       RemoveMovingField(x, y + 1);
6843       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6844       Tile[x][y + 2] = EL_ROCK;
6845       TEST_DrawLevelField(x, y + 2);
6846
6847       object_hit = TRUE;
6848     }
6849 #endif
6850
6851     if (object_hit)
6852       smashed = MovingOrBlocked2Element(x, y + 1);
6853
6854     impact = (last_line || object_hit);
6855   }
6856
6857   if (!last_line && smashed == EL_ACID) // element falls into acid
6858   {
6859     SplashAcid(x, y + 1);
6860     return;
6861   }
6862
6863   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6864   // only reset graphic animation if graphic really changes after impact
6865   if (impact &&
6866       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6867   {
6868     ResetGfxAnimation(x, y);
6869     TEST_DrawLevelField(x, y);
6870   }
6871
6872   if (impact && CAN_EXPLODE_IMPACT(element))
6873   {
6874     Bang(x, y);
6875     return;
6876   }
6877   else if (impact && element == EL_PEARL &&
6878            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6879   {
6880     ResetGfxAnimation(x, y);
6881
6882     Tile[x][y] = EL_PEARL_BREAKING;
6883     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6884     return;
6885   }
6886   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6887   {
6888     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6889
6890     return;
6891   }
6892
6893   if (impact && element == EL_AMOEBA_DROP)
6894   {
6895     if (object_hit && IS_PLAYER(x, y + 1))
6896       KillPlayerUnlessEnemyProtected(x, y + 1);
6897     else if (object_hit && smashed == EL_PENGUIN)
6898       Bang(x, y + 1);
6899     else
6900     {
6901       Tile[x][y] = EL_AMOEBA_GROWING;
6902       Store[x][y] = EL_AMOEBA_WET;
6903
6904       ResetRandomAnimationValue(x, y);
6905     }
6906     return;
6907   }
6908
6909   if (object_hit)               // check which object was hit
6910   {
6911     if ((CAN_PASS_MAGIC_WALL(element) && 
6912          (smashed == EL_MAGIC_WALL ||
6913           smashed == EL_BD_MAGIC_WALL)) ||
6914         (CAN_PASS_DC_MAGIC_WALL(element) &&
6915          smashed == EL_DC_MAGIC_WALL))
6916     {
6917       int xx, yy;
6918       int activated_magic_wall =
6919         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6920          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6921          EL_DC_MAGIC_WALL_ACTIVE);
6922
6923       // activate magic wall / mill
6924       SCAN_PLAYFIELD(xx, yy)
6925       {
6926         if (Tile[xx][yy] == smashed)
6927           Tile[xx][yy] = activated_magic_wall;
6928       }
6929
6930       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6931       game.magic_wall_active = TRUE;
6932
6933       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6934                             SND_MAGIC_WALL_ACTIVATING :
6935                             smashed == EL_BD_MAGIC_WALL ?
6936                             SND_BD_MAGIC_WALL_ACTIVATING :
6937                             SND_DC_MAGIC_WALL_ACTIVATING));
6938     }
6939
6940     if (IS_PLAYER(x, y + 1))
6941     {
6942       if (CAN_SMASH_PLAYER(element))
6943       {
6944         KillPlayerUnlessEnemyProtected(x, y + 1);
6945         return;
6946       }
6947     }
6948     else if (smashed == EL_PENGUIN)
6949     {
6950       if (CAN_SMASH_PLAYER(element))
6951       {
6952         Bang(x, y + 1);
6953         return;
6954       }
6955     }
6956     else if (element == EL_BD_DIAMOND)
6957     {
6958       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6959       {
6960         Bang(x, y + 1);
6961         return;
6962       }
6963     }
6964     else if (((element == EL_SP_INFOTRON ||
6965                element == EL_SP_ZONK) &&
6966               (smashed == EL_SP_SNIKSNAK ||
6967                smashed == EL_SP_ELECTRON ||
6968                smashed == EL_SP_DISK_ORANGE)) ||
6969              (element == EL_SP_INFOTRON &&
6970               smashed == EL_SP_DISK_YELLOW))
6971     {
6972       Bang(x, y + 1);
6973       return;
6974     }
6975     else if (CAN_SMASH_EVERYTHING(element))
6976     {
6977       if (IS_CLASSIC_ENEMY(smashed) ||
6978           CAN_EXPLODE_SMASHED(smashed))
6979       {
6980         Bang(x, y + 1);
6981         return;
6982       }
6983       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6984       {
6985         if (smashed == EL_LAMP ||
6986             smashed == EL_LAMP_ACTIVE)
6987         {
6988           Bang(x, y + 1);
6989           return;
6990         }
6991         else if (smashed == EL_NUT)
6992         {
6993           Tile[x][y + 1] = EL_NUT_BREAKING;
6994           PlayLevelSound(x, y, SND_NUT_BREAKING);
6995           RaiseScoreElement(EL_NUT);
6996           return;
6997         }
6998         else if (smashed == EL_PEARL)
6999         {
7000           ResetGfxAnimation(x, y);
7001
7002           Tile[x][y + 1] = EL_PEARL_BREAKING;
7003           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7004           return;
7005         }
7006         else if (smashed == EL_DIAMOND)
7007         {
7008           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7009           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7010           return;
7011         }
7012         else if (IS_BELT_SWITCH(smashed))
7013         {
7014           ToggleBeltSwitch(x, y + 1);
7015         }
7016         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7017                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7018                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7019                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7020         {
7021           ToggleSwitchgateSwitch();
7022         }
7023         else if (smashed == EL_LIGHT_SWITCH ||
7024                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7025         {
7026           ToggleLightSwitch(x, y + 1);
7027         }
7028         else
7029         {
7030           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7031
7032           CheckElementChangeBySide(x, y + 1, smashed, element,
7033                                    CE_SWITCHED, CH_SIDE_TOP);
7034           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7035                                             CH_SIDE_TOP);
7036         }
7037       }
7038       else
7039       {
7040         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7041       }
7042     }
7043   }
7044
7045   // play sound of magic wall / mill
7046   if (!last_line &&
7047       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7048        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7049        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7050   {
7051     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7052       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7053     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7054       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7055     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7056       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7057
7058     return;
7059   }
7060
7061   // play sound of object that hits the ground
7062   if (last_line || object_hit)
7063     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7064 }
7065
7066 static void TurnRoundExt(int x, int y)
7067 {
7068   static struct
7069   {
7070     int dx, dy;
7071   } move_xy[] =
7072   {
7073     {  0,  0 },
7074     { -1,  0 },
7075     { +1,  0 },
7076     {  0,  0 },
7077     {  0, -1 },
7078     {  0,  0 }, { 0, 0 }, { 0, 0 },
7079     {  0, +1 }
7080   };
7081   static struct
7082   {
7083     int left, right, back;
7084   } turn[] =
7085   {
7086     { 0,        0,              0        },
7087     { MV_DOWN,  MV_UP,          MV_RIGHT },
7088     { MV_UP,    MV_DOWN,        MV_LEFT  },
7089     { 0,        0,              0        },
7090     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7091     { 0,        0,              0        },
7092     { 0,        0,              0        },
7093     { 0,        0,              0        },
7094     { MV_RIGHT, MV_LEFT,        MV_UP    }
7095   };
7096
7097   int element = Tile[x][y];
7098   int move_pattern = element_info[element].move_pattern;
7099
7100   int old_move_dir = MovDir[x][y];
7101   int left_dir  = turn[old_move_dir].left;
7102   int right_dir = turn[old_move_dir].right;
7103   int back_dir  = turn[old_move_dir].back;
7104
7105   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7106   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7107   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7108   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7109
7110   int left_x  = x + left_dx,  left_y  = y + left_dy;
7111   int right_x = x + right_dx, right_y = y + right_dy;
7112   int move_x  = x + move_dx,  move_y  = y + move_dy;
7113
7114   int xx, yy;
7115
7116   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7117   {
7118     TestIfBadThingTouchesOtherBadThing(x, y);
7119
7120     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7121       MovDir[x][y] = right_dir;
7122     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7123       MovDir[x][y] = left_dir;
7124
7125     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7126       MovDelay[x][y] = 9;
7127     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7128       MovDelay[x][y] = 1;
7129   }
7130   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7131   {
7132     TestIfBadThingTouchesOtherBadThing(x, y);
7133
7134     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7135       MovDir[x][y] = left_dir;
7136     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7137       MovDir[x][y] = right_dir;
7138
7139     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7140       MovDelay[x][y] = 9;
7141     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7142       MovDelay[x][y] = 1;
7143   }
7144   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7145   {
7146     TestIfBadThingTouchesOtherBadThing(x, y);
7147
7148     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7149       MovDir[x][y] = left_dir;
7150     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7151       MovDir[x][y] = right_dir;
7152
7153     if (MovDir[x][y] != old_move_dir)
7154       MovDelay[x][y] = 9;
7155   }
7156   else if (element == EL_YAMYAM)
7157   {
7158     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7159     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7160
7161     if (can_turn_left && can_turn_right)
7162       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7163     else if (can_turn_left)
7164       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7165     else if (can_turn_right)
7166       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7167     else
7168       MovDir[x][y] = back_dir;
7169
7170     MovDelay[x][y] = 16 + 16 * RND(3);
7171   }
7172   else if (element == EL_DARK_YAMYAM)
7173   {
7174     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7175                                                          left_x, left_y);
7176     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7177                                                          right_x, right_y);
7178
7179     if (can_turn_left && can_turn_right)
7180       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7181     else if (can_turn_left)
7182       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7183     else if (can_turn_right)
7184       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7185     else
7186       MovDir[x][y] = back_dir;
7187
7188     MovDelay[x][y] = 16 + 16 * RND(3);
7189   }
7190   else if (element == EL_PACMAN)
7191   {
7192     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7193     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7194
7195     if (can_turn_left && can_turn_right)
7196       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7197     else if (can_turn_left)
7198       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7199     else if (can_turn_right)
7200       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7201     else
7202       MovDir[x][y] = back_dir;
7203
7204     MovDelay[x][y] = 6 + RND(40);
7205   }
7206   else if (element == EL_PIG)
7207   {
7208     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7209     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7210     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7211     boolean should_turn_left, should_turn_right, should_move_on;
7212     int rnd_value = 24;
7213     int rnd = RND(rnd_value);
7214
7215     should_turn_left = (can_turn_left &&
7216                         (!can_move_on ||
7217                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7218                                                    y + back_dy + left_dy)));
7219     should_turn_right = (can_turn_right &&
7220                          (!can_move_on ||
7221                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7222                                                     y + back_dy + right_dy)));
7223     should_move_on = (can_move_on &&
7224                       (!can_turn_left ||
7225                        !can_turn_right ||
7226                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7227                                                  y + move_dy + left_dy) ||
7228                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7229                                                  y + move_dy + right_dy)));
7230
7231     if (should_turn_left || should_turn_right || should_move_on)
7232     {
7233       if (should_turn_left && should_turn_right && should_move_on)
7234         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7235                         rnd < 2 * rnd_value / 3 ? right_dir :
7236                         old_move_dir);
7237       else if (should_turn_left && should_turn_right)
7238         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7239       else if (should_turn_left && should_move_on)
7240         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7241       else if (should_turn_right && should_move_on)
7242         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7243       else if (should_turn_left)
7244         MovDir[x][y] = left_dir;
7245       else if (should_turn_right)
7246         MovDir[x][y] = right_dir;
7247       else if (should_move_on)
7248         MovDir[x][y] = old_move_dir;
7249     }
7250     else if (can_move_on && rnd > rnd_value / 8)
7251       MovDir[x][y] = old_move_dir;
7252     else if (can_turn_left && can_turn_right)
7253       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7254     else if (can_turn_left && rnd > rnd_value / 8)
7255       MovDir[x][y] = left_dir;
7256     else if (can_turn_right && rnd > rnd_value/8)
7257       MovDir[x][y] = right_dir;
7258     else
7259       MovDir[x][y] = back_dir;
7260
7261     xx = x + move_xy[MovDir[x][y]].dx;
7262     yy = y + move_xy[MovDir[x][y]].dy;
7263
7264     if (!IN_LEV_FIELD(xx, yy) ||
7265         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7266       MovDir[x][y] = old_move_dir;
7267
7268     MovDelay[x][y] = 0;
7269   }
7270   else if (element == EL_DRAGON)
7271   {
7272     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7273     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7274     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7275     int rnd_value = 24;
7276     int rnd = RND(rnd_value);
7277
7278     if (can_move_on && rnd > rnd_value / 8)
7279       MovDir[x][y] = old_move_dir;
7280     else if (can_turn_left && can_turn_right)
7281       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7282     else if (can_turn_left && rnd > rnd_value / 8)
7283       MovDir[x][y] = left_dir;
7284     else if (can_turn_right && rnd > rnd_value / 8)
7285       MovDir[x][y] = right_dir;
7286     else
7287       MovDir[x][y] = back_dir;
7288
7289     xx = x + move_xy[MovDir[x][y]].dx;
7290     yy = y + move_xy[MovDir[x][y]].dy;
7291
7292     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7293       MovDir[x][y] = old_move_dir;
7294
7295     MovDelay[x][y] = 0;
7296   }
7297   else if (element == EL_MOLE)
7298   {
7299     boolean can_move_on =
7300       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7301                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7302                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7303     if (!can_move_on)
7304     {
7305       boolean can_turn_left =
7306         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7307                               IS_AMOEBOID(Tile[left_x][left_y])));
7308
7309       boolean can_turn_right =
7310         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7311                               IS_AMOEBOID(Tile[right_x][right_y])));
7312
7313       if (can_turn_left && can_turn_right)
7314         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7315       else if (can_turn_left)
7316         MovDir[x][y] = left_dir;
7317       else
7318         MovDir[x][y] = right_dir;
7319     }
7320
7321     if (MovDir[x][y] != old_move_dir)
7322       MovDelay[x][y] = 9;
7323   }
7324   else if (element == EL_BALLOON)
7325   {
7326     MovDir[x][y] = game.wind_direction;
7327     MovDelay[x][y] = 0;
7328   }
7329   else if (element == EL_SPRING)
7330   {
7331     if (MovDir[x][y] & MV_HORIZONTAL)
7332     {
7333       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7334           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7335       {
7336         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7337         ResetGfxAnimation(move_x, move_y);
7338         TEST_DrawLevelField(move_x, move_y);
7339
7340         MovDir[x][y] = back_dir;
7341       }
7342       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7343                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7344         MovDir[x][y] = MV_NONE;
7345     }
7346
7347     MovDelay[x][y] = 0;
7348   }
7349   else if (element == EL_ROBOT ||
7350            element == EL_SATELLITE ||
7351            element == EL_PENGUIN ||
7352            element == EL_EMC_ANDROID)
7353   {
7354     int attr_x = -1, attr_y = -1;
7355
7356     if (game.all_players_gone)
7357     {
7358       attr_x = game.exit_x;
7359       attr_y = game.exit_y;
7360     }
7361     else
7362     {
7363       int i;
7364
7365       for (i = 0; i < MAX_PLAYERS; i++)
7366       {
7367         struct PlayerInfo *player = &stored_player[i];
7368         int jx = player->jx, jy = player->jy;
7369
7370         if (!player->active)
7371           continue;
7372
7373         if (attr_x == -1 ||
7374             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7375         {
7376           attr_x = jx;
7377           attr_y = jy;
7378         }
7379       }
7380     }
7381
7382     if (element == EL_ROBOT &&
7383         game.robot_wheel_x >= 0 &&
7384         game.robot_wheel_y >= 0 &&
7385         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7386          game.engine_version < VERSION_IDENT(3,1,0,0)))
7387     {
7388       attr_x = game.robot_wheel_x;
7389       attr_y = game.robot_wheel_y;
7390     }
7391
7392     if (element == EL_PENGUIN)
7393     {
7394       int i;
7395       struct XY *xy = xy_topdown;
7396
7397       for (i = 0; i < NUM_DIRECTIONS; i++)
7398       {
7399         int ex = x + xy[i].x;
7400         int ey = y + xy[i].y;
7401
7402         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7403                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7404                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7405                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7406         {
7407           attr_x = ex;
7408           attr_y = ey;
7409           break;
7410         }
7411       }
7412     }
7413
7414     MovDir[x][y] = MV_NONE;
7415     if (attr_x < x)
7416       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7417     else if (attr_x > x)
7418       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7419     if (attr_y < y)
7420       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7421     else if (attr_y > y)
7422       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7423
7424     if (element == EL_ROBOT)
7425     {
7426       int newx, newy;
7427
7428       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7429         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7430       Moving2Blocked(x, y, &newx, &newy);
7431
7432       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7433         MovDelay[x][y] = 8 + 8 * !RND(3);
7434       else
7435         MovDelay[x][y] = 16;
7436     }
7437     else if (element == EL_PENGUIN)
7438     {
7439       int newx, newy;
7440
7441       MovDelay[x][y] = 1;
7442
7443       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7444       {
7445         boolean first_horiz = RND(2);
7446         int new_move_dir = MovDir[x][y];
7447
7448         MovDir[x][y] =
7449           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7450         Moving2Blocked(x, y, &newx, &newy);
7451
7452         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7453           return;
7454
7455         MovDir[x][y] =
7456           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7457         Moving2Blocked(x, y, &newx, &newy);
7458
7459         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7460           return;
7461
7462         MovDir[x][y] = old_move_dir;
7463         return;
7464       }
7465     }
7466     else if (element == EL_SATELLITE)
7467     {
7468       int newx, newy;
7469
7470       MovDelay[x][y] = 1;
7471
7472       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7473       {
7474         boolean first_horiz = RND(2);
7475         int new_move_dir = MovDir[x][y];
7476
7477         MovDir[x][y] =
7478           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7479         Moving2Blocked(x, y, &newx, &newy);
7480
7481         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7482           return;
7483
7484         MovDir[x][y] =
7485           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7486         Moving2Blocked(x, y, &newx, &newy);
7487
7488         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7489           return;
7490
7491         MovDir[x][y] = old_move_dir;
7492         return;
7493       }
7494     }
7495     else if (element == EL_EMC_ANDROID)
7496     {
7497       static int check_pos[16] =
7498       {
7499         -1,             //  0 => (invalid)
7500         7,              //  1 => MV_LEFT
7501         3,              //  2 => MV_RIGHT
7502         -1,             //  3 => (invalid)
7503         1,              //  4 =>            MV_UP
7504         0,              //  5 => MV_LEFT  | MV_UP
7505         2,              //  6 => MV_RIGHT | MV_UP
7506         -1,             //  7 => (invalid)
7507         5,              //  8 =>            MV_DOWN
7508         6,              //  9 => MV_LEFT  | MV_DOWN
7509         4,              // 10 => MV_RIGHT | MV_DOWN
7510         -1,             // 11 => (invalid)
7511         -1,             // 12 => (invalid)
7512         -1,             // 13 => (invalid)
7513         -1,             // 14 => (invalid)
7514         -1,             // 15 => (invalid)
7515       };
7516       static struct
7517       {
7518         int dx, dy;
7519         int dir;
7520       } check_xy[8] =
7521       {
7522         { -1, -1,       MV_LEFT  | MV_UP   },
7523         {  0, -1,                  MV_UP   },
7524         { +1, -1,       MV_RIGHT | MV_UP   },
7525         { +1,  0,       MV_RIGHT           },
7526         { +1, +1,       MV_RIGHT | MV_DOWN },
7527         {  0, +1,                  MV_DOWN },
7528         { -1, +1,       MV_LEFT  | MV_DOWN },
7529         { -1,  0,       MV_LEFT            },
7530       };
7531       int start_pos, check_order;
7532       boolean can_clone = FALSE;
7533       int i;
7534
7535       // check if there is any free field around current position
7536       for (i = 0; i < 8; i++)
7537       {
7538         int newx = x + check_xy[i].dx;
7539         int newy = y + check_xy[i].dy;
7540
7541         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7542         {
7543           can_clone = TRUE;
7544
7545           break;
7546         }
7547       }
7548
7549       if (can_clone)            // randomly find an element to clone
7550       {
7551         can_clone = FALSE;
7552
7553         start_pos = check_pos[RND(8)];
7554         check_order = (RND(2) ? -1 : +1);
7555
7556         for (i = 0; i < 8; i++)
7557         {
7558           int pos_raw = start_pos + i * check_order;
7559           int pos = (pos_raw + 8) % 8;
7560           int newx = x + check_xy[pos].dx;
7561           int newy = y + check_xy[pos].dy;
7562
7563           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7564           {
7565             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7566             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7567
7568             Store[x][y] = Tile[newx][newy];
7569
7570             can_clone = TRUE;
7571
7572             break;
7573           }
7574         }
7575       }
7576
7577       if (can_clone)            // randomly find a direction to move
7578       {
7579         can_clone = FALSE;
7580
7581         start_pos = check_pos[RND(8)];
7582         check_order = (RND(2) ? -1 : +1);
7583
7584         for (i = 0; i < 8; i++)
7585         {
7586           int pos_raw = start_pos + i * check_order;
7587           int pos = (pos_raw + 8) % 8;
7588           int newx = x + check_xy[pos].dx;
7589           int newy = y + check_xy[pos].dy;
7590           int new_move_dir = check_xy[pos].dir;
7591
7592           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7593           {
7594             MovDir[x][y] = new_move_dir;
7595             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7596
7597             can_clone = TRUE;
7598
7599             break;
7600           }
7601         }
7602       }
7603
7604       if (can_clone)            // cloning and moving successful
7605         return;
7606
7607       // cannot clone -- try to move towards player
7608
7609       start_pos = check_pos[MovDir[x][y] & 0x0f];
7610       check_order = (RND(2) ? -1 : +1);
7611
7612       for (i = 0; i < 3; i++)
7613       {
7614         // first check start_pos, then previous/next or (next/previous) pos
7615         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7616         int pos = (pos_raw + 8) % 8;
7617         int newx = x + check_xy[pos].dx;
7618         int newy = y + check_xy[pos].dy;
7619         int new_move_dir = check_xy[pos].dir;
7620
7621         if (IS_PLAYER(newx, newy))
7622           break;
7623
7624         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7625         {
7626           MovDir[x][y] = new_move_dir;
7627           MovDelay[x][y] = level.android_move_time * 8 + 1;
7628
7629           break;
7630         }
7631       }
7632     }
7633   }
7634   else if (move_pattern == MV_TURNING_LEFT ||
7635            move_pattern == MV_TURNING_RIGHT ||
7636            move_pattern == MV_TURNING_LEFT_RIGHT ||
7637            move_pattern == MV_TURNING_RIGHT_LEFT ||
7638            move_pattern == MV_TURNING_RANDOM ||
7639            move_pattern == MV_ALL_DIRECTIONS)
7640   {
7641     boolean can_turn_left =
7642       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7643     boolean can_turn_right =
7644       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7645
7646     if (element_info[element].move_stepsize == 0)       // "not moving"
7647       return;
7648
7649     if (move_pattern == MV_TURNING_LEFT)
7650       MovDir[x][y] = left_dir;
7651     else if (move_pattern == MV_TURNING_RIGHT)
7652       MovDir[x][y] = right_dir;
7653     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7654       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7655     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7656       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7657     else if (move_pattern == MV_TURNING_RANDOM)
7658       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7659                       can_turn_right && !can_turn_left ? right_dir :
7660                       RND(2) ? left_dir : right_dir);
7661     else if (can_turn_left && can_turn_right)
7662       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7663     else if (can_turn_left)
7664       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7665     else if (can_turn_right)
7666       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7667     else
7668       MovDir[x][y] = back_dir;
7669
7670     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7671   }
7672   else if (move_pattern == MV_HORIZONTAL ||
7673            move_pattern == MV_VERTICAL)
7674   {
7675     if (move_pattern & old_move_dir)
7676       MovDir[x][y] = back_dir;
7677     else if (move_pattern == MV_HORIZONTAL)
7678       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7679     else if (move_pattern == MV_VERTICAL)
7680       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7681
7682     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683   }
7684   else if (move_pattern & MV_ANY_DIRECTION)
7685   {
7686     MovDir[x][y] = move_pattern;
7687     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7688   }
7689   else if (move_pattern & MV_WIND_DIRECTION)
7690   {
7691     MovDir[x][y] = game.wind_direction;
7692     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7693   }
7694   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7695   {
7696     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7697       MovDir[x][y] = left_dir;
7698     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7699       MovDir[x][y] = right_dir;
7700
7701     if (MovDir[x][y] != old_move_dir)
7702       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7703   }
7704   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7705   {
7706     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7707       MovDir[x][y] = right_dir;
7708     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7709       MovDir[x][y] = left_dir;
7710
7711     if (MovDir[x][y] != old_move_dir)
7712       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7713   }
7714   else if (move_pattern == MV_TOWARDS_PLAYER ||
7715            move_pattern == MV_AWAY_FROM_PLAYER)
7716   {
7717     int attr_x = -1, attr_y = -1;
7718     int newx, newy;
7719     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7720
7721     if (game.all_players_gone)
7722     {
7723       attr_x = game.exit_x;
7724       attr_y = game.exit_y;
7725     }
7726     else
7727     {
7728       int i;
7729
7730       for (i = 0; i < MAX_PLAYERS; i++)
7731       {
7732         struct PlayerInfo *player = &stored_player[i];
7733         int jx = player->jx, jy = player->jy;
7734
7735         if (!player->active)
7736           continue;
7737
7738         if (attr_x == -1 ||
7739             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7740         {
7741           attr_x = jx;
7742           attr_y = jy;
7743         }
7744       }
7745     }
7746
7747     MovDir[x][y] = MV_NONE;
7748     if (attr_x < x)
7749       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7750     else if (attr_x > x)
7751       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7752     if (attr_y < y)
7753       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7754     else if (attr_y > y)
7755       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7756
7757     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7758
7759     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7760     {
7761       boolean first_horiz = RND(2);
7762       int new_move_dir = MovDir[x][y];
7763
7764       if (element_info[element].move_stepsize == 0)     // "not moving"
7765       {
7766         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7767         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7768
7769         return;
7770       }
7771
7772       MovDir[x][y] =
7773         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7774       Moving2Blocked(x, y, &newx, &newy);
7775
7776       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7777         return;
7778
7779       MovDir[x][y] =
7780         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7781       Moving2Blocked(x, y, &newx, &newy);
7782
7783       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7784         return;
7785
7786       MovDir[x][y] = old_move_dir;
7787     }
7788   }
7789   else if (move_pattern == MV_WHEN_PUSHED ||
7790            move_pattern == MV_WHEN_DROPPED)
7791   {
7792     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7793       MovDir[x][y] = MV_NONE;
7794
7795     MovDelay[x][y] = 0;
7796   }
7797   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7798   {
7799     struct XY *test_xy = xy_topdown;
7800     static int test_dir[4] =
7801     {
7802       MV_UP,
7803       MV_LEFT,
7804       MV_RIGHT,
7805       MV_DOWN
7806     };
7807     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7808     int move_preference = -1000000;     // start with very low preference
7809     int new_move_dir = MV_NONE;
7810     int start_test = RND(4);
7811     int i;
7812
7813     for (i = 0; i < NUM_DIRECTIONS; i++)
7814     {
7815       int j = (start_test + i) % 4;
7816       int move_dir = test_dir[j];
7817       int move_dir_preference;
7818
7819       xx = x + test_xy[j].x;
7820       yy = y + test_xy[j].y;
7821
7822       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7823           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7824       {
7825         new_move_dir = move_dir;
7826
7827         break;
7828       }
7829
7830       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7831         continue;
7832
7833       move_dir_preference = -1 * RunnerVisit[xx][yy];
7834       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7835         move_dir_preference = PlayerVisit[xx][yy];
7836
7837       if (move_dir_preference > move_preference)
7838       {
7839         // prefer field that has not been visited for the longest time
7840         move_preference = move_dir_preference;
7841         new_move_dir = move_dir;
7842       }
7843       else if (move_dir_preference == move_preference &&
7844                move_dir == old_move_dir)
7845       {
7846         // prefer last direction when all directions are preferred equally
7847         move_preference = move_dir_preference;
7848         new_move_dir = move_dir;
7849       }
7850     }
7851
7852     MovDir[x][y] = new_move_dir;
7853     if (old_move_dir != new_move_dir)
7854       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7855   }
7856 }
7857
7858 static void TurnRound(int x, int y)
7859 {
7860   int direction = MovDir[x][y];
7861
7862   TurnRoundExt(x, y);
7863
7864   GfxDir[x][y] = MovDir[x][y];
7865
7866   if (direction != MovDir[x][y])
7867     GfxFrame[x][y] = 0;
7868
7869   if (MovDelay[x][y])
7870     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7871
7872   ResetGfxFrame(x, y);
7873 }
7874
7875 static boolean JustBeingPushed(int x, int y)
7876 {
7877   int i;
7878
7879   for (i = 0; i < MAX_PLAYERS; i++)
7880   {
7881     struct PlayerInfo *player = &stored_player[i];
7882
7883     if (player->active && player->is_pushing && player->MovPos)
7884     {
7885       int next_jx = player->jx + (player->jx - player->last_jx);
7886       int next_jy = player->jy + (player->jy - player->last_jy);
7887
7888       if (x == next_jx && y == next_jy)
7889         return TRUE;
7890     }
7891   }
7892
7893   return FALSE;
7894 }
7895
7896 static void StartMoving(int x, int y)
7897 {
7898   boolean started_moving = FALSE;       // some elements can fall _and_ move
7899   int element = Tile[x][y];
7900
7901   if (Stop[x][y])
7902     return;
7903
7904   if (MovDelay[x][y] == 0)
7905     GfxAction[x][y] = ACTION_DEFAULT;
7906
7907   if (CAN_FALL(element) && y < lev_fieldy - 1)
7908   {
7909     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7910         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7911       if (JustBeingPushed(x, y))
7912         return;
7913
7914     if (element == EL_QUICKSAND_FULL)
7915     {
7916       if (IS_FREE(x, y + 1))
7917       {
7918         InitMovingField(x, y, MV_DOWN);
7919         started_moving = TRUE;
7920
7921         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7922 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7923         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7924           Store[x][y] = EL_ROCK;
7925 #else
7926         Store[x][y] = EL_ROCK;
7927 #endif
7928
7929         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7930       }
7931       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7932       {
7933         if (!MovDelay[x][y])
7934         {
7935           MovDelay[x][y] = TILEY + 1;
7936
7937           ResetGfxAnimation(x, y);
7938           ResetGfxAnimation(x, y + 1);
7939         }
7940
7941         if (MovDelay[x][y])
7942         {
7943           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7944           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7945
7946           MovDelay[x][y]--;
7947           if (MovDelay[x][y])
7948             return;
7949         }
7950
7951         Tile[x][y] = EL_QUICKSAND_EMPTY;
7952         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7953         Store[x][y + 1] = Store[x][y];
7954         Store[x][y] = 0;
7955
7956         PlayLevelSoundAction(x, y, ACTION_FILLING);
7957       }
7958       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7959       {
7960         if (!MovDelay[x][y])
7961         {
7962           MovDelay[x][y] = TILEY + 1;
7963
7964           ResetGfxAnimation(x, y);
7965           ResetGfxAnimation(x, y + 1);
7966         }
7967
7968         if (MovDelay[x][y])
7969         {
7970           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7971           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7972
7973           MovDelay[x][y]--;
7974           if (MovDelay[x][y])
7975             return;
7976         }
7977
7978         Tile[x][y] = EL_QUICKSAND_EMPTY;
7979         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7980         Store[x][y + 1] = Store[x][y];
7981         Store[x][y] = 0;
7982
7983         PlayLevelSoundAction(x, y, ACTION_FILLING);
7984       }
7985     }
7986     else if (element == EL_QUICKSAND_FAST_FULL)
7987     {
7988       if (IS_FREE(x, y + 1))
7989       {
7990         InitMovingField(x, y, MV_DOWN);
7991         started_moving = TRUE;
7992
7993         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7994 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7995         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7996           Store[x][y] = EL_ROCK;
7997 #else
7998         Store[x][y] = EL_ROCK;
7999 #endif
8000
8001         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8002       }
8003       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8004       {
8005         if (!MovDelay[x][y])
8006         {
8007           MovDelay[x][y] = TILEY + 1;
8008
8009           ResetGfxAnimation(x, y);
8010           ResetGfxAnimation(x, y + 1);
8011         }
8012
8013         if (MovDelay[x][y])
8014         {
8015           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8016           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8017
8018           MovDelay[x][y]--;
8019           if (MovDelay[x][y])
8020             return;
8021         }
8022
8023         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8024         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8025         Store[x][y + 1] = Store[x][y];
8026         Store[x][y] = 0;
8027
8028         PlayLevelSoundAction(x, y, ACTION_FILLING);
8029       }
8030       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8031       {
8032         if (!MovDelay[x][y])
8033         {
8034           MovDelay[x][y] = TILEY + 1;
8035
8036           ResetGfxAnimation(x, y);
8037           ResetGfxAnimation(x, y + 1);
8038         }
8039
8040         if (MovDelay[x][y])
8041         {
8042           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8043           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8044
8045           MovDelay[x][y]--;
8046           if (MovDelay[x][y])
8047             return;
8048         }
8049
8050         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8051         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8052         Store[x][y + 1] = Store[x][y];
8053         Store[x][y] = 0;
8054
8055         PlayLevelSoundAction(x, y, ACTION_FILLING);
8056       }
8057     }
8058     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8059              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8060     {
8061       InitMovingField(x, y, MV_DOWN);
8062       started_moving = TRUE;
8063
8064       Tile[x][y] = EL_QUICKSAND_FILLING;
8065       Store[x][y] = element;
8066
8067       PlayLevelSoundAction(x, y, ACTION_FILLING);
8068     }
8069     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8070              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8071     {
8072       InitMovingField(x, y, MV_DOWN);
8073       started_moving = TRUE;
8074
8075       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8076       Store[x][y] = element;
8077
8078       PlayLevelSoundAction(x, y, ACTION_FILLING);
8079     }
8080     else if (element == EL_MAGIC_WALL_FULL)
8081     {
8082       if (IS_FREE(x, y + 1))
8083       {
8084         InitMovingField(x, y, MV_DOWN);
8085         started_moving = TRUE;
8086
8087         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8088         Store[x][y] = EL_CHANGED(Store[x][y]);
8089       }
8090       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8091       {
8092         if (!MovDelay[x][y])
8093           MovDelay[x][y] = TILEY / 4 + 1;
8094
8095         if (MovDelay[x][y])
8096         {
8097           MovDelay[x][y]--;
8098           if (MovDelay[x][y])
8099             return;
8100         }
8101
8102         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8103         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8104         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8105         Store[x][y] = 0;
8106       }
8107     }
8108     else if (element == EL_BD_MAGIC_WALL_FULL)
8109     {
8110       if (IS_FREE(x, y + 1))
8111       {
8112         InitMovingField(x, y, MV_DOWN);
8113         started_moving = TRUE;
8114
8115         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8116         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8117       }
8118       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8119       {
8120         if (!MovDelay[x][y])
8121           MovDelay[x][y] = TILEY / 4 + 1;
8122
8123         if (MovDelay[x][y])
8124         {
8125           MovDelay[x][y]--;
8126           if (MovDelay[x][y])
8127             return;
8128         }
8129
8130         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8131         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8132         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8133         Store[x][y] = 0;
8134       }
8135     }
8136     else if (element == EL_DC_MAGIC_WALL_FULL)
8137     {
8138       if (IS_FREE(x, y + 1))
8139       {
8140         InitMovingField(x, y, MV_DOWN);
8141         started_moving = TRUE;
8142
8143         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8144         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8145       }
8146       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8147       {
8148         if (!MovDelay[x][y])
8149           MovDelay[x][y] = TILEY / 4 + 1;
8150
8151         if (MovDelay[x][y])
8152         {
8153           MovDelay[x][y]--;
8154           if (MovDelay[x][y])
8155             return;
8156         }
8157
8158         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8159         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8160         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8161         Store[x][y] = 0;
8162       }
8163     }
8164     else if ((CAN_PASS_MAGIC_WALL(element) &&
8165               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8166                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8167              (CAN_PASS_DC_MAGIC_WALL(element) &&
8168               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8169
8170     {
8171       InitMovingField(x, y, MV_DOWN);
8172       started_moving = TRUE;
8173
8174       Tile[x][y] =
8175         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8176          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8177          EL_DC_MAGIC_WALL_FILLING);
8178       Store[x][y] = element;
8179     }
8180     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8181     {
8182       SplashAcid(x, y + 1);
8183
8184       InitMovingField(x, y, MV_DOWN);
8185       started_moving = TRUE;
8186
8187       Store[x][y] = EL_ACID;
8188     }
8189     else if (
8190              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8191               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8192              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8193               CAN_FALL(element) && WasJustFalling[x][y] &&
8194               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8195
8196              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8197               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8198               (Tile[x][y + 1] == EL_BLOCKED)))
8199     {
8200       /* this is needed for a special case not covered by calling "Impact()"
8201          from "ContinueMoving()": if an element moves to a tile directly below
8202          another element which was just falling on that tile (which was empty
8203          in the previous frame), the falling element above would just stop
8204          instead of smashing the element below (in previous version, the above
8205          element was just checked for "moving" instead of "falling", resulting
8206          in incorrect smashes caused by horizontal movement of the above
8207          element; also, the case of the player being the element to smash was
8208          simply not covered here... :-/ ) */
8209
8210       CheckCollision[x][y] = 0;
8211       CheckImpact[x][y] = 0;
8212
8213       Impact(x, y);
8214     }
8215     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8216     {
8217       if (MovDir[x][y] == MV_NONE)
8218       {
8219         InitMovingField(x, y, MV_DOWN);
8220         started_moving = TRUE;
8221       }
8222     }
8223     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8224     {
8225       if (WasJustFalling[x][y]) // prevent animation from being restarted
8226         MovDir[x][y] = MV_DOWN;
8227
8228       InitMovingField(x, y, MV_DOWN);
8229       started_moving = TRUE;
8230     }
8231     else if (element == EL_AMOEBA_DROP)
8232     {
8233       Tile[x][y] = EL_AMOEBA_GROWING;
8234       Store[x][y] = EL_AMOEBA_WET;
8235     }
8236     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8237               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8238              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8239              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8240     {
8241       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8242                                 (IS_FREE(x - 1, y + 1) ||
8243                                  Tile[x - 1][y + 1] == EL_ACID));
8244       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8245                                 (IS_FREE(x + 1, y + 1) ||
8246                                  Tile[x + 1][y + 1] == EL_ACID));
8247       boolean can_fall_any  = (can_fall_left || can_fall_right);
8248       boolean can_fall_both = (can_fall_left && can_fall_right);
8249       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8250
8251       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8252       {
8253         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8254           can_fall_right = FALSE;
8255         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8256           can_fall_left = FALSE;
8257         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8258           can_fall_right = FALSE;
8259         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8260           can_fall_left = FALSE;
8261
8262         can_fall_any  = (can_fall_left || can_fall_right);
8263         can_fall_both = FALSE;
8264       }
8265
8266       if (can_fall_both)
8267       {
8268         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8269           can_fall_right = FALSE;       // slip down on left side
8270         else
8271           can_fall_left = !(can_fall_right = RND(2));
8272
8273         can_fall_both = FALSE;
8274       }
8275
8276       if (can_fall_any)
8277       {
8278         // if not determined otherwise, prefer left side for slipping down
8279         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8280         started_moving = TRUE;
8281       }
8282     }
8283     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8284     {
8285       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8286       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8287       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8288       int belt_dir = game.belt_dir[belt_nr];
8289
8290       if ((belt_dir == MV_LEFT  && left_is_free) ||
8291           (belt_dir == MV_RIGHT && right_is_free))
8292       {
8293         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8294
8295         InitMovingField(x, y, belt_dir);
8296         started_moving = TRUE;
8297
8298         Pushed[x][y] = TRUE;
8299         Pushed[nextx][y] = TRUE;
8300
8301         GfxAction[x][y] = ACTION_DEFAULT;
8302       }
8303       else
8304       {
8305         MovDir[x][y] = 0;       // if element was moving, stop it
8306       }
8307     }
8308   }
8309
8310   // not "else if" because of elements that can fall and move (EL_SPRING)
8311   if (CAN_MOVE(element) && !started_moving)
8312   {
8313     int move_pattern = element_info[element].move_pattern;
8314     int newx, newy;
8315
8316     Moving2Blocked(x, y, &newx, &newy);
8317
8318     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8319       return;
8320
8321     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8322         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8323     {
8324       WasJustMoving[x][y] = 0;
8325       CheckCollision[x][y] = 0;
8326
8327       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8328
8329       if (Tile[x][y] != element)        // element has changed
8330         return;
8331     }
8332
8333     if (!MovDelay[x][y])        // start new movement phase
8334     {
8335       // all objects that can change their move direction after each step
8336       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8337
8338       if (element != EL_YAMYAM &&
8339           element != EL_DARK_YAMYAM &&
8340           element != EL_PACMAN &&
8341           !(move_pattern & MV_ANY_DIRECTION) &&
8342           move_pattern != MV_TURNING_LEFT &&
8343           move_pattern != MV_TURNING_RIGHT &&
8344           move_pattern != MV_TURNING_LEFT_RIGHT &&
8345           move_pattern != MV_TURNING_RIGHT_LEFT &&
8346           move_pattern != MV_TURNING_RANDOM)
8347       {
8348         TurnRound(x, y);
8349
8350         if (MovDelay[x][y] && (element == EL_BUG ||
8351                                element == EL_SPACESHIP ||
8352                                element == EL_SP_SNIKSNAK ||
8353                                element == EL_SP_ELECTRON ||
8354                                element == EL_MOLE))
8355           TEST_DrawLevelField(x, y);
8356       }
8357     }
8358
8359     if (MovDelay[x][y])         // wait some time before next movement
8360     {
8361       MovDelay[x][y]--;
8362
8363       if (element == EL_ROBOT ||
8364           element == EL_YAMYAM ||
8365           element == EL_DARK_YAMYAM)
8366       {
8367         DrawLevelElementAnimationIfNeeded(x, y, element);
8368         PlayLevelSoundAction(x, y, ACTION_WAITING);
8369       }
8370       else if (element == EL_SP_ELECTRON)
8371         DrawLevelElementAnimationIfNeeded(x, y, element);
8372       else if (element == EL_DRAGON)
8373       {
8374         int i;
8375         int dir = MovDir[x][y];
8376         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8377         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8378         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8379                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8380                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8381                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8382         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8383
8384         GfxAction[x][y] = ACTION_ATTACKING;
8385
8386         if (IS_PLAYER(x, y))
8387           DrawPlayerField(x, y);
8388         else
8389           TEST_DrawLevelField(x, y);
8390
8391         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8392
8393         for (i = 1; i <= 3; i++)
8394         {
8395           int xx = x + i * dx;
8396           int yy = y + i * dy;
8397           int sx = SCREENX(xx);
8398           int sy = SCREENY(yy);
8399           int flame_graphic = graphic + (i - 1);
8400
8401           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8402             break;
8403
8404           if (MovDelay[x][y])
8405           {
8406             int flamed = MovingOrBlocked2Element(xx, yy);
8407
8408             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8409               Bang(xx, yy);
8410             else
8411               RemoveMovingField(xx, yy);
8412
8413             ChangeDelay[xx][yy] = 0;
8414
8415             Tile[xx][yy] = EL_FLAMES;
8416
8417             if (IN_SCR_FIELD(sx, sy))
8418             {
8419               TEST_DrawLevelFieldCrumbled(xx, yy);
8420               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8421             }
8422           }
8423           else
8424           {
8425             if (Tile[xx][yy] == EL_FLAMES)
8426               Tile[xx][yy] = EL_EMPTY;
8427             TEST_DrawLevelField(xx, yy);
8428           }
8429         }
8430       }
8431
8432       if (MovDelay[x][y])       // element still has to wait some time
8433       {
8434         PlayLevelSoundAction(x, y, ACTION_WAITING);
8435
8436         return;
8437       }
8438     }
8439
8440     // now make next step
8441
8442     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8443
8444     if (DONT_COLLIDE_WITH(element) &&
8445         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8446         !PLAYER_ENEMY_PROTECTED(newx, newy))
8447     {
8448       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8449
8450       return;
8451     }
8452
8453     else if (CAN_MOVE_INTO_ACID(element) &&
8454              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8455              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8456              (MovDir[x][y] == MV_DOWN ||
8457               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8458     {
8459       SplashAcid(newx, newy);
8460       Store[x][y] = EL_ACID;
8461     }
8462     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8463     {
8464       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8465           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8466           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8467           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8468       {
8469         RemoveField(x, y);
8470         TEST_DrawLevelField(x, y);
8471
8472         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8473         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8474           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8475
8476         game.friends_still_needed--;
8477         if (!game.friends_still_needed &&
8478             !game.GameOver &&
8479             game.all_players_gone)
8480           LevelSolved();
8481
8482         return;
8483       }
8484       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8485       {
8486         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8487           TEST_DrawLevelField(newx, newy);
8488         else
8489           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8490       }
8491       else if (!IS_FREE(newx, newy))
8492       {
8493         GfxAction[x][y] = ACTION_WAITING;
8494
8495         if (IS_PLAYER(x, y))
8496           DrawPlayerField(x, y);
8497         else
8498           TEST_DrawLevelField(x, y);
8499
8500         return;
8501       }
8502     }
8503     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8504     {
8505       if (IS_FOOD_PIG(Tile[newx][newy]))
8506       {
8507         if (IS_MOVING(newx, newy))
8508           RemoveMovingField(newx, newy);
8509         else
8510         {
8511           Tile[newx][newy] = EL_EMPTY;
8512           TEST_DrawLevelField(newx, newy);
8513         }
8514
8515         PlayLevelSound(x, y, SND_PIG_DIGGING);
8516       }
8517       else if (!IS_FREE(newx, newy))
8518       {
8519         if (IS_PLAYER(x, y))
8520           DrawPlayerField(x, y);
8521         else
8522           TEST_DrawLevelField(x, y);
8523
8524         return;
8525       }
8526     }
8527     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8528     {
8529       if (Store[x][y] != EL_EMPTY)
8530       {
8531         boolean can_clone = FALSE;
8532         int xx, yy;
8533
8534         // check if element to clone is still there
8535         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8536         {
8537           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8538           {
8539             can_clone = TRUE;
8540
8541             break;
8542           }
8543         }
8544
8545         // cannot clone or target field not free anymore -- do not clone
8546         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8547           Store[x][y] = EL_EMPTY;
8548       }
8549
8550       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8551       {
8552         if (IS_MV_DIAGONAL(MovDir[x][y]))
8553         {
8554           int diagonal_move_dir = MovDir[x][y];
8555           int stored = Store[x][y];
8556           int change_delay = 8;
8557           int graphic;
8558
8559           // android is moving diagonally
8560
8561           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8562
8563           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8564           GfxElement[x][y] = EL_EMC_ANDROID;
8565           GfxAction[x][y] = ACTION_SHRINKING;
8566           GfxDir[x][y] = diagonal_move_dir;
8567           ChangeDelay[x][y] = change_delay;
8568
8569           if (Store[x][y] == EL_EMPTY)
8570             Store[x][y] = GfxElementEmpty[x][y];
8571
8572           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8573                                    GfxDir[x][y]);
8574
8575           DrawLevelGraphicAnimation(x, y, graphic);
8576           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8577
8578           if (Tile[newx][newy] == EL_ACID)
8579           {
8580             SplashAcid(newx, newy);
8581
8582             return;
8583           }
8584
8585           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8586
8587           Store[newx][newy] = EL_EMC_ANDROID;
8588           GfxElement[newx][newy] = EL_EMC_ANDROID;
8589           GfxAction[newx][newy] = ACTION_GROWING;
8590           GfxDir[newx][newy] = diagonal_move_dir;
8591           ChangeDelay[newx][newy] = change_delay;
8592
8593           graphic = el_act_dir2img(GfxElement[newx][newy],
8594                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8595
8596           DrawLevelGraphicAnimation(newx, newy, graphic);
8597           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8598
8599           return;
8600         }
8601         else
8602         {
8603           Tile[newx][newy] = EL_EMPTY;
8604           TEST_DrawLevelField(newx, newy);
8605
8606           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8607         }
8608       }
8609       else if (!IS_FREE(newx, newy))
8610       {
8611         return;
8612       }
8613     }
8614     else if (IS_CUSTOM_ELEMENT(element) &&
8615              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8616     {
8617       if (!DigFieldByCE(newx, newy, element))
8618         return;
8619
8620       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8621       {
8622         RunnerVisit[x][y] = FrameCounter;
8623         PlayerVisit[x][y] /= 8;         // expire player visit path
8624       }
8625     }
8626     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8627     {
8628       if (!IS_FREE(newx, newy))
8629       {
8630         if (IS_PLAYER(x, y))
8631           DrawPlayerField(x, y);
8632         else
8633           TEST_DrawLevelField(x, y);
8634
8635         return;
8636       }
8637       else
8638       {
8639         boolean wanna_flame = !RND(10);
8640         int dx = newx - x, dy = newy - y;
8641         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8642         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8643         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8644                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8645         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8646                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8647
8648         if ((wanna_flame ||
8649              IS_CLASSIC_ENEMY(element1) ||
8650              IS_CLASSIC_ENEMY(element2)) &&
8651             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8652             element1 != EL_FLAMES && element2 != EL_FLAMES)
8653         {
8654           ResetGfxAnimation(x, y);
8655           GfxAction[x][y] = ACTION_ATTACKING;
8656
8657           if (IS_PLAYER(x, y))
8658             DrawPlayerField(x, y);
8659           else
8660             TEST_DrawLevelField(x, y);
8661
8662           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8663
8664           MovDelay[x][y] = 50;
8665
8666           Tile[newx][newy] = EL_FLAMES;
8667           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8668             Tile[newx1][newy1] = EL_FLAMES;
8669           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8670             Tile[newx2][newy2] = EL_FLAMES;
8671
8672           return;
8673         }
8674       }
8675     }
8676     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8677              Tile[newx][newy] == EL_DIAMOND)
8678     {
8679       if (IS_MOVING(newx, newy))
8680         RemoveMovingField(newx, newy);
8681       else
8682       {
8683         Tile[newx][newy] = EL_EMPTY;
8684         TEST_DrawLevelField(newx, newy);
8685       }
8686
8687       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8688     }
8689     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8690              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8691     {
8692       if (AmoebaNr[newx][newy])
8693       {
8694         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8695         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8696             Tile[newx][newy] == EL_BD_AMOEBA)
8697           AmoebaCnt[AmoebaNr[newx][newy]]--;
8698       }
8699
8700       if (IS_MOVING(newx, newy))
8701       {
8702         RemoveMovingField(newx, newy);
8703       }
8704       else
8705       {
8706         Tile[newx][newy] = EL_EMPTY;
8707         TEST_DrawLevelField(newx, newy);
8708       }
8709
8710       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8711     }
8712     else if ((element == EL_PACMAN || element == EL_MOLE)
8713              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(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 (element == EL_MOLE)
8724       {
8725         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8726         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8727
8728         ResetGfxAnimation(x, y);
8729         GfxAction[x][y] = ACTION_DIGGING;
8730         TEST_DrawLevelField(x, y);
8731
8732         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8733
8734         return;                         // wait for shrinking amoeba
8735       }
8736       else      // element == EL_PACMAN
8737       {
8738         Tile[newx][newy] = EL_EMPTY;
8739         TEST_DrawLevelField(newx, newy);
8740         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8741       }
8742     }
8743     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8744              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8745               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8746     {
8747       // wait for shrinking amoeba to completely disappear
8748       return;
8749     }
8750     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8751     {
8752       // object was running against a wall
8753
8754       TurnRound(x, y);
8755
8756       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8757         DrawLevelElementAnimation(x, y, element);
8758
8759       if (DONT_TOUCH(element))
8760         TestIfBadThingTouchesPlayer(x, y);
8761
8762       return;
8763     }
8764
8765     InitMovingField(x, y, MovDir[x][y]);
8766
8767     PlayLevelSoundAction(x, y, ACTION_MOVING);
8768   }
8769
8770   if (MovDir[x][y])
8771     ContinueMoving(x, y);
8772 }
8773
8774 void ContinueMoving(int x, int y)
8775 {
8776   int element = Tile[x][y];
8777   struct ElementInfo *ei = &element_info[element];
8778   int direction = MovDir[x][y];
8779   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8780   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8781   int newx = x + dx, newy = y + dy;
8782   int stored = Store[x][y];
8783   int stored_new = Store[newx][newy];
8784   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8785   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8786   boolean last_line = (newy == lev_fieldy - 1);
8787   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8788
8789   if (pushed_by_player)         // special case: moving object pushed by player
8790   {
8791     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8792   }
8793   else if (use_step_delay)      // special case: moving object has step delay
8794   {
8795     if (!MovDelay[x][y])
8796       MovPos[x][y] += getElementMoveStepsize(x, y);
8797
8798     if (MovDelay[x][y])
8799       MovDelay[x][y]--;
8800     else
8801       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8802
8803     if (MovDelay[x][y])
8804     {
8805       TEST_DrawLevelField(x, y);
8806
8807       return;   // element is still waiting
8808     }
8809   }
8810   else                          // normal case: generically moving object
8811   {
8812     MovPos[x][y] += getElementMoveStepsize(x, y);
8813   }
8814
8815   if (ABS(MovPos[x][y]) < TILEX)
8816   {
8817     TEST_DrawLevelField(x, y);
8818
8819     return;     // element is still moving
8820   }
8821
8822   // element reached destination field
8823
8824   Tile[x][y] = EL_EMPTY;
8825   Tile[newx][newy] = element;
8826   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8827
8828   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8829   {
8830     element = Tile[newx][newy] = EL_ACID;
8831   }
8832   else if (element == EL_MOLE)
8833   {
8834     Tile[x][y] = EL_SAND;
8835
8836     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8837   }
8838   else if (element == EL_QUICKSAND_FILLING)
8839   {
8840     element = Tile[newx][newy] = get_next_element(element);
8841     Store[newx][newy] = Store[x][y];
8842   }
8843   else if (element == EL_QUICKSAND_EMPTYING)
8844   {
8845     Tile[x][y] = get_next_element(element);
8846     element = Tile[newx][newy] = Store[x][y];
8847   }
8848   else if (element == EL_QUICKSAND_FAST_FILLING)
8849   {
8850     element = Tile[newx][newy] = get_next_element(element);
8851     Store[newx][newy] = Store[x][y];
8852   }
8853   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8854   {
8855     Tile[x][y] = get_next_element(element);
8856     element = Tile[newx][newy] = Store[x][y];
8857   }
8858   else if (element == EL_MAGIC_WALL_FILLING)
8859   {
8860     element = Tile[newx][newy] = get_next_element(element);
8861     if (!game.magic_wall_active)
8862       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8863     Store[newx][newy] = Store[x][y];
8864   }
8865   else if (element == EL_MAGIC_WALL_EMPTYING)
8866   {
8867     Tile[x][y] = get_next_element(element);
8868     if (!game.magic_wall_active)
8869       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8870     element = Tile[newx][newy] = Store[x][y];
8871
8872     InitField(newx, newy, FALSE);
8873   }
8874   else if (element == EL_BD_MAGIC_WALL_FILLING)
8875   {
8876     element = Tile[newx][newy] = get_next_element(element);
8877     if (!game.magic_wall_active)
8878       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8879     Store[newx][newy] = Store[x][y];
8880   }
8881   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8882   {
8883     Tile[x][y] = get_next_element(element);
8884     if (!game.magic_wall_active)
8885       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8886     element = Tile[newx][newy] = Store[x][y];
8887
8888     InitField(newx, newy, FALSE);
8889   }
8890   else if (element == EL_DC_MAGIC_WALL_FILLING)
8891   {
8892     element = Tile[newx][newy] = get_next_element(element);
8893     if (!game.magic_wall_active)
8894       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8895     Store[newx][newy] = Store[x][y];
8896   }
8897   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8898   {
8899     Tile[x][y] = get_next_element(element);
8900     if (!game.magic_wall_active)
8901       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8902     element = Tile[newx][newy] = Store[x][y];
8903
8904     InitField(newx, newy, FALSE);
8905   }
8906   else if (element == EL_AMOEBA_DROPPING)
8907   {
8908     Tile[x][y] = get_next_element(element);
8909     element = Tile[newx][newy] = Store[x][y];
8910   }
8911   else if (element == EL_SOKOBAN_OBJECT)
8912   {
8913     if (Back[x][y])
8914       Tile[x][y] = Back[x][y];
8915
8916     if (Back[newx][newy])
8917       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8918
8919     Back[x][y] = Back[newx][newy] = 0;
8920   }
8921
8922   Store[x][y] = EL_EMPTY;
8923   MovPos[x][y] = 0;
8924   MovDir[x][y] = 0;
8925   MovDelay[x][y] = 0;
8926
8927   MovDelay[newx][newy] = 0;
8928
8929   if (CAN_CHANGE_OR_HAS_ACTION(element))
8930   {
8931     // copy element change control values to new field
8932     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8933     ChangePage[newx][newy]  = ChangePage[x][y];
8934     ChangeCount[newx][newy] = ChangeCount[x][y];
8935     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8936   }
8937
8938   CustomValue[newx][newy] = CustomValue[x][y];
8939
8940   ChangeDelay[x][y] = 0;
8941   ChangePage[x][y] = -1;
8942   ChangeCount[x][y] = 0;
8943   ChangeEvent[x][y] = -1;
8944
8945   CustomValue[x][y] = 0;
8946
8947   // copy animation control values to new field
8948   GfxFrame[newx][newy]  = GfxFrame[x][y];
8949   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8950   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8951   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8952
8953   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8954
8955   // some elements can leave other elements behind after moving
8956   if (ei->move_leave_element != EL_EMPTY &&
8957       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8958       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8959   {
8960     int move_leave_element = ei->move_leave_element;
8961
8962     // this makes it possible to leave the removed element again
8963     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8964       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8965
8966     Tile[x][y] = move_leave_element;
8967
8968     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8969       MovDir[x][y] = direction;
8970
8971     InitField(x, y, FALSE);
8972
8973     if (GFX_CRUMBLED(Tile[x][y]))
8974       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8975
8976     if (IS_PLAYER_ELEMENT(move_leave_element))
8977       RelocatePlayer(x, y, move_leave_element);
8978   }
8979
8980   // do this after checking for left-behind element
8981   ResetGfxAnimation(x, y);      // reset animation values for old field
8982
8983   if (!CAN_MOVE(element) ||
8984       (CAN_FALL(element) && direction == MV_DOWN &&
8985        (element == EL_SPRING ||
8986         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8987         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8988     GfxDir[x][y] = MovDir[newx][newy] = 0;
8989
8990   TEST_DrawLevelField(x, y);
8991   TEST_DrawLevelField(newx, newy);
8992
8993   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8994
8995   // prevent pushed element from moving on in pushed direction
8996   if (pushed_by_player && CAN_MOVE(element) &&
8997       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8998       !(element_info[element].move_pattern & direction))
8999     TurnRound(newx, newy);
9000
9001   // prevent elements on conveyor belt from moving on in last direction
9002   if (pushed_by_conveyor && CAN_FALL(element) &&
9003       direction & MV_HORIZONTAL)
9004     MovDir[newx][newy] = 0;
9005
9006   if (!pushed_by_player)
9007   {
9008     int nextx = newx + dx, nexty = newy + dy;
9009     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9010
9011     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9012
9013     if (CAN_FALL(element) && direction == MV_DOWN)
9014       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9015
9016     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9017       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9018
9019     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9020       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9021   }
9022
9023   if (DONT_TOUCH(element))      // object may be nasty to player or others
9024   {
9025     TestIfBadThingTouchesPlayer(newx, newy);
9026     TestIfBadThingTouchesFriend(newx, newy);
9027
9028     if (!IS_CUSTOM_ELEMENT(element))
9029       TestIfBadThingTouchesOtherBadThing(newx, newy);
9030   }
9031   else if (element == EL_PENGUIN)
9032     TestIfFriendTouchesBadThing(newx, newy);
9033
9034   if (DONT_GET_HIT_BY(element))
9035   {
9036     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9037   }
9038
9039   // give the player one last chance (one more frame) to move away
9040   if (CAN_FALL(element) && direction == MV_DOWN &&
9041       (last_line || (!IS_FREE(x, newy + 1) &&
9042                      (!IS_PLAYER(x, newy + 1) ||
9043                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9044     Impact(x, newy);
9045
9046   if (pushed_by_player && !game.use_change_when_pushing_bug)
9047   {
9048     int push_side = MV_DIR_OPPOSITE(direction);
9049     struct PlayerInfo *player = PLAYERINFO(x, y);
9050
9051     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9052                                player->index_bit, push_side);
9053     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9054                                         player->index_bit, push_side);
9055   }
9056
9057   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9058     MovDelay[newx][newy] = 1;
9059
9060   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9061
9062   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9063   TestIfElementHitsCustomElement(newx, newy, direction);
9064   TestIfPlayerTouchesCustomElement(newx, newy);
9065   TestIfElementTouchesCustomElement(newx, newy);
9066
9067   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9068       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9069     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9070                              MV_DIR_OPPOSITE(direction));
9071 }
9072
9073 int AmoebaNeighbourNr(int ax, int ay)
9074 {
9075   int i;
9076   int element = Tile[ax][ay];
9077   int group_nr = 0;
9078   struct XY *xy = xy_topdown;
9079
9080   for (i = 0; i < NUM_DIRECTIONS; i++)
9081   {
9082     int x = ax + xy[i].x;
9083     int y = ay + xy[i].y;
9084
9085     if (!IN_LEV_FIELD(x, y))
9086       continue;
9087
9088     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9089       group_nr = AmoebaNr[x][y];
9090   }
9091
9092   return group_nr;
9093 }
9094
9095 static void AmoebaMerge(int ax, int ay)
9096 {
9097   int i, x, y, xx, yy;
9098   int new_group_nr = AmoebaNr[ax][ay];
9099   struct XY *xy = xy_topdown;
9100
9101   if (new_group_nr == 0)
9102     return;
9103
9104   for (i = 0; i < NUM_DIRECTIONS; i++)
9105   {
9106     x = ax + xy[i].x;
9107     y = ay + xy[i].y;
9108
9109     if (!IN_LEV_FIELD(x, y))
9110       continue;
9111
9112     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9113          Tile[x][y] == EL_BD_AMOEBA ||
9114          Tile[x][y] == EL_AMOEBA_DEAD) &&
9115         AmoebaNr[x][y] != new_group_nr)
9116     {
9117       int old_group_nr = AmoebaNr[x][y];
9118
9119       if (old_group_nr == 0)
9120         return;
9121
9122       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9123       AmoebaCnt[old_group_nr] = 0;
9124       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9125       AmoebaCnt2[old_group_nr] = 0;
9126
9127       SCAN_PLAYFIELD(xx, yy)
9128       {
9129         if (AmoebaNr[xx][yy] == old_group_nr)
9130           AmoebaNr[xx][yy] = new_group_nr;
9131       }
9132     }
9133   }
9134 }
9135
9136 void AmoebaToDiamond(int ax, int ay)
9137 {
9138   int i, x, y;
9139
9140   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9141   {
9142     int group_nr = AmoebaNr[ax][ay];
9143
9144 #ifdef DEBUG
9145     if (group_nr == 0)
9146     {
9147       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9148       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9149
9150       return;
9151     }
9152 #endif
9153
9154     SCAN_PLAYFIELD(x, y)
9155     {
9156       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9157       {
9158         AmoebaNr[x][y] = 0;
9159         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9160       }
9161     }
9162
9163     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9164                             SND_AMOEBA_TURNING_TO_GEM :
9165                             SND_AMOEBA_TURNING_TO_ROCK));
9166     Bang(ax, ay);
9167   }
9168   else
9169   {
9170     struct XY *xy = xy_topdown;
9171
9172     for (i = 0; i < NUM_DIRECTIONS; i++)
9173     {
9174       x = ax + xy[i].x;
9175       y = ay + xy[i].y;
9176
9177       if (!IN_LEV_FIELD(x, y))
9178         continue;
9179
9180       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9181       {
9182         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9183                               SND_AMOEBA_TURNING_TO_GEM :
9184                               SND_AMOEBA_TURNING_TO_ROCK));
9185         Bang(x, y);
9186       }
9187     }
9188   }
9189 }
9190
9191 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9192 {
9193   int x, y;
9194   int group_nr = AmoebaNr[ax][ay];
9195   boolean done = FALSE;
9196
9197 #ifdef DEBUG
9198   if (group_nr == 0)
9199   {
9200     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9201     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9202
9203     return;
9204   }
9205 #endif
9206
9207   SCAN_PLAYFIELD(x, y)
9208   {
9209     if (AmoebaNr[x][y] == group_nr &&
9210         (Tile[x][y] == EL_AMOEBA_DEAD ||
9211          Tile[x][y] == EL_BD_AMOEBA ||
9212          Tile[x][y] == EL_AMOEBA_GROWING))
9213     {
9214       AmoebaNr[x][y] = 0;
9215       Tile[x][y] = new_element;
9216       InitField(x, y, FALSE);
9217       TEST_DrawLevelField(x, y);
9218       done = TRUE;
9219     }
9220   }
9221
9222   if (done)
9223     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9224                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9225                             SND_BD_AMOEBA_TURNING_TO_GEM));
9226 }
9227
9228 static void AmoebaGrowing(int x, int y)
9229 {
9230   static DelayCounter sound_delay = { 0 };
9231
9232   if (!MovDelay[x][y])          // start new growing cycle
9233   {
9234     MovDelay[x][y] = 7;
9235
9236     if (DelayReached(&sound_delay))
9237     {
9238       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9239       sound_delay.value = 30;
9240     }
9241   }
9242
9243   if (MovDelay[x][y])           // wait some time before growing bigger
9244   {
9245     MovDelay[x][y]--;
9246     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9247     {
9248       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9249                                            6 - MovDelay[x][y]);
9250
9251       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9252     }
9253
9254     if (!MovDelay[x][y])
9255     {
9256       Tile[x][y] = Store[x][y];
9257       Store[x][y] = 0;
9258       TEST_DrawLevelField(x, y);
9259     }
9260   }
9261 }
9262
9263 static void AmoebaShrinking(int x, int y)
9264 {
9265   static DelayCounter sound_delay = { 0 };
9266
9267   if (!MovDelay[x][y])          // start new shrinking cycle
9268   {
9269     MovDelay[x][y] = 7;
9270
9271     if (DelayReached(&sound_delay))
9272       sound_delay.value = 30;
9273   }
9274
9275   if (MovDelay[x][y])           // wait some time before shrinking
9276   {
9277     MovDelay[x][y]--;
9278     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9279     {
9280       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9281                                            6 - MovDelay[x][y]);
9282
9283       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9284     }
9285
9286     if (!MovDelay[x][y])
9287     {
9288       Tile[x][y] = EL_EMPTY;
9289       TEST_DrawLevelField(x, y);
9290
9291       // don't let mole enter this field in this cycle;
9292       // (give priority to objects falling to this field from above)
9293       Stop[x][y] = TRUE;
9294     }
9295   }
9296 }
9297
9298 static void AmoebaReproduce(int ax, int ay)
9299 {
9300   int i;
9301   int element = Tile[ax][ay];
9302   int graphic = el2img(element);
9303   int newax = ax, neway = ay;
9304   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9305   struct XY *xy = xy_topdown;
9306
9307   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9308   {
9309     Tile[ax][ay] = EL_AMOEBA_DEAD;
9310     TEST_DrawLevelField(ax, ay);
9311     return;
9312   }
9313
9314   if (IS_ANIMATED(graphic))
9315     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9316
9317   if (!MovDelay[ax][ay])        // start making new amoeba field
9318     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9319
9320   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9321   {
9322     MovDelay[ax][ay]--;
9323     if (MovDelay[ax][ay])
9324       return;
9325   }
9326
9327   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9328   {
9329     int start = RND(4);
9330     int x = ax + xy[start].x;
9331     int y = ay + xy[start].y;
9332
9333     if (!IN_LEV_FIELD(x, y))
9334       return;
9335
9336     if (IS_FREE(x, y) ||
9337         CAN_GROW_INTO(Tile[x][y]) ||
9338         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9339         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9340     {
9341       newax = x;
9342       neway = y;
9343     }
9344
9345     if (newax == ax && neway == ay)
9346       return;
9347   }
9348   else                          // normal or "filled" (BD style) amoeba
9349   {
9350     int start = RND(4);
9351     boolean waiting_for_player = FALSE;
9352
9353     for (i = 0; i < NUM_DIRECTIONS; i++)
9354     {
9355       int j = (start + i) % 4;
9356       int x = ax + xy[j].x;
9357       int y = ay + xy[j].y;
9358
9359       if (!IN_LEV_FIELD(x, y))
9360         continue;
9361
9362       if (IS_FREE(x, y) ||
9363           CAN_GROW_INTO(Tile[x][y]) ||
9364           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9365           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9366       {
9367         newax = x;
9368         neway = y;
9369         break;
9370       }
9371       else if (IS_PLAYER(x, y))
9372         waiting_for_player = TRUE;
9373     }
9374
9375     if (newax == ax && neway == ay)             // amoeba cannot grow
9376     {
9377       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9378       {
9379         Tile[ax][ay] = EL_AMOEBA_DEAD;
9380         TEST_DrawLevelField(ax, ay);
9381         AmoebaCnt[AmoebaNr[ax][ay]]--;
9382
9383         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9384         {
9385           if (element == EL_AMOEBA_FULL)
9386             AmoebaToDiamond(ax, ay);
9387           else if (element == EL_BD_AMOEBA)
9388             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9389         }
9390       }
9391       return;
9392     }
9393     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9394     {
9395       // amoeba gets larger by growing in some direction
9396
9397       int new_group_nr = AmoebaNr[ax][ay];
9398
9399 #ifdef DEBUG
9400   if (new_group_nr == 0)
9401   {
9402     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9403           newax, neway);
9404     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9405
9406     return;
9407   }
9408 #endif
9409
9410       AmoebaNr[newax][neway] = new_group_nr;
9411       AmoebaCnt[new_group_nr]++;
9412       AmoebaCnt2[new_group_nr]++;
9413
9414       // if amoeba touches other amoeba(s) after growing, unify them
9415       AmoebaMerge(newax, neway);
9416
9417       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9418       {
9419         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9420         return;
9421       }
9422     }
9423   }
9424
9425   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9426       (neway == lev_fieldy - 1 && newax != ax))
9427   {
9428     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9429     Store[newax][neway] = element;
9430   }
9431   else if (neway == ay || element == EL_EMC_DRIPPER)
9432   {
9433     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9434
9435     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9436   }
9437   else
9438   {
9439     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9440     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9441     Store[ax][ay] = EL_AMOEBA_DROP;
9442     ContinueMoving(ax, ay);
9443     return;
9444   }
9445
9446   TEST_DrawLevelField(newax, neway);
9447 }
9448
9449 static void Life(int ax, int ay)
9450 {
9451   int x1, y1, x2, y2;
9452   int life_time = 40;
9453   int element = Tile[ax][ay];
9454   int graphic = el2img(element);
9455   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9456                          level.biomaze);
9457   boolean changed = FALSE;
9458
9459   if (IS_ANIMATED(graphic))
9460     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9461
9462   if (Stop[ax][ay])
9463     return;
9464
9465   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9466     MovDelay[ax][ay] = life_time;
9467
9468   if (MovDelay[ax][ay])         // wait some time before next cycle
9469   {
9470     MovDelay[ax][ay]--;
9471     if (MovDelay[ax][ay])
9472       return;
9473   }
9474
9475   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9476   {
9477     int xx = ax + x1, yy = ay + y1;
9478     int old_element = Tile[xx][yy];
9479     int num_neighbours = 0;
9480
9481     if (!IN_LEV_FIELD(xx, yy))
9482       continue;
9483
9484     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9485     {
9486       int x = xx + x2, y = yy + y2;
9487
9488       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9489         continue;
9490
9491       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9492       boolean is_neighbour = FALSE;
9493
9494       if (level.use_life_bugs)
9495         is_neighbour =
9496           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9497            (IS_FREE(x, y)                             &&  Stop[x][y]));
9498       else
9499         is_neighbour =
9500           (Last[x][y] == element || is_player_cell);
9501
9502       if (is_neighbour)
9503         num_neighbours++;
9504     }
9505
9506     boolean is_free = FALSE;
9507
9508     if (level.use_life_bugs)
9509       is_free = (IS_FREE(xx, yy));
9510     else
9511       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9512
9513     if (xx == ax && yy == ay)           // field in the middle
9514     {
9515       if (num_neighbours < life_parameter[0] ||
9516           num_neighbours > life_parameter[1])
9517       {
9518         Tile[xx][yy] = EL_EMPTY;
9519         if (Tile[xx][yy] != old_element)
9520           TEST_DrawLevelField(xx, yy);
9521         Stop[xx][yy] = TRUE;
9522         changed = TRUE;
9523       }
9524     }
9525     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9526     {                                   // free border field
9527       if (num_neighbours >= life_parameter[2] &&
9528           num_neighbours <= life_parameter[3])
9529       {
9530         Tile[xx][yy] = element;
9531         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9532         if (Tile[xx][yy] != old_element)
9533           TEST_DrawLevelField(xx, yy);
9534         Stop[xx][yy] = TRUE;
9535         changed = TRUE;
9536       }
9537     }
9538   }
9539
9540   if (changed)
9541     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9542                    SND_GAME_OF_LIFE_GROWING);
9543 }
9544
9545 static void InitRobotWheel(int x, int y)
9546 {
9547   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9548 }
9549
9550 static void RunRobotWheel(int x, int y)
9551 {
9552   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9553 }
9554
9555 static void StopRobotWheel(int x, int y)
9556 {
9557   if (game.robot_wheel_x == x &&
9558       game.robot_wheel_y == y)
9559   {
9560     game.robot_wheel_x = -1;
9561     game.robot_wheel_y = -1;
9562     game.robot_wheel_active = FALSE;
9563   }
9564 }
9565
9566 static void InitTimegateWheel(int x, int y)
9567 {
9568   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9569 }
9570
9571 static void RunTimegateWheel(int x, int y)
9572 {
9573   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9574 }
9575
9576 static void InitMagicBallDelay(int x, int y)
9577 {
9578   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9579 }
9580
9581 static void ActivateMagicBall(int bx, int by)
9582 {
9583   int x, y;
9584
9585   if (level.ball_random)
9586   {
9587     int pos_border = RND(8);    // select one of the eight border elements
9588     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9589     int xx = pos_content % 3;
9590     int yy = pos_content / 3;
9591
9592     x = bx - 1 + xx;
9593     y = by - 1 + yy;
9594
9595     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9596       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9597   }
9598   else
9599   {
9600     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9601     {
9602       int xx = x - bx + 1;
9603       int yy = y - by + 1;
9604
9605       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9606         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9607     }
9608   }
9609
9610   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9611 }
9612
9613 static void CheckExit(int x, int y)
9614 {
9615   if (game.gems_still_needed > 0 ||
9616       game.sokoban_fields_still_needed > 0 ||
9617       game.sokoban_objects_still_needed > 0 ||
9618       game.lights_still_needed > 0)
9619   {
9620     int element = Tile[x][y];
9621     int graphic = el2img(element);
9622
9623     if (IS_ANIMATED(graphic))
9624       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9625
9626     return;
9627   }
9628
9629   // do not re-open exit door closed after last player
9630   if (game.all_players_gone)
9631     return;
9632
9633   Tile[x][y] = EL_EXIT_OPENING;
9634
9635   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9636 }
9637
9638 static void CheckExitEM(int x, int y)
9639 {
9640   if (game.gems_still_needed > 0 ||
9641       game.sokoban_fields_still_needed > 0 ||
9642       game.sokoban_objects_still_needed > 0 ||
9643       game.lights_still_needed > 0)
9644   {
9645     int element = Tile[x][y];
9646     int graphic = el2img(element);
9647
9648     if (IS_ANIMATED(graphic))
9649       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9650
9651     return;
9652   }
9653
9654   // do not re-open exit door closed after last player
9655   if (game.all_players_gone)
9656     return;
9657
9658   Tile[x][y] = EL_EM_EXIT_OPENING;
9659
9660   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9661 }
9662
9663 static void CheckExitSteel(int x, int y)
9664 {
9665   if (game.gems_still_needed > 0 ||
9666       game.sokoban_fields_still_needed > 0 ||
9667       game.sokoban_objects_still_needed > 0 ||
9668       game.lights_still_needed > 0)
9669   {
9670     int element = Tile[x][y];
9671     int graphic = el2img(element);
9672
9673     if (IS_ANIMATED(graphic))
9674       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9675
9676     return;
9677   }
9678
9679   // do not re-open exit door closed after last player
9680   if (game.all_players_gone)
9681     return;
9682
9683   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9684
9685   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9686 }
9687
9688 static void CheckExitSteelEM(int x, int y)
9689 {
9690   if (game.gems_still_needed > 0 ||
9691       game.sokoban_fields_still_needed > 0 ||
9692       game.sokoban_objects_still_needed > 0 ||
9693       game.lights_still_needed > 0)
9694   {
9695     int element = Tile[x][y];
9696     int graphic = el2img(element);
9697
9698     if (IS_ANIMATED(graphic))
9699       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9700
9701     return;
9702   }
9703
9704   // do not re-open exit door closed after last player
9705   if (game.all_players_gone)
9706     return;
9707
9708   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9709
9710   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9711 }
9712
9713 static void CheckExitSP(int x, int y)
9714 {
9715   if (game.gems_still_needed > 0)
9716   {
9717     int element = Tile[x][y];
9718     int graphic = el2img(element);
9719
9720     if (IS_ANIMATED(graphic))
9721       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9722
9723     return;
9724   }
9725
9726   // do not re-open exit door closed after last player
9727   if (game.all_players_gone)
9728     return;
9729
9730   Tile[x][y] = EL_SP_EXIT_OPENING;
9731
9732   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9733 }
9734
9735 static void CloseAllOpenTimegates(void)
9736 {
9737   int x, y;
9738
9739   SCAN_PLAYFIELD(x, y)
9740   {
9741     int element = Tile[x][y];
9742
9743     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9744     {
9745       Tile[x][y] = EL_TIMEGATE_CLOSING;
9746
9747       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9748     }
9749   }
9750 }
9751
9752 static void DrawTwinkleOnField(int x, int y)
9753 {
9754   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9755     return;
9756
9757   if (Tile[x][y] == EL_BD_DIAMOND)
9758     return;
9759
9760   if (MovDelay[x][y] == 0)      // next animation frame
9761     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9762
9763   if (MovDelay[x][y] != 0)      // wait some time before next frame
9764   {
9765     MovDelay[x][y]--;
9766
9767     DrawLevelElementAnimation(x, y, Tile[x][y]);
9768
9769     if (MovDelay[x][y] != 0)
9770     {
9771       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9772                                            10 - MovDelay[x][y]);
9773
9774       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9775     }
9776   }
9777 }
9778
9779 static void WallGrowing(int x, int y)
9780 {
9781   int delay = 6;
9782
9783   if (!MovDelay[x][y])          // next animation frame
9784     MovDelay[x][y] = 3 * delay;
9785
9786   if (MovDelay[x][y])           // wait some time before next frame
9787   {
9788     MovDelay[x][y]--;
9789
9790     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9791     {
9792       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9793       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9794
9795       DrawLevelGraphic(x, y, graphic, frame);
9796     }
9797
9798     if (!MovDelay[x][y])
9799     {
9800       if (MovDir[x][y] == MV_LEFT)
9801       {
9802         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9803           TEST_DrawLevelField(x - 1, y);
9804       }
9805       else if (MovDir[x][y] == MV_RIGHT)
9806       {
9807         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9808           TEST_DrawLevelField(x + 1, y);
9809       }
9810       else if (MovDir[x][y] == MV_UP)
9811       {
9812         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9813           TEST_DrawLevelField(x, y - 1);
9814       }
9815       else
9816       {
9817         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9818           TEST_DrawLevelField(x, y + 1);
9819       }
9820
9821       Tile[x][y] = Store[x][y];
9822       Store[x][y] = 0;
9823       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9824       TEST_DrawLevelField(x, y);
9825     }
9826   }
9827 }
9828
9829 static void CheckWallGrowing(int ax, int ay)
9830 {
9831   int element = Tile[ax][ay];
9832   int graphic = el2img(element);
9833   boolean free_top    = FALSE;
9834   boolean free_bottom = FALSE;
9835   boolean free_left   = FALSE;
9836   boolean free_right  = FALSE;
9837   boolean stop_top    = FALSE;
9838   boolean stop_bottom = FALSE;
9839   boolean stop_left   = FALSE;
9840   boolean stop_right  = FALSE;
9841   boolean new_wall    = FALSE;
9842
9843   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9844                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9845                            element == EL_EXPANDABLE_STEELWALL_ANY);
9846
9847   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9848                              element == EL_EXPANDABLE_WALL_ANY ||
9849                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9850                              element == EL_EXPANDABLE_STEELWALL_ANY);
9851
9852   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9853                              element == EL_EXPANDABLE_WALL_ANY ||
9854                              element == EL_EXPANDABLE_WALL ||
9855                              element == EL_BD_EXPANDABLE_WALL ||
9856                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9857                              element == EL_EXPANDABLE_STEELWALL_ANY);
9858
9859   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9860                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9861
9862   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9863                              element == EL_EXPANDABLE_WALL ||
9864                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9865
9866   int wall_growing = (is_steelwall ?
9867                       EL_EXPANDABLE_STEELWALL_GROWING :
9868                       EL_EXPANDABLE_WALL_GROWING);
9869
9870   int gfx_wall_growing_up    = (is_steelwall ?
9871                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9872                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9873   int gfx_wall_growing_down  = (is_steelwall ?
9874                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9875                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9876   int gfx_wall_growing_left  = (is_steelwall ?
9877                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9878                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9879   int gfx_wall_growing_right = (is_steelwall ?
9880                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9881                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9882
9883   if (IS_ANIMATED(graphic))
9884     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9885
9886   if (!MovDelay[ax][ay])        // start building new wall
9887     MovDelay[ax][ay] = 6;
9888
9889   if (MovDelay[ax][ay])         // wait some time before building new wall
9890   {
9891     MovDelay[ax][ay]--;
9892     if (MovDelay[ax][ay])
9893       return;
9894   }
9895
9896   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9897     free_top = TRUE;
9898   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9899     free_bottom = TRUE;
9900   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9901     free_left = TRUE;
9902   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9903     free_right = TRUE;
9904
9905   if (grow_vertical)
9906   {
9907     if (free_top)
9908     {
9909       Tile[ax][ay - 1] = wall_growing;
9910       Store[ax][ay - 1] = element;
9911       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9912
9913       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9914         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9915
9916       new_wall = TRUE;
9917     }
9918
9919     if (free_bottom)
9920     {
9921       Tile[ax][ay + 1] = wall_growing;
9922       Store[ax][ay + 1] = element;
9923       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9924
9925       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9926         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9927
9928       new_wall = TRUE;
9929     }
9930   }
9931
9932   if (grow_horizontal)
9933   {
9934     if (free_left)
9935     {
9936       Tile[ax - 1][ay] = wall_growing;
9937       Store[ax - 1][ay] = element;
9938       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9939
9940       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9941         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9942
9943       new_wall = TRUE;
9944     }
9945
9946     if (free_right)
9947     {
9948       Tile[ax + 1][ay] = wall_growing;
9949       Store[ax + 1][ay] = element;
9950       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9951
9952       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9953         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9954
9955       new_wall = TRUE;
9956     }
9957   }
9958
9959   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9960     TEST_DrawLevelField(ax, ay);
9961
9962   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9963     stop_top = TRUE;
9964   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9965     stop_bottom = TRUE;
9966   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9967     stop_left = TRUE;
9968   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9969     stop_right = TRUE;
9970
9971   if (((stop_top && stop_bottom) || stop_horizontal) &&
9972       ((stop_left && stop_right) || stop_vertical))
9973     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9974
9975   if (new_wall)
9976     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9977 }
9978
9979 static void CheckForDragon(int x, int y)
9980 {
9981   int i, j;
9982   boolean dragon_found = FALSE;
9983   struct XY *xy = xy_topdown;
9984
9985   for (i = 0; i < NUM_DIRECTIONS; i++)
9986   {
9987     for (j = 0; j < 4; j++)
9988     {
9989       int xx = x + j * xy[i].x;
9990       int yy = y + j * xy[i].y;
9991
9992       if (IN_LEV_FIELD(xx, yy) &&
9993           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9994       {
9995         if (Tile[xx][yy] == EL_DRAGON)
9996           dragon_found = TRUE;
9997       }
9998       else
9999         break;
10000     }
10001   }
10002
10003   if (!dragon_found)
10004   {
10005     for (i = 0; i < NUM_DIRECTIONS; i++)
10006     {
10007       for (j = 0; j < 3; j++)
10008       {
10009         int xx = x + j * xy[i].x;
10010         int yy = y + j * xy[i].y;
10011
10012         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10013         {
10014           Tile[xx][yy] = EL_EMPTY;
10015           TEST_DrawLevelField(xx, yy);
10016         }
10017         else
10018           break;
10019       }
10020     }
10021   }
10022 }
10023
10024 static void InitBuggyBase(int x, int y)
10025 {
10026   int element = Tile[x][y];
10027   int activating_delay = FRAMES_PER_SECOND / 4;
10028
10029   ChangeDelay[x][y] =
10030     (element == EL_SP_BUGGY_BASE ?
10031      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10032      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10033      activating_delay :
10034      element == EL_SP_BUGGY_BASE_ACTIVE ?
10035      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10036 }
10037
10038 static void WarnBuggyBase(int x, int y)
10039 {
10040   int i;
10041   struct XY *xy = xy_topdown;
10042
10043   for (i = 0; i < NUM_DIRECTIONS; i++)
10044   {
10045     int xx = x + xy[i].x;
10046     int yy = y + xy[i].y;
10047
10048     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10049     {
10050       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10051
10052       break;
10053     }
10054   }
10055 }
10056
10057 static void InitTrap(int x, int y)
10058 {
10059   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10060 }
10061
10062 static void ActivateTrap(int x, int y)
10063 {
10064   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10065 }
10066
10067 static void ChangeActiveTrap(int x, int y)
10068 {
10069   int graphic = IMG_TRAP_ACTIVE;
10070
10071   // if new animation frame was drawn, correct crumbled sand border
10072   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10073     TEST_DrawLevelFieldCrumbled(x, y);
10074 }
10075
10076 static int getSpecialActionElement(int element, int number, int base_element)
10077 {
10078   return (element != EL_EMPTY ? element :
10079           number != -1 ? base_element + number - 1 :
10080           EL_EMPTY);
10081 }
10082
10083 static int getModifiedActionNumber(int value_old, int operator, int operand,
10084                                    int value_min, int value_max)
10085 {
10086   int value_new = (operator == CA_MODE_SET      ? operand :
10087                    operator == CA_MODE_ADD      ? value_old + operand :
10088                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10089                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10090                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10091                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10092                    value_old);
10093
10094   return (value_new < value_min ? value_min :
10095           value_new > value_max ? value_max :
10096           value_new);
10097 }
10098
10099 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10100 {
10101   struct ElementInfo *ei = &element_info[element];
10102   struct ElementChangeInfo *change = &ei->change_page[page];
10103   int target_element = change->target_element;
10104   int action_type = change->action_type;
10105   int action_mode = change->action_mode;
10106   int action_arg = change->action_arg;
10107   int action_element = change->action_element;
10108   int i;
10109
10110   if (!change->has_action)
10111     return;
10112
10113   // ---------- determine action paramater values -----------------------------
10114
10115   int level_time_value =
10116     (level.time > 0 ? TimeLeft :
10117      TimePlayed);
10118
10119   int action_arg_element_raw =
10120     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10121      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10122      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10123      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10124      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10125      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10126      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10127      EL_EMPTY);
10128   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10129
10130   int action_arg_direction =
10131     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10132      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10133      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10134      change->actual_trigger_side :
10135      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10136      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10137      MV_NONE);
10138
10139   int action_arg_number_min =
10140     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10141      CA_ARG_MIN);
10142
10143   int action_arg_number_max =
10144     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10145      action_type == CA_SET_LEVEL_GEMS ? 999 :
10146      action_type == CA_SET_LEVEL_TIME ? 9999 :
10147      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10148      action_type == CA_SET_CE_VALUE ? 9999 :
10149      action_type == CA_SET_CE_SCORE ? 9999 :
10150      CA_ARG_MAX);
10151
10152   int action_arg_number_reset =
10153     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10154      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10155      action_type == CA_SET_LEVEL_TIME ? level.time :
10156      action_type == CA_SET_LEVEL_SCORE ? 0 :
10157      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10158      action_type == CA_SET_CE_SCORE ? 0 :
10159      0);
10160
10161   int action_arg_number =
10162     (action_arg <= CA_ARG_MAX ? action_arg :
10163      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10164      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10165      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10166      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10167      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10168      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10169      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10170      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10171      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10172      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10173      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10174      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10175      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10176      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10177      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10178      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10179      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10180      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10181      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10182      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10183      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10184      -1);
10185
10186   int action_arg_number_old =
10187     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10188      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10189      action_type == CA_SET_LEVEL_SCORE ? game.score :
10190      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10191      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10192      0);
10193
10194   int action_arg_number_new =
10195     getModifiedActionNumber(action_arg_number_old,
10196                             action_mode, action_arg_number,
10197                             action_arg_number_min, action_arg_number_max);
10198
10199   int trigger_player_bits =
10200     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10201      change->actual_trigger_player_bits : change->trigger_player);
10202
10203   int action_arg_player_bits =
10204     (action_arg >= CA_ARG_PLAYER_1 &&
10205      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10206      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10207      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10208      PLAYER_BITS_ANY);
10209
10210   // ---------- execute action  -----------------------------------------------
10211
10212   switch (action_type)
10213   {
10214     case CA_NO_ACTION:
10215     {
10216       return;
10217     }
10218
10219     // ---------- level actions  ----------------------------------------------
10220
10221     case CA_RESTART_LEVEL:
10222     {
10223       game.restart_level = TRUE;
10224
10225       break;
10226     }
10227
10228     case CA_SHOW_ENVELOPE:
10229     {
10230       int element = getSpecialActionElement(action_arg_element,
10231                                             action_arg_number, EL_ENVELOPE_1);
10232
10233       if (IS_ENVELOPE(element))
10234         local_player->show_envelope = element;
10235
10236       break;
10237     }
10238
10239     case CA_SET_LEVEL_TIME:
10240     {
10241       if (level.time > 0)       // only modify limited time value
10242       {
10243         TimeLeft = action_arg_number_new;
10244
10245         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10246
10247         DisplayGameControlValues();
10248
10249         if (!TimeLeft && game.time_limit)
10250           for (i = 0; i < MAX_PLAYERS; i++)
10251             KillPlayer(&stored_player[i]);
10252       }
10253
10254       break;
10255     }
10256
10257     case CA_SET_LEVEL_SCORE:
10258     {
10259       game.score = action_arg_number_new;
10260
10261       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10262
10263       DisplayGameControlValues();
10264
10265       break;
10266     }
10267
10268     case CA_SET_LEVEL_GEMS:
10269     {
10270       game.gems_still_needed = action_arg_number_new;
10271
10272       game.snapshot.collected_item = TRUE;
10273
10274       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10275
10276       DisplayGameControlValues();
10277
10278       break;
10279     }
10280
10281     case CA_SET_LEVEL_WIND:
10282     {
10283       game.wind_direction = action_arg_direction;
10284
10285       break;
10286     }
10287
10288     case CA_SET_LEVEL_RANDOM_SEED:
10289     {
10290       // ensure that setting a new random seed while playing is predictable
10291       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10292
10293       break;
10294     }
10295
10296     // ---------- player actions  ---------------------------------------------
10297
10298     case CA_MOVE_PLAYER:
10299     case CA_MOVE_PLAYER_NEW:
10300     {
10301       // automatically move to the next field in specified direction
10302       for (i = 0; i < MAX_PLAYERS; i++)
10303         if (trigger_player_bits & (1 << i))
10304           if (action_type == CA_MOVE_PLAYER ||
10305               stored_player[i].MovPos == 0)
10306             stored_player[i].programmed_action = action_arg_direction;
10307
10308       break;
10309     }
10310
10311     case CA_EXIT_PLAYER:
10312     {
10313       for (i = 0; i < MAX_PLAYERS; i++)
10314         if (action_arg_player_bits & (1 << i))
10315           ExitPlayer(&stored_player[i]);
10316
10317       if (game.players_still_needed == 0)
10318         LevelSolved();
10319
10320       break;
10321     }
10322
10323     case CA_KILL_PLAYER:
10324     {
10325       for (i = 0; i < MAX_PLAYERS; i++)
10326         if (action_arg_player_bits & (1 << i))
10327           KillPlayer(&stored_player[i]);
10328
10329       break;
10330     }
10331
10332     case CA_SET_PLAYER_KEYS:
10333     {
10334       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10335       int element = getSpecialActionElement(action_arg_element,
10336                                             action_arg_number, EL_KEY_1);
10337
10338       if (IS_KEY(element))
10339       {
10340         for (i = 0; i < MAX_PLAYERS; i++)
10341         {
10342           if (trigger_player_bits & (1 << i))
10343           {
10344             stored_player[i].key[KEY_NR(element)] = key_state;
10345
10346             DrawGameDoorValues();
10347           }
10348         }
10349       }
10350
10351       break;
10352     }
10353
10354     case CA_SET_PLAYER_SPEED:
10355     {
10356       for (i = 0; i < MAX_PLAYERS; i++)
10357       {
10358         if (trigger_player_bits & (1 << i))
10359         {
10360           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10361
10362           if (action_arg == CA_ARG_SPEED_FASTER &&
10363               stored_player[i].cannot_move)
10364           {
10365             action_arg_number = STEPSIZE_VERY_SLOW;
10366           }
10367           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10368                    action_arg == CA_ARG_SPEED_FASTER)
10369           {
10370             action_arg_number = 2;
10371             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10372                            CA_MODE_MULTIPLY);
10373           }
10374           else if (action_arg == CA_ARG_NUMBER_RESET)
10375           {
10376             action_arg_number = level.initial_player_stepsize[i];
10377           }
10378
10379           move_stepsize =
10380             getModifiedActionNumber(move_stepsize,
10381                                     action_mode,
10382                                     action_arg_number,
10383                                     action_arg_number_min,
10384                                     action_arg_number_max);
10385
10386           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_PLAYER_SHIELD:
10394     {
10395       for (i = 0; i < MAX_PLAYERS; i++)
10396       {
10397         if (trigger_player_bits & (1 << i))
10398         {
10399           if (action_arg == CA_ARG_SHIELD_OFF)
10400           {
10401             stored_player[i].shield_normal_time_left = 0;
10402             stored_player[i].shield_deadly_time_left = 0;
10403           }
10404           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10405           {
10406             stored_player[i].shield_normal_time_left = 999999;
10407           }
10408           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10409           {
10410             stored_player[i].shield_normal_time_left = 999999;
10411             stored_player[i].shield_deadly_time_left = 999999;
10412           }
10413         }
10414       }
10415
10416       break;
10417     }
10418
10419     case CA_SET_PLAYER_GRAVITY:
10420     {
10421       for (i = 0; i < MAX_PLAYERS; i++)
10422       {
10423         if (trigger_player_bits & (1 << i))
10424         {
10425           stored_player[i].gravity =
10426             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10427              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10428              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10429              stored_player[i].gravity);
10430         }
10431       }
10432
10433       break;
10434     }
10435
10436     case CA_SET_PLAYER_ARTWORK:
10437     {
10438       for (i = 0; i < MAX_PLAYERS; i++)
10439       {
10440         if (trigger_player_bits & (1 << i))
10441         {
10442           int artwork_element = action_arg_element;
10443
10444           if (action_arg == CA_ARG_ELEMENT_RESET)
10445             artwork_element =
10446               (level.use_artwork_element[i] ? level.artwork_element[i] :
10447                stored_player[i].element_nr);
10448
10449           if (stored_player[i].artwork_element != artwork_element)
10450             stored_player[i].Frame = 0;
10451
10452           stored_player[i].artwork_element = artwork_element;
10453
10454           SetPlayerWaiting(&stored_player[i], FALSE);
10455
10456           // set number of special actions for bored and sleeping animation
10457           stored_player[i].num_special_action_bored =
10458             get_num_special_action(artwork_element,
10459                                    ACTION_BORING_1, ACTION_BORING_LAST);
10460           stored_player[i].num_special_action_sleeping =
10461             get_num_special_action(artwork_element,
10462                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10463         }
10464       }
10465
10466       break;
10467     }
10468
10469     case CA_SET_PLAYER_INVENTORY:
10470     {
10471       for (i = 0; i < MAX_PLAYERS; i++)
10472       {
10473         struct PlayerInfo *player = &stored_player[i];
10474         int j, k;
10475
10476         if (trigger_player_bits & (1 << i))
10477         {
10478           int inventory_element = action_arg_element;
10479
10480           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10481               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10482               action_arg == CA_ARG_ELEMENT_ACTION)
10483           {
10484             int element = inventory_element;
10485             int collect_count = element_info[element].collect_count_initial;
10486
10487             if (!IS_CUSTOM_ELEMENT(element))
10488               collect_count = 1;
10489
10490             if (collect_count == 0)
10491               player->inventory_infinite_element = element;
10492             else
10493               for (k = 0; k < collect_count; k++)
10494                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10495                   player->inventory_element[player->inventory_size++] =
10496                     element;
10497           }
10498           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10499                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10500                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10501           {
10502             if (player->inventory_infinite_element != EL_UNDEFINED &&
10503                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10504                                      action_arg_element_raw))
10505               player->inventory_infinite_element = EL_UNDEFINED;
10506
10507             for (k = 0, j = 0; j < player->inventory_size; j++)
10508             {
10509               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10510                                         action_arg_element_raw))
10511                 player->inventory_element[k++] = player->inventory_element[j];
10512             }
10513
10514             player->inventory_size = k;
10515           }
10516           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10517           {
10518             if (player->inventory_size > 0)
10519             {
10520               for (j = 0; j < player->inventory_size - 1; j++)
10521                 player->inventory_element[j] = player->inventory_element[j + 1];
10522
10523               player->inventory_size--;
10524             }
10525           }
10526           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10527           {
10528             if (player->inventory_size > 0)
10529               player->inventory_size--;
10530           }
10531           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10532           {
10533             player->inventory_infinite_element = EL_UNDEFINED;
10534             player->inventory_size = 0;
10535           }
10536           else if (action_arg == CA_ARG_INVENTORY_RESET)
10537           {
10538             player->inventory_infinite_element = EL_UNDEFINED;
10539             player->inventory_size = 0;
10540
10541             if (level.use_initial_inventory[i])
10542             {
10543               for (j = 0; j < level.initial_inventory_size[i]; j++)
10544               {
10545                 int element = level.initial_inventory_content[i][j];
10546                 int collect_count = element_info[element].collect_count_initial;
10547
10548                 if (!IS_CUSTOM_ELEMENT(element))
10549                   collect_count = 1;
10550
10551                 if (collect_count == 0)
10552                   player->inventory_infinite_element = element;
10553                 else
10554                   for (k = 0; k < collect_count; k++)
10555                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10556                       player->inventory_element[player->inventory_size++] =
10557                         element;
10558               }
10559             }
10560           }
10561         }
10562       }
10563
10564       break;
10565     }
10566
10567     // ---------- CE actions  -------------------------------------------------
10568
10569     case CA_SET_CE_VALUE:
10570     {
10571       int last_ce_value = CustomValue[x][y];
10572
10573       CustomValue[x][y] = action_arg_number_new;
10574
10575       if (CustomValue[x][y] != last_ce_value)
10576       {
10577         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10578         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10579
10580         if (CustomValue[x][y] == 0)
10581         {
10582           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10583           ChangeCount[x][y] = 0;        // allow at least one more change
10584
10585           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10586           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10587         }
10588       }
10589
10590       break;
10591     }
10592
10593     case CA_SET_CE_SCORE:
10594     {
10595       int last_ce_score = ei->collect_score;
10596
10597       ei->collect_score = action_arg_number_new;
10598
10599       if (ei->collect_score != last_ce_score)
10600       {
10601         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10602         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10603
10604         if (ei->collect_score == 0)
10605         {
10606           int xx, yy;
10607
10608           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10609           ChangeCount[x][y] = 0;        // allow at least one more change
10610
10611           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10612           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10613
10614           /*
10615             This is a very special case that seems to be a mixture between
10616             CheckElementChange() and CheckTriggeredElementChange(): while
10617             the first one only affects single elements that are triggered
10618             directly, the second one affects multiple elements in the playfield
10619             that are triggered indirectly by another element. This is a third
10620             case: Changing the CE score always affects multiple identical CEs,
10621             so every affected CE must be checked, not only the single CE for
10622             which the CE score was changed in the first place (as every instance
10623             of that CE shares the same CE score, and therefore also can change)!
10624           */
10625           SCAN_PLAYFIELD(xx, yy)
10626           {
10627             if (Tile[xx][yy] == element)
10628               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10629                                  CE_SCORE_GETS_ZERO);
10630           }
10631         }
10632       }
10633
10634       break;
10635     }
10636
10637     case CA_SET_CE_ARTWORK:
10638     {
10639       int artwork_element = action_arg_element;
10640       boolean reset_frame = FALSE;
10641       int xx, yy;
10642
10643       if (action_arg == CA_ARG_ELEMENT_RESET)
10644         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10645                            element);
10646
10647       if (ei->gfx_element != artwork_element)
10648         reset_frame = TRUE;
10649
10650       ei->gfx_element = artwork_element;
10651
10652       SCAN_PLAYFIELD(xx, yy)
10653       {
10654         if (Tile[xx][yy] == element)
10655         {
10656           if (reset_frame)
10657           {
10658             ResetGfxAnimation(xx, yy);
10659             ResetRandomAnimationValue(xx, yy);
10660           }
10661
10662           TEST_DrawLevelField(xx, yy);
10663         }
10664       }
10665
10666       break;
10667     }
10668
10669     // ---------- engine actions  ---------------------------------------------
10670
10671     case CA_SET_ENGINE_SCAN_MODE:
10672     {
10673       InitPlayfieldScanMode(action_arg);
10674
10675       break;
10676     }
10677
10678     default:
10679       break;
10680   }
10681 }
10682
10683 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10684 {
10685   int old_element = Tile[x][y];
10686   int new_element = GetElementFromGroupElement(element);
10687   int previous_move_direction = MovDir[x][y];
10688   int last_ce_value = CustomValue[x][y];
10689   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10690   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10691   boolean add_player_onto_element = (new_element_is_player &&
10692                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10693                                      IS_WALKABLE(old_element));
10694
10695   if (!add_player_onto_element)
10696   {
10697     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10698       RemoveMovingField(x, y);
10699     else
10700       RemoveField(x, y);
10701
10702     Tile[x][y] = new_element;
10703
10704     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10705       MovDir[x][y] = previous_move_direction;
10706
10707     if (element_info[new_element].use_last_ce_value)
10708       CustomValue[x][y] = last_ce_value;
10709
10710     InitField_WithBug1(x, y, FALSE);
10711
10712     new_element = Tile[x][y];   // element may have changed
10713
10714     ResetGfxAnimation(x, y);
10715     ResetRandomAnimationValue(x, y);
10716
10717     TEST_DrawLevelField(x, y);
10718
10719     if (GFX_CRUMBLED(new_element))
10720       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10721
10722     if (old_element == EL_EXPLOSION)
10723     {
10724       Store[x][y] = Store2[x][y] = 0;
10725
10726       // check if new element replaces an exploding player, requiring cleanup
10727       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10728         StorePlayer[x][y] = 0;
10729     }
10730
10731     // check if element under the player changes from accessible to unaccessible
10732     // (needed for special case of dropping element which then changes)
10733     // (must be checked after creating new element for walkable group elements)
10734     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10735         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10736     {
10737       KillPlayer(PLAYERINFO(x, y));
10738
10739       return;
10740     }
10741   }
10742
10743   // "ChangeCount" not set yet to allow "entered by player" change one time
10744   if (new_element_is_player)
10745     RelocatePlayer(x, y, new_element);
10746
10747   if (is_change)
10748     ChangeCount[x][y]++;        // count number of changes in the same frame
10749
10750   TestIfBadThingTouchesPlayer(x, y);
10751   TestIfPlayerTouchesCustomElement(x, y);
10752   TestIfElementTouchesCustomElement(x, y);
10753 }
10754
10755 static void CreateField(int x, int y, int element)
10756 {
10757   CreateFieldExt(x, y, element, FALSE);
10758 }
10759
10760 static void CreateElementFromChange(int x, int y, int element)
10761 {
10762   element = GET_VALID_RUNTIME_ELEMENT(element);
10763
10764   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10765   {
10766     int old_element = Tile[x][y];
10767
10768     // prevent changed element from moving in same engine frame
10769     // unless both old and new element can either fall or move
10770     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10771         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10772       Stop[x][y] = TRUE;
10773   }
10774
10775   CreateFieldExt(x, y, element, TRUE);
10776 }
10777
10778 static boolean ChangeElement(int x, int y, int element, int page)
10779 {
10780   struct ElementInfo *ei = &element_info[element];
10781   struct ElementChangeInfo *change = &ei->change_page[page];
10782   int ce_value = CustomValue[x][y];
10783   int ce_score = ei->collect_score;
10784   int target_element;
10785   int old_element = Tile[x][y];
10786
10787   // always use default change event to prevent running into a loop
10788   if (ChangeEvent[x][y] == -1)
10789     ChangeEvent[x][y] = CE_DELAY;
10790
10791   if (ChangeEvent[x][y] == CE_DELAY)
10792   {
10793     // reset actual trigger element, trigger player and action element
10794     change->actual_trigger_element = EL_EMPTY;
10795     change->actual_trigger_player = EL_EMPTY;
10796     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10797     change->actual_trigger_side = CH_SIDE_NONE;
10798     change->actual_trigger_ce_value = 0;
10799     change->actual_trigger_ce_score = 0;
10800     change->actual_trigger_x = -1;
10801     change->actual_trigger_y = -1;
10802   }
10803
10804   // do not change elements more than a specified maximum number of changes
10805   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10806     return FALSE;
10807
10808   ChangeCount[x][y]++;          // count number of changes in the same frame
10809
10810   if (ei->has_anim_event)
10811     HandleGlobalAnimEventByElementChange(element, page, x, y,
10812                                          change->actual_trigger_x,
10813                                          change->actual_trigger_y);
10814
10815   if (change->explode)
10816   {
10817     Bang(x, y);
10818
10819     return TRUE;
10820   }
10821
10822   if (change->use_target_content)
10823   {
10824     boolean complete_replace = TRUE;
10825     boolean can_replace[3][3];
10826     int xx, yy;
10827
10828     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10829     {
10830       boolean is_empty;
10831       boolean is_walkable;
10832       boolean is_diggable;
10833       boolean is_collectible;
10834       boolean is_removable;
10835       boolean is_destructible;
10836       int ex = x + xx - 1;
10837       int ey = y + yy - 1;
10838       int content_element = change->target_content.e[xx][yy];
10839       int e;
10840
10841       can_replace[xx][yy] = TRUE;
10842
10843       if (ex == x && ey == y)   // do not check changing element itself
10844         continue;
10845
10846       if (content_element == EL_EMPTY_SPACE)
10847       {
10848         can_replace[xx][yy] = FALSE;    // do not replace border with space
10849
10850         continue;
10851       }
10852
10853       if (!IN_LEV_FIELD(ex, ey))
10854       {
10855         can_replace[xx][yy] = FALSE;
10856         complete_replace = FALSE;
10857
10858         continue;
10859       }
10860
10861       e = Tile[ex][ey];
10862
10863       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10864         e = MovingOrBlocked2Element(ex, ey);
10865
10866       is_empty = (IS_FREE(ex, ey) ||
10867                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10868
10869       is_walkable     = (is_empty || IS_WALKABLE(e));
10870       is_diggable     = (is_empty || IS_DIGGABLE(e));
10871       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10872       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10873       is_removable    = (is_diggable || is_collectible);
10874
10875       can_replace[xx][yy] =
10876         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10877           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10878           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10879           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10880           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10881           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10882          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10883
10884       if (!can_replace[xx][yy])
10885         complete_replace = FALSE;
10886     }
10887
10888     if (!change->only_if_complete || complete_replace)
10889     {
10890       boolean something_has_changed = FALSE;
10891
10892       if (change->only_if_complete && change->use_random_replace &&
10893           RND(100) < change->random_percentage)
10894         return FALSE;
10895
10896       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10897       {
10898         int ex = x + xx - 1;
10899         int ey = y + yy - 1;
10900         int content_element;
10901
10902         if (can_replace[xx][yy] && (!change->use_random_replace ||
10903                                     RND(100) < change->random_percentage))
10904         {
10905           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10906             RemoveMovingField(ex, ey);
10907
10908           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10909
10910           content_element = change->target_content.e[xx][yy];
10911           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10912                                               ce_value, ce_score);
10913
10914           CreateElementFromChange(ex, ey, target_element);
10915
10916           something_has_changed = TRUE;
10917
10918           // for symmetry reasons, freeze newly created border elements
10919           if (ex != x || ey != y)
10920             Stop[ex][ey] = TRUE;        // no more moving in this frame
10921         }
10922       }
10923
10924       if (something_has_changed)
10925       {
10926         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10927         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10928       }
10929     }
10930   }
10931   else
10932   {
10933     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10934                                         ce_value, ce_score);
10935
10936     if (element == EL_DIAGONAL_GROWING ||
10937         element == EL_DIAGONAL_SHRINKING)
10938     {
10939       target_element = Store[x][y];
10940
10941       Store[x][y] = EL_EMPTY;
10942     }
10943
10944     // special case: element changes to player (and may be kept if walkable)
10945     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10946       CreateElementFromChange(x, y, EL_EMPTY);
10947
10948     CreateElementFromChange(x, y, target_element);
10949
10950     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10951     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10952   }
10953
10954   // this uses direct change before indirect change
10955   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10956
10957   return TRUE;
10958 }
10959
10960 static void HandleElementChange(int x, int y, int page)
10961 {
10962   int element = MovingOrBlocked2Element(x, y);
10963   struct ElementInfo *ei = &element_info[element];
10964   struct ElementChangeInfo *change = &ei->change_page[page];
10965   boolean handle_action_before_change = FALSE;
10966
10967 #ifdef DEBUG
10968   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10969       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10970   {
10971     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10972           x, y, element, element_info[element].token_name);
10973     Debug("game:playing:HandleElementChange", "This should never happen!");
10974   }
10975 #endif
10976
10977   // this can happen with classic bombs on walkable, changing elements
10978   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10979   {
10980     return;
10981   }
10982
10983   if (ChangeDelay[x][y] == 0)           // initialize element change
10984   {
10985     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10986
10987     if (change->can_change)
10988     {
10989       // !!! not clear why graphic animation should be reset at all here !!!
10990       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10991       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10992
10993       /*
10994         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10995
10996         When using an animation frame delay of 1 (this only happens with
10997         "sp_zonk.moving.left/right" in the classic graphics), the default
10998         (non-moving) animation shows wrong animation frames (while the
10999         moving animation, like "sp_zonk.moving.left/right", is correct,
11000         so this graphical bug never shows up with the classic graphics).
11001         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11002         be drawn instead of the correct frames 0,1,2,3. This is caused by
11003         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11004         an element change: First when the change delay ("ChangeDelay[][]")
11005         counter has reached zero after decrementing, then a second time in
11006         the next frame (after "GfxFrame[][]" was already incremented) when
11007         "ChangeDelay[][]" is reset to the initial delay value again.
11008
11009         This causes frame 0 to be drawn twice, while the last frame won't
11010         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11011
11012         As some animations may already be cleverly designed around this bug
11013         (at least the "Snake Bite" snake tail animation does this), it cannot
11014         simply be fixed here without breaking such existing animations.
11015         Unfortunately, it cannot easily be detected if a graphics set was
11016         designed "before" or "after" the bug was fixed. As a workaround,
11017         a new graphics set option "game.graphics_engine_version" was added
11018         to be able to specify the game's major release version for which the
11019         graphics set was designed, which can then be used to decide if the
11020         bugfix should be used (version 4 and above) or not (version 3 or
11021         below, or if no version was specified at all, as with old sets).
11022
11023         (The wrong/fixed animation frames can be tested with the test level set
11024         "test_gfxframe" and level "000", which contains a specially prepared
11025         custom element at level position (x/y) == (11/9) which uses the zonk
11026         animation mentioned above. Using "game.graphics_engine_version: 4"
11027         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11028         This can also be seen from the debug output for this test element.)
11029       */
11030
11031       // when a custom element is about to change (for example by change delay),
11032       // do not reset graphic animation when the custom element is moving
11033       if (game.graphics_engine_version < 4 &&
11034           !IS_MOVING(x, y))
11035       {
11036         ResetGfxAnimation(x, y);
11037         ResetRandomAnimationValue(x, y);
11038       }
11039
11040       if (change->pre_change_function)
11041         change->pre_change_function(x, y);
11042     }
11043   }
11044
11045   ChangeDelay[x][y]--;
11046
11047   if (ChangeDelay[x][y] != 0)           // continue element change
11048   {
11049     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11050
11051     // also needed if CE can not change, but has CE delay with CE action
11052     if (IS_ANIMATED(graphic))
11053       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11054
11055     if (change->can_change)
11056     {
11057       if (change->change_function)
11058         change->change_function(x, y);
11059     }
11060   }
11061   else                                  // finish element change
11062   {
11063     if (ChangePage[x][y] != -1)         // remember page from delayed change
11064     {
11065       page = ChangePage[x][y];
11066       ChangePage[x][y] = -1;
11067
11068       change = &ei->change_page[page];
11069     }
11070
11071     if (IS_MOVING(x, y))                // never change a running system ;-)
11072     {
11073       ChangeDelay[x][y] = 1;            // try change after next move step
11074       ChangePage[x][y] = page;          // remember page to use for change
11075
11076       return;
11077     }
11078
11079     // special case: set new level random seed before changing element
11080     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11081       handle_action_before_change = TRUE;
11082
11083     if (change->has_action && handle_action_before_change)
11084       ExecuteCustomElementAction(x, y, element, page);
11085
11086     if (change->can_change)
11087     {
11088       if (ChangeElement(x, y, element, page))
11089       {
11090         if (change->post_change_function)
11091           change->post_change_function(x, y);
11092       }
11093     }
11094
11095     if (change->has_action && !handle_action_before_change)
11096       ExecuteCustomElementAction(x, y, element, page);
11097   }
11098 }
11099
11100 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11101                                               int trigger_element,
11102                                               int trigger_event,
11103                                               int trigger_player,
11104                                               int trigger_side,
11105                                               int trigger_page)
11106 {
11107   boolean change_done_any = FALSE;
11108   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11109   int i;
11110
11111   if (!(trigger_events[trigger_element][trigger_event]))
11112     return FALSE;
11113
11114   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11115
11116   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11117   {
11118     int element = EL_CUSTOM_START + i;
11119     boolean change_done = FALSE;
11120     int p;
11121
11122     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11123         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11124       continue;
11125
11126     for (p = 0; p < element_info[element].num_change_pages; p++)
11127     {
11128       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11129
11130       if (change->can_change_or_has_action &&
11131           change->has_event[trigger_event] &&
11132           change->trigger_side & trigger_side &&
11133           change->trigger_player & trigger_player &&
11134           change->trigger_page & trigger_page_bits &&
11135           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11136       {
11137         change->actual_trigger_element = trigger_element;
11138         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11139         change->actual_trigger_player_bits = trigger_player;
11140         change->actual_trigger_side = trigger_side;
11141         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11142         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11143         change->actual_trigger_x = trigger_x;
11144         change->actual_trigger_y = trigger_y;
11145
11146         if ((change->can_change && !change_done) || change->has_action)
11147         {
11148           int x, y;
11149
11150           SCAN_PLAYFIELD(x, y)
11151           {
11152             if (Tile[x][y] == element)
11153             {
11154               if (change->can_change && !change_done)
11155               {
11156                 // if element already changed in this frame, not only prevent
11157                 // another element change (checked in ChangeElement()), but
11158                 // also prevent additional element actions for this element
11159
11160                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11161                     !level.use_action_after_change_bug)
11162                   continue;
11163
11164                 ChangeDelay[x][y] = 1;
11165                 ChangeEvent[x][y] = trigger_event;
11166
11167                 HandleElementChange(x, y, p);
11168               }
11169               else if (change->has_action)
11170               {
11171                 // if element already changed in this frame, not only prevent
11172                 // another element change (checked in ChangeElement()), but
11173                 // also prevent additional element actions for this element
11174
11175                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11176                     !level.use_action_after_change_bug)
11177                   continue;
11178
11179                 ExecuteCustomElementAction(x, y, element, p);
11180                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11181               }
11182             }
11183           }
11184
11185           if (change->can_change)
11186           {
11187             change_done = TRUE;
11188             change_done_any = TRUE;
11189           }
11190         }
11191       }
11192     }
11193   }
11194
11195   RECURSION_LOOP_DETECTION_END();
11196
11197   return change_done_any;
11198 }
11199
11200 static boolean CheckElementChangeExt(int x, int y,
11201                                      int element,
11202                                      int trigger_element,
11203                                      int trigger_event,
11204                                      int trigger_player,
11205                                      int trigger_side)
11206 {
11207   boolean change_done = FALSE;
11208   int p;
11209
11210   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11211       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11212     return FALSE;
11213
11214   if (Tile[x][y] == EL_BLOCKED)
11215   {
11216     Blocked2Moving(x, y, &x, &y);
11217     element = Tile[x][y];
11218   }
11219
11220   // check if element has already changed or is about to change after moving
11221   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11222        Tile[x][y] != element) ||
11223
11224       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11225        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11226         ChangePage[x][y] != -1)))
11227     return FALSE;
11228
11229   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11230
11231   for (p = 0; p < element_info[element].num_change_pages; p++)
11232   {
11233     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11234
11235     /* check trigger element for all events where the element that is checked
11236        for changing interacts with a directly adjacent element -- this is
11237        different to element changes that affect other elements to change on the
11238        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11239     boolean check_trigger_element =
11240       (trigger_event == CE_NEXT_TO_X ||
11241        trigger_event == CE_TOUCHING_X ||
11242        trigger_event == CE_HITTING_X ||
11243        trigger_event == CE_HIT_BY_X ||
11244        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11245
11246     if (change->can_change_or_has_action &&
11247         change->has_event[trigger_event] &&
11248         change->trigger_side & trigger_side &&
11249         change->trigger_player & trigger_player &&
11250         (!check_trigger_element ||
11251          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11252     {
11253       change->actual_trigger_element = trigger_element;
11254       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11255       change->actual_trigger_player_bits = trigger_player;
11256       change->actual_trigger_side = trigger_side;
11257       change->actual_trigger_ce_value = CustomValue[x][y];
11258       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11259       change->actual_trigger_x = x;
11260       change->actual_trigger_y = y;
11261
11262       // special case: trigger element not at (x,y) position for some events
11263       if (check_trigger_element)
11264       {
11265         static struct
11266         {
11267           int dx, dy;
11268         } move_xy[] =
11269           {
11270             {  0,  0 },
11271             { -1,  0 },
11272             { +1,  0 },
11273             {  0,  0 },
11274             {  0, -1 },
11275             {  0,  0 }, { 0, 0 }, { 0, 0 },
11276             {  0, +1 }
11277           };
11278
11279         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11280         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11281
11282         change->actual_trigger_ce_value = CustomValue[xx][yy];
11283         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11284         change->actual_trigger_x = xx;
11285         change->actual_trigger_y = yy;
11286       }
11287
11288       if (change->can_change && !change_done)
11289       {
11290         ChangeDelay[x][y] = 1;
11291         ChangeEvent[x][y] = trigger_event;
11292
11293         HandleElementChange(x, y, p);
11294
11295         change_done = TRUE;
11296       }
11297       else if (change->has_action)
11298       {
11299         ExecuteCustomElementAction(x, y, element, p);
11300         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11301       }
11302     }
11303   }
11304
11305   RECURSION_LOOP_DETECTION_END();
11306
11307   return change_done;
11308 }
11309
11310 static void PlayPlayerSound(struct PlayerInfo *player)
11311 {
11312   int jx = player->jx, jy = player->jy;
11313   int sound_element = player->artwork_element;
11314   int last_action = player->last_action_waiting;
11315   int action = player->action_waiting;
11316
11317   if (player->is_waiting)
11318   {
11319     if (action != last_action)
11320       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11321     else
11322       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11323   }
11324   else
11325   {
11326     if (action != last_action)
11327       StopSound(element_info[sound_element].sound[last_action]);
11328
11329     if (last_action == ACTION_SLEEPING)
11330       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11331   }
11332 }
11333
11334 static void PlayAllPlayersSound(void)
11335 {
11336   int i;
11337
11338   for (i = 0; i < MAX_PLAYERS; i++)
11339     if (stored_player[i].active)
11340       PlayPlayerSound(&stored_player[i]);
11341 }
11342
11343 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11344 {
11345   boolean last_waiting = player->is_waiting;
11346   int move_dir = player->MovDir;
11347
11348   player->dir_waiting = move_dir;
11349   player->last_action_waiting = player->action_waiting;
11350
11351   if (is_waiting)
11352   {
11353     if (!last_waiting)          // not waiting -> waiting
11354     {
11355       player->is_waiting = TRUE;
11356
11357       player->frame_counter_bored =
11358         FrameCounter +
11359         game.player_boring_delay_fixed +
11360         GetSimpleRandom(game.player_boring_delay_random);
11361       player->frame_counter_sleeping =
11362         FrameCounter +
11363         game.player_sleeping_delay_fixed +
11364         GetSimpleRandom(game.player_sleeping_delay_random);
11365
11366       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11367     }
11368
11369     if (game.player_sleeping_delay_fixed +
11370         game.player_sleeping_delay_random > 0 &&
11371         player->anim_delay_counter == 0 &&
11372         player->post_delay_counter == 0 &&
11373         FrameCounter >= player->frame_counter_sleeping)
11374       player->is_sleeping = TRUE;
11375     else if (game.player_boring_delay_fixed +
11376              game.player_boring_delay_random > 0 &&
11377              FrameCounter >= player->frame_counter_bored)
11378       player->is_bored = TRUE;
11379
11380     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11381                               player->is_bored ? ACTION_BORING :
11382                               ACTION_WAITING);
11383
11384     if (player->is_sleeping && player->use_murphy)
11385     {
11386       // special case for sleeping Murphy when leaning against non-free tile
11387
11388       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11389           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11390            !IS_MOVING(player->jx - 1, player->jy)))
11391         move_dir = MV_LEFT;
11392       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11393                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11394                 !IS_MOVING(player->jx + 1, player->jy)))
11395         move_dir = MV_RIGHT;
11396       else
11397         player->is_sleeping = FALSE;
11398
11399       player->dir_waiting = move_dir;
11400     }
11401
11402     if (player->is_sleeping)
11403     {
11404       if (player->num_special_action_sleeping > 0)
11405       {
11406         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11407         {
11408           int last_special_action = player->special_action_sleeping;
11409           int num_special_action = player->num_special_action_sleeping;
11410           int special_action =
11411             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11412              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11413              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11414              last_special_action + 1 : ACTION_SLEEPING);
11415           int special_graphic =
11416             el_act_dir2img(player->artwork_element, special_action, move_dir);
11417
11418           player->anim_delay_counter =
11419             graphic_info[special_graphic].anim_delay_fixed +
11420             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11421           player->post_delay_counter =
11422             graphic_info[special_graphic].post_delay_fixed +
11423             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11424
11425           player->special_action_sleeping = special_action;
11426         }
11427
11428         if (player->anim_delay_counter > 0)
11429         {
11430           player->action_waiting = player->special_action_sleeping;
11431           player->anim_delay_counter--;
11432         }
11433         else if (player->post_delay_counter > 0)
11434         {
11435           player->post_delay_counter--;
11436         }
11437       }
11438     }
11439     else if (player->is_bored)
11440     {
11441       if (player->num_special_action_bored > 0)
11442       {
11443         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11444         {
11445           int special_action =
11446             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11447           int special_graphic =
11448             el_act_dir2img(player->artwork_element, special_action, move_dir);
11449
11450           player->anim_delay_counter =
11451             graphic_info[special_graphic].anim_delay_fixed +
11452             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11453           player->post_delay_counter =
11454             graphic_info[special_graphic].post_delay_fixed +
11455             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11456
11457           player->special_action_bored = special_action;
11458         }
11459
11460         if (player->anim_delay_counter > 0)
11461         {
11462           player->action_waiting = player->special_action_bored;
11463           player->anim_delay_counter--;
11464         }
11465         else if (player->post_delay_counter > 0)
11466         {
11467           player->post_delay_counter--;
11468         }
11469       }
11470     }
11471   }
11472   else if (last_waiting)        // waiting -> not waiting
11473   {
11474     player->is_waiting = FALSE;
11475     player->is_bored = FALSE;
11476     player->is_sleeping = FALSE;
11477
11478     player->frame_counter_bored = -1;
11479     player->frame_counter_sleeping = -1;
11480
11481     player->anim_delay_counter = 0;
11482     player->post_delay_counter = 0;
11483
11484     player->dir_waiting = player->MovDir;
11485     player->action_waiting = ACTION_DEFAULT;
11486
11487     player->special_action_bored = ACTION_DEFAULT;
11488     player->special_action_sleeping = ACTION_DEFAULT;
11489   }
11490 }
11491
11492 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11493 {
11494   if ((!player->is_moving  && player->was_moving) ||
11495       (player->MovPos == 0 && player->was_moving) ||
11496       (player->is_snapping && !player->was_snapping) ||
11497       (player->is_dropping && !player->was_dropping))
11498   {
11499     if (!CheckSaveEngineSnapshotToList())
11500       return;
11501
11502     player->was_moving = FALSE;
11503     player->was_snapping = TRUE;
11504     player->was_dropping = TRUE;
11505   }
11506   else
11507   {
11508     if (player->is_moving)
11509       player->was_moving = TRUE;
11510
11511     if (!player->is_snapping)
11512       player->was_snapping = FALSE;
11513
11514     if (!player->is_dropping)
11515       player->was_dropping = FALSE;
11516   }
11517
11518   static struct MouseActionInfo mouse_action_last = { 0 };
11519   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11520   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11521
11522   if (new_released)
11523     CheckSaveEngineSnapshotToList();
11524
11525   mouse_action_last = mouse_action;
11526 }
11527
11528 static void CheckSingleStepMode(struct PlayerInfo *player)
11529 {
11530   if (tape.single_step && tape.recording && !tape.pausing)
11531   {
11532     // as it is called "single step mode", just return to pause mode when the
11533     // player stopped moving after one tile (or never starts moving at all)
11534     // (reverse logic needed here in case single step mode used in team mode)
11535     if (player->is_moving ||
11536         player->is_pushing ||
11537         player->is_dropping_pressed ||
11538         player->effective_mouse_action.button)
11539       game.enter_single_step_mode = FALSE;
11540   }
11541
11542   CheckSaveEngineSnapshot(player);
11543 }
11544
11545 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11546 {
11547   int left      = player_action & JOY_LEFT;
11548   int right     = player_action & JOY_RIGHT;
11549   int up        = player_action & JOY_UP;
11550   int down      = player_action & JOY_DOWN;
11551   int button1   = player_action & JOY_BUTTON_1;
11552   int button2   = player_action & JOY_BUTTON_2;
11553   int dx        = (left ? -1 : right ? 1 : 0);
11554   int dy        = (up   ? -1 : down  ? 1 : 0);
11555
11556   if (!player->active || tape.pausing)
11557     return 0;
11558
11559   if (player_action)
11560   {
11561     if (button1)
11562       SnapField(player, dx, dy);
11563     else
11564     {
11565       if (button2)
11566         DropElement(player);
11567
11568       MovePlayer(player, dx, dy);
11569     }
11570
11571     CheckSingleStepMode(player);
11572
11573     SetPlayerWaiting(player, FALSE);
11574
11575     return player_action;
11576   }
11577   else
11578   {
11579     // no actions for this player (no input at player's configured device)
11580
11581     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11582     SnapField(player, 0, 0);
11583     CheckGravityMovementWhenNotMoving(player);
11584
11585     if (player->MovPos == 0)
11586       SetPlayerWaiting(player, TRUE);
11587
11588     if (player->MovPos == 0)    // needed for tape.playing
11589       player->is_moving = FALSE;
11590
11591     player->is_dropping = FALSE;
11592     player->is_dropping_pressed = FALSE;
11593     player->drop_pressed_delay = 0;
11594
11595     CheckSingleStepMode(player);
11596
11597     return 0;
11598   }
11599 }
11600
11601 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11602                                          byte *tape_action)
11603 {
11604   if (!tape.use_mouse_actions)
11605     return;
11606
11607   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11608   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11609   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11610 }
11611
11612 static void SetTapeActionFromMouseAction(byte *tape_action,
11613                                          struct MouseActionInfo *mouse_action)
11614 {
11615   if (!tape.use_mouse_actions)
11616     return;
11617
11618   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11619   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11620   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11621 }
11622
11623 static void CheckLevelSolved(void)
11624 {
11625   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11626   {
11627     if (game_em.level_solved &&
11628         !game_em.game_over)                             // game won
11629     {
11630       LevelSolved();
11631
11632       game_em.game_over = TRUE;
11633
11634       game.all_players_gone = TRUE;
11635     }
11636
11637     if (game_em.game_over)                              // game lost
11638       game.all_players_gone = TRUE;
11639   }
11640   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11641   {
11642     if (game_sp.level_solved &&
11643         !game_sp.game_over)                             // game won
11644     {
11645       LevelSolved();
11646
11647       game_sp.game_over = TRUE;
11648
11649       game.all_players_gone = TRUE;
11650     }
11651
11652     if (game_sp.game_over)                              // game lost
11653       game.all_players_gone = TRUE;
11654   }
11655   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11656   {
11657     if (game_mm.level_solved &&
11658         !game_mm.game_over)                             // game won
11659     {
11660       LevelSolved();
11661
11662       game_mm.game_over = TRUE;
11663
11664       game.all_players_gone = TRUE;
11665     }
11666
11667     if (game_mm.game_over)                              // game lost
11668       game.all_players_gone = TRUE;
11669   }
11670 }
11671
11672 static void CheckLevelTime_StepCounter(void)
11673 {
11674   int i;
11675
11676   TimePlayed++;
11677
11678   if (TimeLeft > 0)
11679   {
11680     TimeLeft--;
11681
11682     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11683       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11684
11685     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11686
11687     DisplayGameControlValues();
11688
11689     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11690       for (i = 0; i < MAX_PLAYERS; i++)
11691         KillPlayer(&stored_player[i]);
11692   }
11693   else if (game.no_level_time_limit && !game.all_players_gone)
11694   {
11695     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11696
11697     DisplayGameControlValues();
11698   }
11699 }
11700
11701 static void CheckLevelTime(void)
11702 {
11703   int i;
11704
11705   if (TimeFrames >= FRAMES_PER_SECOND)
11706   {
11707     TimeFrames = 0;
11708
11709     for (i = 0; i < MAX_PLAYERS; i++)
11710     {
11711       struct PlayerInfo *player = &stored_player[i];
11712
11713       if (SHIELD_ON(player))
11714       {
11715         player->shield_normal_time_left--;
11716
11717         if (player->shield_deadly_time_left > 0)
11718           player->shield_deadly_time_left--;
11719       }
11720     }
11721
11722     if (!game.LevelSolved && !level.use_step_counter)
11723     {
11724       TimePlayed++;
11725
11726       if (TimeLeft > 0)
11727       {
11728         TimeLeft--;
11729
11730         if (TimeLeft <= 10 && game.time_limit)
11731           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11732
11733         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11734            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11735
11736         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11737
11738         if (!TimeLeft && game.time_limit)
11739         {
11740           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11741             game_em.lev->killed_out_of_time = TRUE;
11742           else
11743             for (i = 0; i < MAX_PLAYERS; i++)
11744               KillPlayer(&stored_player[i]);
11745         }
11746       }
11747       else if (game.no_level_time_limit && !game.all_players_gone)
11748       {
11749         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11750       }
11751
11752       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11753     }
11754   }
11755
11756   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11757   {
11758     TapeTimeFrames = 0;
11759     TapeTime++;
11760
11761     if (tape.recording || tape.playing)
11762       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11763   }
11764
11765   if (tape.recording || tape.playing)
11766     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11767
11768   UpdateAndDisplayGameControlValues();
11769 }
11770
11771 void AdvanceFrameAndPlayerCounters(int player_nr)
11772 {
11773   int i;
11774
11775   // advance frame counters (global frame counter and tape time frame counter)
11776   FrameCounter++;
11777   TapeTimeFrames++;
11778
11779   // advance time frame counter (used to control available time to solve level)
11780   TimeFrames++;
11781
11782   // advance player counters (counters for move delay, move animation etc.)
11783   for (i = 0; i < MAX_PLAYERS; i++)
11784   {
11785     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11786     int move_delay_value = stored_player[i].move_delay_value;
11787     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11788
11789     if (!advance_player_counters)       // not all players may be affected
11790       continue;
11791
11792     if (move_frames == 0)       // less than one move per game frame
11793     {
11794       int stepsize = TILEX / move_delay_value;
11795       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11796       int count = (stored_player[i].is_moving ?
11797                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11798
11799       if (count % delay == 0)
11800         move_frames = 1;
11801     }
11802
11803     stored_player[i].Frame += move_frames;
11804
11805     if (stored_player[i].MovPos != 0)
11806       stored_player[i].StepFrame += move_frames;
11807
11808     if (stored_player[i].move_delay > 0)
11809       stored_player[i].move_delay--;
11810
11811     // due to bugs in previous versions, counter must count up, not down
11812     if (stored_player[i].push_delay != -1)
11813       stored_player[i].push_delay++;
11814
11815     if (stored_player[i].drop_delay > 0)
11816       stored_player[i].drop_delay--;
11817
11818     if (stored_player[i].is_dropping_pressed)
11819       stored_player[i].drop_pressed_delay++;
11820   }
11821 }
11822
11823 void AdvanceFrameCounter(void)
11824 {
11825   FrameCounter++;
11826 }
11827
11828 void AdvanceGfxFrame(void)
11829 {
11830   int x, y;
11831
11832   SCAN_PLAYFIELD(x, y)
11833   {
11834     GfxFrame[x][y]++;
11835   }
11836 }
11837
11838 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11839                               struct MouseActionInfo *mouse_action_last)
11840 {
11841   if (mouse_action->button)
11842   {
11843     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11844     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11845     int x = mouse_action->lx;
11846     int y = mouse_action->ly;
11847     int element = Tile[x][y];
11848
11849     if (new_button)
11850     {
11851       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11852       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11853                                          ch_button);
11854     }
11855
11856     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11857     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11858                                        ch_button);
11859
11860     if (level.use_step_counter)
11861     {
11862       boolean counted_click = FALSE;
11863
11864       // element clicked that can change when clicked/pressed
11865       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11866           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11867            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11868         counted_click = TRUE;
11869
11870       // element clicked that can trigger change when clicked/pressed
11871       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11872           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11873         counted_click = TRUE;
11874
11875       if (new_button && counted_click)
11876         CheckLevelTime_StepCounter();
11877     }
11878   }
11879 }
11880
11881 void StartGameActions(boolean init_network_game, boolean record_tape,
11882                       int random_seed)
11883 {
11884   unsigned int new_random_seed = InitRND(random_seed);
11885
11886   if (record_tape)
11887     TapeStartRecording(new_random_seed);
11888
11889   if (setup.auto_pause_on_start && !tape.pausing)
11890     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11891
11892   if (init_network_game)
11893   {
11894     SendToServer_LevelFile();
11895     SendToServer_StartPlaying();
11896
11897     return;
11898   }
11899
11900   InitGame();
11901 }
11902
11903 static void GameActionsExt(void)
11904 {
11905 #if 0
11906   static unsigned int game_frame_delay = 0;
11907 #endif
11908   unsigned int game_frame_delay_value;
11909   byte *recorded_player_action;
11910   byte summarized_player_action = 0;
11911   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11912   int i;
11913
11914   // detect endless loops, caused by custom element programming
11915   if (recursion_loop_detected && recursion_loop_depth == 0)
11916   {
11917     char *message = getStringCat3("Internal Error! Element ",
11918                                   EL_NAME(recursion_loop_element),
11919                                   " caused endless loop! Quit the game?");
11920
11921     Warn("element '%s' caused endless loop in game engine",
11922          EL_NAME(recursion_loop_element));
11923
11924     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11925
11926     recursion_loop_detected = FALSE;    // if game should be continued
11927
11928     free(message);
11929
11930     return;
11931   }
11932
11933   if (game.restart_level)
11934     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11935
11936   CheckLevelSolved();
11937
11938   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11939     GameWon();
11940
11941   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11942     TapeStop();
11943
11944   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11945     return;
11946
11947   game_frame_delay_value =
11948     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11949
11950   if (tape.playing && tape.warp_forward && !tape.pausing)
11951     game_frame_delay_value = 0;
11952
11953   SetVideoFrameDelay(game_frame_delay_value);
11954
11955   // (de)activate virtual buttons depending on current game status
11956   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11957   {
11958     if (game.all_players_gone)  // if no players there to be controlled anymore
11959       SetOverlayActive(FALSE);
11960     else if (!tape.playing)     // if game continues after tape stopped playing
11961       SetOverlayActive(TRUE);
11962   }
11963
11964 #if 0
11965 #if 0
11966   // ---------- main game synchronization point ----------
11967
11968   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11969
11970   Debug("game:playing:skip", "skip == %d", skip);
11971
11972 #else
11973   // ---------- main game synchronization point ----------
11974
11975   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11976 #endif
11977 #endif
11978
11979   if (network_playing && !network_player_action_received)
11980   {
11981     // try to get network player actions in time
11982
11983     // last chance to get network player actions without main loop delay
11984     HandleNetworking();
11985
11986     // game was quit by network peer
11987     if (game_status != GAME_MODE_PLAYING)
11988       return;
11989
11990     // check if network player actions still missing and game still running
11991     if (!network_player_action_received && !checkGameEnded())
11992       return;           // failed to get network player actions in time
11993
11994     // do not yet reset "network_player_action_received" (for tape.pausing)
11995   }
11996
11997   if (tape.pausing)
11998     return;
11999
12000   // at this point we know that we really continue executing the game
12001
12002   network_player_action_received = FALSE;
12003
12004   // when playing tape, read previously recorded player input from tape data
12005   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12006
12007   local_player->effective_mouse_action = local_player->mouse_action;
12008
12009   if (recorded_player_action != NULL)
12010     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12011                                  recorded_player_action);
12012
12013   // TapePlayAction() may return NULL when toggling to "pause before death"
12014   if (tape.pausing)
12015     return;
12016
12017   if (tape.set_centered_player)
12018   {
12019     game.centered_player_nr_next = tape.centered_player_nr_next;
12020     game.set_centered_player = TRUE;
12021   }
12022
12023   for (i = 0; i < MAX_PLAYERS; i++)
12024   {
12025     summarized_player_action |= stored_player[i].action;
12026
12027     if (!network_playing && (game.team_mode || tape.playing))
12028       stored_player[i].effective_action = stored_player[i].action;
12029   }
12030
12031   if (network_playing && !checkGameEnded())
12032     SendToServer_MovePlayer(summarized_player_action);
12033
12034   // summarize all actions at local players mapped input device position
12035   // (this allows using different input devices in single player mode)
12036   if (!network.enabled && !game.team_mode)
12037     stored_player[map_player_action[local_player->index_nr]].effective_action =
12038       summarized_player_action;
12039
12040   // summarize all actions at centered player in local team mode
12041   if (tape.recording &&
12042       setup.team_mode && !network.enabled &&
12043       setup.input_on_focus &&
12044       game.centered_player_nr != -1)
12045   {
12046     for (i = 0; i < MAX_PLAYERS; i++)
12047       stored_player[map_player_action[i]].effective_action =
12048         (i == game.centered_player_nr ? summarized_player_action : 0);
12049   }
12050
12051   if (recorded_player_action != NULL)
12052     for (i = 0; i < MAX_PLAYERS; i++)
12053       stored_player[i].effective_action = recorded_player_action[i];
12054
12055   for (i = 0; i < MAX_PLAYERS; i++)
12056   {
12057     tape_action[i] = stored_player[i].effective_action;
12058
12059     /* (this may happen in the RND game engine if a player was not present on
12060        the playfield on level start, but appeared later from a custom element */
12061     if (setup.team_mode &&
12062         tape.recording &&
12063         tape_action[i] &&
12064         !tape.player_participates[i])
12065       tape.player_participates[i] = TRUE;
12066   }
12067
12068   SetTapeActionFromMouseAction(tape_action,
12069                                &local_player->effective_mouse_action);
12070
12071   // only record actions from input devices, but not programmed actions
12072   if (tape.recording)
12073     TapeRecordAction(tape_action);
12074
12075   // remember if game was played (especially after tape stopped playing)
12076   if (!tape.playing && summarized_player_action && !checkGameFailed())
12077     game.GamePlayed = TRUE;
12078
12079 #if USE_NEW_PLAYER_ASSIGNMENTS
12080   // !!! also map player actions in single player mode !!!
12081   // if (game.team_mode)
12082   if (1)
12083   {
12084     byte mapped_action[MAX_PLAYERS];
12085
12086 #if DEBUG_PLAYER_ACTIONS
12087     for (i = 0; i < MAX_PLAYERS; i++)
12088       DebugContinued("", "%d, ", stored_player[i].effective_action);
12089 #endif
12090
12091     for (i = 0; i < MAX_PLAYERS; i++)
12092       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12093
12094     for (i = 0; i < MAX_PLAYERS; i++)
12095       stored_player[i].effective_action = mapped_action[i];
12096
12097 #if DEBUG_PLAYER_ACTIONS
12098     DebugContinued("", "=> ");
12099     for (i = 0; i < MAX_PLAYERS; i++)
12100       DebugContinued("", "%d, ", stored_player[i].effective_action);
12101     DebugContinued("game:playing:player", "\n");
12102 #endif
12103   }
12104 #if DEBUG_PLAYER_ACTIONS
12105   else
12106   {
12107     for (i = 0; i < MAX_PLAYERS; i++)
12108       DebugContinued("", "%d, ", stored_player[i].effective_action);
12109     DebugContinued("game:playing:player", "\n");
12110   }
12111 #endif
12112 #endif
12113
12114   for (i = 0; i < MAX_PLAYERS; i++)
12115   {
12116     // allow engine snapshot in case of changed movement attempt
12117     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12118         (stored_player[i].effective_action & KEY_MOTION))
12119       game.snapshot.changed_action = TRUE;
12120
12121     // allow engine snapshot in case of snapping/dropping attempt
12122     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12123         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12124       game.snapshot.changed_action = TRUE;
12125
12126     game.snapshot.last_action[i] = stored_player[i].effective_action;
12127   }
12128
12129   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12130   {
12131     GameActions_EM_Main();
12132   }
12133   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12134   {
12135     GameActions_SP_Main();
12136   }
12137   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12138   {
12139     GameActions_MM_Main();
12140   }
12141   else
12142   {
12143     GameActions_RND_Main();
12144   }
12145
12146   BlitScreenToBitmap(backbuffer);
12147
12148   CheckLevelSolved();
12149   CheckLevelTime();
12150
12151   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12152
12153   if (global.show_frames_per_second)
12154   {
12155     static unsigned int fps_counter = 0;
12156     static int fps_frames = 0;
12157     unsigned int fps_delay_ms = Counter() - fps_counter;
12158
12159     fps_frames++;
12160
12161     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12162     {
12163       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12164
12165       fps_frames = 0;
12166       fps_counter = Counter();
12167
12168       // always draw FPS to screen after FPS value was updated
12169       redraw_mask |= REDRAW_FPS;
12170     }
12171
12172     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12173     if (GetDrawDeactivationMask() == REDRAW_NONE)
12174       redraw_mask |= REDRAW_FPS;
12175   }
12176 }
12177
12178 static void GameActions_CheckSaveEngineSnapshot(void)
12179 {
12180   if (!game.snapshot.save_snapshot)
12181     return;
12182
12183   // clear flag for saving snapshot _before_ saving snapshot
12184   game.snapshot.save_snapshot = FALSE;
12185
12186   SaveEngineSnapshotToList();
12187 }
12188
12189 void GameActions(void)
12190 {
12191   GameActionsExt();
12192
12193   GameActions_CheckSaveEngineSnapshot();
12194 }
12195
12196 void GameActions_EM_Main(void)
12197 {
12198   byte effective_action[MAX_PLAYERS];
12199   int i;
12200
12201   for (i = 0; i < MAX_PLAYERS; i++)
12202     effective_action[i] = stored_player[i].effective_action;
12203
12204   GameActions_EM(effective_action);
12205 }
12206
12207 void GameActions_SP_Main(void)
12208 {
12209   byte effective_action[MAX_PLAYERS];
12210   int i;
12211
12212   for (i = 0; i < MAX_PLAYERS; i++)
12213     effective_action[i] = stored_player[i].effective_action;
12214
12215   GameActions_SP(effective_action);
12216
12217   for (i = 0; i < MAX_PLAYERS; i++)
12218   {
12219     if (stored_player[i].force_dropping)
12220       stored_player[i].action |= KEY_BUTTON_DROP;
12221
12222     stored_player[i].force_dropping = FALSE;
12223   }
12224 }
12225
12226 void GameActions_MM_Main(void)
12227 {
12228   AdvanceGfxFrame();
12229
12230   GameActions_MM(local_player->effective_mouse_action);
12231 }
12232
12233 void GameActions_RND_Main(void)
12234 {
12235   GameActions_RND();
12236 }
12237
12238 void GameActions_RND(void)
12239 {
12240   static struct MouseActionInfo mouse_action_last = { 0 };
12241   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12242   int magic_wall_x = 0, magic_wall_y = 0;
12243   int i, x, y, element, graphic, last_gfx_frame;
12244
12245   InitPlayfieldScanModeVars();
12246
12247   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12248   {
12249     SCAN_PLAYFIELD(x, y)
12250     {
12251       ChangeCount[x][y] = 0;
12252       ChangeEvent[x][y] = -1;
12253     }
12254   }
12255
12256   if (game.set_centered_player)
12257   {
12258     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12259
12260     // switching to "all players" only possible if all players fit to screen
12261     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12262     {
12263       game.centered_player_nr_next = game.centered_player_nr;
12264       game.set_centered_player = FALSE;
12265     }
12266
12267     // do not switch focus to non-existing (or non-active) player
12268     if (game.centered_player_nr_next >= 0 &&
12269         !stored_player[game.centered_player_nr_next].active)
12270     {
12271       game.centered_player_nr_next = game.centered_player_nr;
12272       game.set_centered_player = FALSE;
12273     }
12274   }
12275
12276   if (game.set_centered_player &&
12277       ScreenMovPos == 0)        // screen currently aligned at tile position
12278   {
12279     int sx, sy;
12280
12281     if (game.centered_player_nr_next == -1)
12282     {
12283       setScreenCenteredToAllPlayers(&sx, &sy);
12284     }
12285     else
12286     {
12287       sx = stored_player[game.centered_player_nr_next].jx;
12288       sy = stored_player[game.centered_player_nr_next].jy;
12289     }
12290
12291     game.centered_player_nr = game.centered_player_nr_next;
12292     game.set_centered_player = FALSE;
12293
12294     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12295     DrawGameDoorValues();
12296   }
12297
12298   // check single step mode (set flag and clear again if any player is active)
12299   game.enter_single_step_mode =
12300     (tape.single_step && tape.recording && !tape.pausing);
12301
12302   for (i = 0; i < MAX_PLAYERS; i++)
12303   {
12304     int actual_player_action = stored_player[i].effective_action;
12305
12306 #if 1
12307     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12308        - rnd_equinox_tetrachloride 048
12309        - rnd_equinox_tetrachloride_ii 096
12310        - rnd_emanuel_schmieg 002
12311        - doctor_sloan_ww 001, 020
12312     */
12313     if (stored_player[i].MovPos == 0)
12314       CheckGravityMovement(&stored_player[i]);
12315 #endif
12316
12317     // overwrite programmed action with tape action
12318     if (stored_player[i].programmed_action)
12319       actual_player_action = stored_player[i].programmed_action;
12320
12321     PlayerActions(&stored_player[i], actual_player_action);
12322
12323     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12324   }
12325
12326   // single step pause mode may already have been toggled by "ScrollPlayer()"
12327   if (game.enter_single_step_mode && !tape.pausing)
12328     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12329
12330   ScrollScreen(NULL, SCROLL_GO_ON);
12331
12332   /* for backwards compatibility, the following code emulates a fixed bug that
12333      occured when pushing elements (causing elements that just made their last
12334      pushing step to already (if possible) make their first falling step in the
12335      same game frame, which is bad); this code is also needed to use the famous
12336      "spring push bug" which is used in older levels and might be wanted to be
12337      used also in newer levels, but in this case the buggy pushing code is only
12338      affecting the "spring" element and no other elements */
12339
12340   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12341   {
12342     for (i = 0; i < MAX_PLAYERS; i++)
12343     {
12344       struct PlayerInfo *player = &stored_player[i];
12345       int x = player->jx;
12346       int y = player->jy;
12347
12348       if (player->active && player->is_pushing && player->is_moving &&
12349           IS_MOVING(x, y) &&
12350           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12351            Tile[x][y] == EL_SPRING))
12352       {
12353         ContinueMoving(x, y);
12354
12355         // continue moving after pushing (this is actually a bug)
12356         if (!IS_MOVING(x, y))
12357           Stop[x][y] = FALSE;
12358       }
12359     }
12360   }
12361
12362   SCAN_PLAYFIELD(x, y)
12363   {
12364     Last[x][y] = Tile[x][y];
12365
12366     ChangeCount[x][y] = 0;
12367     ChangeEvent[x][y] = -1;
12368
12369     // this must be handled before main playfield loop
12370     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12371     {
12372       MovDelay[x][y]--;
12373       if (MovDelay[x][y] <= 0)
12374         RemoveField(x, y);
12375     }
12376
12377     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12378     {
12379       MovDelay[x][y]--;
12380       if (MovDelay[x][y] <= 0)
12381       {
12382         int element = Store[x][y];
12383         int move_direction = MovDir[x][y];
12384         int player_index_bit = Store2[x][y];
12385
12386         Store[x][y] = 0;
12387         Store2[x][y] = 0;
12388
12389         RemoveField(x, y);
12390         TEST_DrawLevelField(x, y);
12391
12392         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12393
12394         if (IS_ENVELOPE(element))
12395           local_player->show_envelope = element;
12396       }
12397     }
12398
12399 #if DEBUG
12400     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12401     {
12402       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12403             x, y);
12404       Debug("game:playing:GameActions_RND", "This should never happen!");
12405
12406       ChangePage[x][y] = -1;
12407     }
12408 #endif
12409
12410     Stop[x][y] = FALSE;
12411     if (WasJustMoving[x][y] > 0)
12412       WasJustMoving[x][y]--;
12413     if (WasJustFalling[x][y] > 0)
12414       WasJustFalling[x][y]--;
12415     if (CheckCollision[x][y] > 0)
12416       CheckCollision[x][y]--;
12417     if (CheckImpact[x][y] > 0)
12418       CheckImpact[x][y]--;
12419
12420     GfxFrame[x][y]++;
12421
12422     /* reset finished pushing action (not done in ContinueMoving() to allow
12423        continuous pushing animation for elements with zero push delay) */
12424     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12425     {
12426       ResetGfxAnimation(x, y);
12427       TEST_DrawLevelField(x, y);
12428     }
12429
12430 #if DEBUG
12431     if (IS_BLOCKED(x, y))
12432     {
12433       int oldx, oldy;
12434
12435       Blocked2Moving(x, y, &oldx, &oldy);
12436       if (!IS_MOVING(oldx, oldy))
12437       {
12438         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12439         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12440         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12441         Debug("game:playing:GameActions_RND", "This should never happen!");
12442       }
12443     }
12444 #endif
12445   }
12446
12447   HandleMouseAction(&mouse_action, &mouse_action_last);
12448
12449   SCAN_PLAYFIELD(x, y)
12450   {
12451     element = Tile[x][y];
12452     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12453     last_gfx_frame = GfxFrame[x][y];
12454
12455     if (element == EL_EMPTY)
12456       graphic = el2img(GfxElementEmpty[x][y]);
12457
12458     ResetGfxFrame(x, y);
12459
12460     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12461       DrawLevelGraphicAnimation(x, y, graphic);
12462
12463     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12464         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12465       ResetRandomAnimationValue(x, y);
12466
12467     SetRandomAnimationValue(x, y);
12468
12469     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12470
12471     if (IS_INACTIVE(element))
12472     {
12473       if (IS_ANIMATED(graphic))
12474         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12475
12476       continue;
12477     }
12478
12479     // this may take place after moving, so 'element' may have changed
12480     if (IS_CHANGING(x, y) &&
12481         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12482     {
12483       int page = element_info[element].event_page_nr[CE_DELAY];
12484
12485       HandleElementChange(x, y, page);
12486
12487       element = Tile[x][y];
12488       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12489     }
12490
12491     CheckNextToConditions(x, y);
12492
12493     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12494     {
12495       StartMoving(x, y);
12496
12497       element = Tile[x][y];
12498       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12499
12500       if (IS_ANIMATED(graphic) &&
12501           !IS_MOVING(x, y) &&
12502           !Stop[x][y])
12503         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12504
12505       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12506         TEST_DrawTwinkleOnField(x, y);
12507     }
12508     else if (element == EL_ACID)
12509     {
12510       if (!Stop[x][y])
12511         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12512     }
12513     else if ((element == EL_EXIT_OPEN ||
12514               element == EL_EM_EXIT_OPEN ||
12515               element == EL_SP_EXIT_OPEN ||
12516               element == EL_STEEL_EXIT_OPEN ||
12517               element == EL_EM_STEEL_EXIT_OPEN ||
12518               element == EL_SP_TERMINAL ||
12519               element == EL_SP_TERMINAL_ACTIVE ||
12520               element == EL_EXTRA_TIME ||
12521               element == EL_SHIELD_NORMAL ||
12522               element == EL_SHIELD_DEADLY) &&
12523              IS_ANIMATED(graphic))
12524       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12525     else if (IS_MOVING(x, y))
12526       ContinueMoving(x, y);
12527     else if (IS_ACTIVE_BOMB(element))
12528       CheckDynamite(x, y);
12529     else if (element == EL_AMOEBA_GROWING)
12530       AmoebaGrowing(x, y);
12531     else if (element == EL_AMOEBA_SHRINKING)
12532       AmoebaShrinking(x, y);
12533
12534 #if !USE_NEW_AMOEBA_CODE
12535     else if (IS_AMOEBALIVE(element))
12536       AmoebaReproduce(x, y);
12537 #endif
12538
12539     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12540       Life(x, y);
12541     else if (element == EL_EXIT_CLOSED)
12542       CheckExit(x, y);
12543     else if (element == EL_EM_EXIT_CLOSED)
12544       CheckExitEM(x, y);
12545     else if (element == EL_STEEL_EXIT_CLOSED)
12546       CheckExitSteel(x, y);
12547     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12548       CheckExitSteelEM(x, y);
12549     else if (element == EL_SP_EXIT_CLOSED)
12550       CheckExitSP(x, y);
12551     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12552              element == EL_EXPANDABLE_STEELWALL_GROWING)
12553       WallGrowing(x, y);
12554     else if (element == EL_EXPANDABLE_WALL ||
12555              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12556              element == EL_EXPANDABLE_WALL_VERTICAL ||
12557              element == EL_EXPANDABLE_WALL_ANY ||
12558              element == EL_BD_EXPANDABLE_WALL ||
12559              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12560              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12561              element == EL_EXPANDABLE_STEELWALL_ANY)
12562       CheckWallGrowing(x, y);
12563     else if (element == EL_FLAMES)
12564       CheckForDragon(x, y);
12565     else if (element == EL_EXPLOSION)
12566       ; // drawing of correct explosion animation is handled separately
12567     else if (element == EL_ELEMENT_SNAPPING ||
12568              element == EL_DIAGONAL_SHRINKING ||
12569              element == EL_DIAGONAL_GROWING)
12570     {
12571       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12572
12573       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12574     }
12575     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12576       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12577
12578     if (IS_BELT_ACTIVE(element))
12579       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12580
12581     if (game.magic_wall_active)
12582     {
12583       int jx = local_player->jx, jy = local_player->jy;
12584
12585       // play the element sound at the position nearest to the player
12586       if ((element == EL_MAGIC_WALL_FULL ||
12587            element == EL_MAGIC_WALL_ACTIVE ||
12588            element == EL_MAGIC_WALL_EMPTYING ||
12589            element == EL_BD_MAGIC_WALL_FULL ||
12590            element == EL_BD_MAGIC_WALL_ACTIVE ||
12591            element == EL_BD_MAGIC_WALL_EMPTYING ||
12592            element == EL_DC_MAGIC_WALL_FULL ||
12593            element == EL_DC_MAGIC_WALL_ACTIVE ||
12594            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12595           ABS(x - jx) + ABS(y - jy) <
12596           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12597       {
12598         magic_wall_x = x;
12599         magic_wall_y = y;
12600       }
12601     }
12602   }
12603
12604 #if USE_NEW_AMOEBA_CODE
12605   // new experimental amoeba growth stuff
12606   if (!(FrameCounter % 8))
12607   {
12608     static unsigned int random = 1684108901;
12609
12610     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12611     {
12612       x = RND(lev_fieldx);
12613       y = RND(lev_fieldy);
12614       element = Tile[x][y];
12615
12616       if (!IS_PLAYER(x, y) &&
12617           (element == EL_EMPTY ||
12618            CAN_GROW_INTO(element) ||
12619            element == EL_QUICKSAND_EMPTY ||
12620            element == EL_QUICKSAND_FAST_EMPTY ||
12621            element == EL_ACID_SPLASH_LEFT ||
12622            element == EL_ACID_SPLASH_RIGHT))
12623       {
12624         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12625             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12626             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12627             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12628           Tile[x][y] = EL_AMOEBA_DROP;
12629       }
12630
12631       random = random * 129 + 1;
12632     }
12633   }
12634 #endif
12635
12636   game.explosions_delayed = FALSE;
12637
12638   SCAN_PLAYFIELD(x, y)
12639   {
12640     element = Tile[x][y];
12641
12642     if (ExplodeField[x][y])
12643       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12644     else if (element == EL_EXPLOSION)
12645       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12646
12647     ExplodeField[x][y] = EX_TYPE_NONE;
12648   }
12649
12650   game.explosions_delayed = TRUE;
12651
12652   if (game.magic_wall_active)
12653   {
12654     if (!(game.magic_wall_time_left % 4))
12655     {
12656       int element = Tile[magic_wall_x][magic_wall_y];
12657
12658       if (element == EL_BD_MAGIC_WALL_FULL ||
12659           element == EL_BD_MAGIC_WALL_ACTIVE ||
12660           element == EL_BD_MAGIC_WALL_EMPTYING)
12661         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12662       else if (element == EL_DC_MAGIC_WALL_FULL ||
12663                element == EL_DC_MAGIC_WALL_ACTIVE ||
12664                element == EL_DC_MAGIC_WALL_EMPTYING)
12665         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12666       else
12667         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12668     }
12669
12670     if (game.magic_wall_time_left > 0)
12671     {
12672       game.magic_wall_time_left--;
12673
12674       if (!game.magic_wall_time_left)
12675       {
12676         SCAN_PLAYFIELD(x, y)
12677         {
12678           element = Tile[x][y];
12679
12680           if (element == EL_MAGIC_WALL_ACTIVE ||
12681               element == EL_MAGIC_WALL_FULL)
12682           {
12683             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12684             TEST_DrawLevelField(x, y);
12685           }
12686           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12687                    element == EL_BD_MAGIC_WALL_FULL)
12688           {
12689             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12690             TEST_DrawLevelField(x, y);
12691           }
12692           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12693                    element == EL_DC_MAGIC_WALL_FULL)
12694           {
12695             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12696             TEST_DrawLevelField(x, y);
12697           }
12698         }
12699
12700         game.magic_wall_active = FALSE;
12701       }
12702     }
12703   }
12704
12705   if (game.light_time_left > 0)
12706   {
12707     game.light_time_left--;
12708
12709     if (game.light_time_left == 0)
12710       RedrawAllLightSwitchesAndInvisibleElements();
12711   }
12712
12713   if (game.timegate_time_left > 0)
12714   {
12715     game.timegate_time_left--;
12716
12717     if (game.timegate_time_left == 0)
12718       CloseAllOpenTimegates();
12719   }
12720
12721   if (game.lenses_time_left > 0)
12722   {
12723     game.lenses_time_left--;
12724
12725     if (game.lenses_time_left == 0)
12726       RedrawAllInvisibleElementsForLenses();
12727   }
12728
12729   if (game.magnify_time_left > 0)
12730   {
12731     game.magnify_time_left--;
12732
12733     if (game.magnify_time_left == 0)
12734       RedrawAllInvisibleElementsForMagnifier();
12735   }
12736
12737   for (i = 0; i < MAX_PLAYERS; i++)
12738   {
12739     struct PlayerInfo *player = &stored_player[i];
12740
12741     if (SHIELD_ON(player))
12742     {
12743       if (player->shield_deadly_time_left)
12744         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12745       else if (player->shield_normal_time_left)
12746         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12747     }
12748   }
12749
12750 #if USE_DELAYED_GFX_REDRAW
12751   SCAN_PLAYFIELD(x, y)
12752   {
12753     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12754     {
12755       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12756          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12757
12758       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12759         DrawLevelField(x, y);
12760
12761       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12762         DrawLevelFieldCrumbled(x, y);
12763
12764       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12765         DrawLevelFieldCrumbledNeighbours(x, y);
12766
12767       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12768         DrawTwinkleOnField(x, y);
12769     }
12770
12771     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12772   }
12773 #endif
12774
12775   DrawAllPlayers();
12776   PlayAllPlayersSound();
12777
12778   for (i = 0; i < MAX_PLAYERS; i++)
12779   {
12780     struct PlayerInfo *player = &stored_player[i];
12781
12782     if (player->show_envelope != 0 && (!player->active ||
12783                                        player->MovPos == 0))
12784     {
12785       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12786
12787       player->show_envelope = 0;
12788     }
12789   }
12790
12791   // use random number generator in every frame to make it less predictable
12792   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12793     RND(1);
12794
12795   mouse_action_last = mouse_action;
12796 }
12797
12798 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12799 {
12800   int min_x = x, min_y = y, max_x = x, max_y = y;
12801   int scr_fieldx = getScreenFieldSizeX();
12802   int scr_fieldy = getScreenFieldSizeY();
12803   int i;
12804
12805   for (i = 0; i < MAX_PLAYERS; i++)
12806   {
12807     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12808
12809     if (!stored_player[i].active || &stored_player[i] == player)
12810       continue;
12811
12812     min_x = MIN(min_x, jx);
12813     min_y = MIN(min_y, jy);
12814     max_x = MAX(max_x, jx);
12815     max_y = MAX(max_y, jy);
12816   }
12817
12818   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12819 }
12820
12821 static boolean AllPlayersInVisibleScreen(void)
12822 {
12823   int i;
12824
12825   for (i = 0; i < MAX_PLAYERS; i++)
12826   {
12827     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12828
12829     if (!stored_player[i].active)
12830       continue;
12831
12832     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12833       return FALSE;
12834   }
12835
12836   return TRUE;
12837 }
12838
12839 void ScrollLevel(int dx, int dy)
12840 {
12841   int scroll_offset = 2 * TILEX_VAR;
12842   int x, y;
12843
12844   BlitBitmap(drawto_field, drawto_field,
12845              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12846              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12847              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12848              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12849              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12850              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12851
12852   if (dx != 0)
12853   {
12854     x = (dx == 1 ? BX1 : BX2);
12855     for (y = BY1; y <= BY2; y++)
12856       DrawScreenField(x, y);
12857   }
12858
12859   if (dy != 0)
12860   {
12861     y = (dy == 1 ? BY1 : BY2);
12862     for (x = BX1; x <= BX2; x++)
12863       DrawScreenField(x, y);
12864   }
12865
12866   redraw_mask |= REDRAW_FIELD;
12867 }
12868
12869 static boolean canFallDown(struct PlayerInfo *player)
12870 {
12871   int jx = player->jx, jy = player->jy;
12872
12873   return (IN_LEV_FIELD(jx, jy + 1) &&
12874           (IS_FREE(jx, jy + 1) ||
12875            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12876           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12877           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12878 }
12879
12880 static boolean canPassField(int x, int y, int move_dir)
12881 {
12882   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12883   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12884   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12885   int nextx = x + dx;
12886   int nexty = y + dy;
12887   int element = Tile[x][y];
12888
12889   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12890           !CAN_MOVE(element) &&
12891           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12892           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12893           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12894 }
12895
12896 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12897 {
12898   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12899   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12900   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12901   int newx = x + dx;
12902   int newy = y + dy;
12903
12904   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12905           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12906           (IS_DIGGABLE(Tile[newx][newy]) ||
12907            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12908            canPassField(newx, newy, move_dir)));
12909 }
12910
12911 static void CheckGravityMovement(struct PlayerInfo *player)
12912 {
12913   if (player->gravity && !player->programmed_action)
12914   {
12915     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12916     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12917     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12918     int jx = player->jx, jy = player->jy;
12919     boolean player_is_moving_to_valid_field =
12920       (!player_is_snapping &&
12921        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12922         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12923     boolean player_can_fall_down = canFallDown(player);
12924
12925     if (player_can_fall_down &&
12926         !player_is_moving_to_valid_field)
12927       player->programmed_action = MV_DOWN;
12928   }
12929 }
12930
12931 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12932 {
12933   return CheckGravityMovement(player);
12934
12935   if (player->gravity && !player->programmed_action)
12936   {
12937     int jx = player->jx, jy = player->jy;
12938     boolean field_under_player_is_free =
12939       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12940     boolean player_is_standing_on_valid_field =
12941       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12942        (IS_WALKABLE(Tile[jx][jy]) &&
12943         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12944
12945     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12946       player->programmed_action = MV_DOWN;
12947   }
12948 }
12949
12950 /*
12951   MovePlayerOneStep()
12952   -----------------------------------------------------------------------------
12953   dx, dy:               direction (non-diagonal) to try to move the player to
12954   real_dx, real_dy:     direction as read from input device (can be diagonal)
12955 */
12956
12957 boolean MovePlayerOneStep(struct PlayerInfo *player,
12958                           int dx, int dy, int real_dx, int real_dy)
12959 {
12960   int jx = player->jx, jy = player->jy;
12961   int new_jx = jx + dx, new_jy = jy + dy;
12962   int can_move;
12963   boolean player_can_move = !player->cannot_move;
12964
12965   if (!player->active || (!dx && !dy))
12966     return MP_NO_ACTION;
12967
12968   player->MovDir = (dx < 0 ? MV_LEFT :
12969                     dx > 0 ? MV_RIGHT :
12970                     dy < 0 ? MV_UP :
12971                     dy > 0 ? MV_DOWN :  MV_NONE);
12972
12973   if (!IN_LEV_FIELD(new_jx, new_jy))
12974     return MP_NO_ACTION;
12975
12976   if (!player_can_move)
12977   {
12978     if (player->MovPos == 0)
12979     {
12980       player->is_moving = FALSE;
12981       player->is_digging = FALSE;
12982       player->is_collecting = FALSE;
12983       player->is_snapping = FALSE;
12984       player->is_pushing = FALSE;
12985     }
12986   }
12987
12988   if (!network.enabled && game.centered_player_nr == -1 &&
12989       !AllPlayersInSight(player, new_jx, new_jy))
12990     return MP_NO_ACTION;
12991
12992   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12993   if (can_move != MP_MOVING)
12994     return can_move;
12995
12996   // check if DigField() has caused relocation of the player
12997   if (player->jx != jx || player->jy != jy)
12998     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12999
13000   StorePlayer[jx][jy] = 0;
13001   player->last_jx = jx;
13002   player->last_jy = jy;
13003   player->jx = new_jx;
13004   player->jy = new_jy;
13005   StorePlayer[new_jx][new_jy] = player->element_nr;
13006
13007   if (player->move_delay_value_next != -1)
13008   {
13009     player->move_delay_value = player->move_delay_value_next;
13010     player->move_delay_value_next = -1;
13011   }
13012
13013   player->MovPos =
13014     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13015
13016   player->step_counter++;
13017
13018   PlayerVisit[jx][jy] = FrameCounter;
13019
13020   player->is_moving = TRUE;
13021
13022 #if 1
13023   // should better be called in MovePlayer(), but this breaks some tapes
13024   ScrollPlayer(player, SCROLL_INIT);
13025 #endif
13026
13027   return MP_MOVING;
13028 }
13029
13030 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13031 {
13032   int jx = player->jx, jy = player->jy;
13033   int old_jx = jx, old_jy = jy;
13034   int moved = MP_NO_ACTION;
13035
13036   if (!player->active)
13037     return FALSE;
13038
13039   if (!dx && !dy)
13040   {
13041     if (player->MovPos == 0)
13042     {
13043       player->is_moving = FALSE;
13044       player->is_digging = FALSE;
13045       player->is_collecting = FALSE;
13046       player->is_snapping = FALSE;
13047       player->is_pushing = FALSE;
13048     }
13049
13050     return FALSE;
13051   }
13052
13053   if (player->move_delay > 0)
13054     return FALSE;
13055
13056   player->move_delay = -1;              // set to "uninitialized" value
13057
13058   // store if player is automatically moved to next field
13059   player->is_auto_moving = (player->programmed_action != MV_NONE);
13060
13061   // remove the last programmed player action
13062   player->programmed_action = 0;
13063
13064   if (player->MovPos)
13065   {
13066     // should only happen if pre-1.2 tape recordings are played
13067     // this is only for backward compatibility
13068
13069     int original_move_delay_value = player->move_delay_value;
13070
13071 #if DEBUG
13072     Debug("game:playing:MovePlayer",
13073           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13074           tape.counter);
13075 #endif
13076
13077     // scroll remaining steps with finest movement resolution
13078     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13079
13080     while (player->MovPos)
13081     {
13082       ScrollPlayer(player, SCROLL_GO_ON);
13083       ScrollScreen(NULL, SCROLL_GO_ON);
13084
13085       AdvanceFrameAndPlayerCounters(player->index_nr);
13086
13087       DrawAllPlayers();
13088       BackToFront_WithFrameDelay(0);
13089     }
13090
13091     player->move_delay_value = original_move_delay_value;
13092   }
13093
13094   player->is_active = FALSE;
13095
13096   if (player->last_move_dir & MV_HORIZONTAL)
13097   {
13098     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13099       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13100   }
13101   else
13102   {
13103     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13104       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13105   }
13106
13107   if (!moved && !player->is_active)
13108   {
13109     player->is_moving = FALSE;
13110     player->is_digging = FALSE;
13111     player->is_collecting = FALSE;
13112     player->is_snapping = FALSE;
13113     player->is_pushing = FALSE;
13114   }
13115
13116   jx = player->jx;
13117   jy = player->jy;
13118
13119   if (moved & MP_MOVING && !ScreenMovPos &&
13120       (player->index_nr == game.centered_player_nr ||
13121        game.centered_player_nr == -1))
13122   {
13123     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13124
13125     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13126     {
13127       // actual player has left the screen -- scroll in that direction
13128       if (jx != old_jx)         // player has moved horizontally
13129         scroll_x += (jx - old_jx);
13130       else                      // player has moved vertically
13131         scroll_y += (jy - old_jy);
13132     }
13133     else
13134     {
13135       int offset_raw = game.scroll_delay_value;
13136
13137       if (jx != old_jx)         // player has moved horizontally
13138       {
13139         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13140         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13141         int new_scroll_x = jx - MIDPOSX + offset_x;
13142
13143         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13144             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13145           scroll_x = new_scroll_x;
13146
13147         // don't scroll over playfield boundaries
13148         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13149
13150         // don't scroll more than one field at a time
13151         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13152
13153         // don't scroll against the player's moving direction
13154         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13155             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13156           scroll_x = old_scroll_x;
13157       }
13158       else                      // player has moved vertically
13159       {
13160         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13161         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13162         int new_scroll_y = jy - MIDPOSY + offset_y;
13163
13164         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13165             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13166           scroll_y = new_scroll_y;
13167
13168         // don't scroll over playfield boundaries
13169         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13170
13171         // don't scroll more than one field at a time
13172         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13173
13174         // don't scroll against the player's moving direction
13175         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13176             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13177           scroll_y = old_scroll_y;
13178       }
13179     }
13180
13181     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13182     {
13183       if (!network.enabled && game.centered_player_nr == -1 &&
13184           !AllPlayersInVisibleScreen())
13185       {
13186         scroll_x = old_scroll_x;
13187         scroll_y = old_scroll_y;
13188       }
13189       else
13190       {
13191         ScrollScreen(player, SCROLL_INIT);
13192         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13193       }
13194     }
13195   }
13196
13197   player->StepFrame = 0;
13198
13199   if (moved & MP_MOVING)
13200   {
13201     if (old_jx != jx && old_jy == jy)
13202       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13203     else if (old_jx == jx && old_jy != jy)
13204       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13205
13206     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13207
13208     player->last_move_dir = player->MovDir;
13209     player->is_moving = TRUE;
13210     player->is_snapping = FALSE;
13211     player->is_switching = FALSE;
13212     player->is_dropping = FALSE;
13213     player->is_dropping_pressed = FALSE;
13214     player->drop_pressed_delay = 0;
13215
13216 #if 0
13217     // should better be called here than above, but this breaks some tapes
13218     ScrollPlayer(player, SCROLL_INIT);
13219 #endif
13220   }
13221   else
13222   {
13223     CheckGravityMovementWhenNotMoving(player);
13224
13225     player->is_moving = FALSE;
13226
13227     /* at this point, the player is allowed to move, but cannot move right now
13228        (e.g. because of something blocking the way) -- ensure that the player
13229        is also allowed to move in the next frame (in old versions before 3.1.1,
13230        the player was forced to wait again for eight frames before next try) */
13231
13232     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13233       player->move_delay = 0;   // allow direct movement in the next frame
13234   }
13235
13236   if (player->move_delay == -1)         // not yet initialized by DigField()
13237     player->move_delay = player->move_delay_value;
13238
13239   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13240   {
13241     TestIfPlayerTouchesBadThing(jx, jy);
13242     TestIfPlayerTouchesCustomElement(jx, jy);
13243   }
13244
13245   if (!player->active)
13246     RemovePlayer(player);
13247
13248   return moved;
13249 }
13250
13251 void ScrollPlayer(struct PlayerInfo *player, int mode)
13252 {
13253   int jx = player->jx, jy = player->jy;
13254   int last_jx = player->last_jx, last_jy = player->last_jy;
13255   int move_stepsize = TILEX / player->move_delay_value;
13256
13257   if (!player->active)
13258     return;
13259
13260   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13261     return;
13262
13263   if (mode == SCROLL_INIT)
13264   {
13265     player->actual_frame_counter.count = FrameCounter;
13266     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13267
13268     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13269         Tile[last_jx][last_jy] == EL_EMPTY)
13270     {
13271       int last_field_block_delay = 0;   // start with no blocking at all
13272       int block_delay_adjustment = player->block_delay_adjustment;
13273
13274       // if player blocks last field, add delay for exactly one move
13275       if (player->block_last_field)
13276       {
13277         last_field_block_delay += player->move_delay_value;
13278
13279         // when blocking enabled, prevent moving up despite gravity
13280         if (player->gravity && player->MovDir == MV_UP)
13281           block_delay_adjustment = -1;
13282       }
13283
13284       // add block delay adjustment (also possible when not blocking)
13285       last_field_block_delay += block_delay_adjustment;
13286
13287       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13288       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13289     }
13290
13291     if (player->MovPos != 0)    // player has not yet reached destination
13292       return;
13293   }
13294   else if (!FrameReached(&player->actual_frame_counter))
13295     return;
13296
13297   if (player->MovPos != 0)
13298   {
13299     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13300     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13301
13302     // before DrawPlayer() to draw correct player graphic for this case
13303     if (player->MovPos == 0)
13304       CheckGravityMovement(player);
13305   }
13306
13307   if (player->MovPos == 0)      // player reached destination field
13308   {
13309     if (player->move_delay_reset_counter > 0)
13310     {
13311       player->move_delay_reset_counter--;
13312
13313       if (player->move_delay_reset_counter == 0)
13314       {
13315         // continue with normal speed after quickly moving through gate
13316         HALVE_PLAYER_SPEED(player);
13317
13318         // be able to make the next move without delay
13319         player->move_delay = 0;
13320       }
13321     }
13322
13323     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13324         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13325         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13326         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13327         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13328         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13329         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13330         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13331     {
13332       ExitPlayer(player);
13333
13334       if (game.players_still_needed == 0 &&
13335           (game.friends_still_needed == 0 ||
13336            IS_SP_ELEMENT(Tile[jx][jy])))
13337         LevelSolved();
13338     }
13339
13340     player->last_jx = jx;
13341     player->last_jy = jy;
13342
13343     // this breaks one level: "machine", level 000
13344     {
13345       int move_direction = player->MovDir;
13346       int enter_side = MV_DIR_OPPOSITE(move_direction);
13347       int leave_side = move_direction;
13348       int old_jx = last_jx;
13349       int old_jy = last_jy;
13350       int old_element = Tile[old_jx][old_jy];
13351       int new_element = Tile[jx][jy];
13352
13353       if (IS_CUSTOM_ELEMENT(old_element))
13354         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13355                                    CE_LEFT_BY_PLAYER,
13356                                    player->index_bit, leave_side);
13357
13358       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13359                                           CE_PLAYER_LEAVES_X,
13360                                           player->index_bit, leave_side);
13361
13362       // needed because pushed element has not yet reached its destination,
13363       // so it would trigger a change event at its previous field location
13364       if (!player->is_pushing)
13365       {
13366         if (IS_CUSTOM_ELEMENT(new_element))
13367           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13368                                      player->index_bit, enter_side);
13369
13370         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13371                                             CE_PLAYER_ENTERS_X,
13372                                             player->index_bit, enter_side);
13373       }
13374
13375       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13376                                         CE_MOVE_OF_X, move_direction);
13377     }
13378
13379     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13380     {
13381       TestIfPlayerTouchesBadThing(jx, jy);
13382       TestIfPlayerTouchesCustomElement(jx, jy);
13383
13384       // needed because pushed element has not yet reached its destination,
13385       // so it would trigger a change event at its previous field location
13386       if (!player->is_pushing)
13387         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13388
13389       if (level.finish_dig_collect &&
13390           (player->is_digging || player->is_collecting))
13391       {
13392         int last_element = player->last_removed_element;
13393         int move_direction = player->MovDir;
13394         int enter_side = MV_DIR_OPPOSITE(move_direction);
13395         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13396                             CE_PLAYER_COLLECTS_X);
13397
13398         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13399                                             player->index_bit, enter_side);
13400
13401         player->last_removed_element = EL_UNDEFINED;
13402       }
13403
13404       if (!player->active)
13405         RemovePlayer(player);
13406     }
13407
13408     if (level.use_step_counter)
13409       CheckLevelTime_StepCounter();
13410
13411     if (tape.single_step && tape.recording && !tape.pausing &&
13412         !player->programmed_action)
13413       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13414
13415     if (!player->programmed_action)
13416       CheckSaveEngineSnapshot(player);
13417   }
13418 }
13419
13420 void ScrollScreen(struct PlayerInfo *player, int mode)
13421 {
13422   static DelayCounter screen_frame_counter = { 0 };
13423
13424   if (mode == SCROLL_INIT)
13425   {
13426     // set scrolling step size according to actual player's moving speed
13427     ScrollStepSize = TILEX / player->move_delay_value;
13428
13429     screen_frame_counter.count = FrameCounter;
13430     screen_frame_counter.value = 1;
13431
13432     ScreenMovDir = player->MovDir;
13433     ScreenMovPos = player->MovPos;
13434     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13435     return;
13436   }
13437   else if (!FrameReached(&screen_frame_counter))
13438     return;
13439
13440   if (ScreenMovPos)
13441   {
13442     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13443     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13444     redraw_mask |= REDRAW_FIELD;
13445   }
13446   else
13447     ScreenMovDir = MV_NONE;
13448 }
13449
13450 void CheckNextToConditions(int x, int y)
13451 {
13452   int element = Tile[x][y];
13453
13454   if (IS_PLAYER(x, y))
13455     TestIfPlayerNextToCustomElement(x, y);
13456
13457   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13458       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13459     TestIfElementNextToCustomElement(x, y);
13460 }
13461
13462 void TestIfPlayerNextToCustomElement(int x, int y)
13463 {
13464   struct XY *xy = xy_topdown;
13465   static int trigger_sides[4][2] =
13466   {
13467     // center side       border side
13468     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13469     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13470     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13471     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13472   };
13473   int i;
13474
13475   if (!IS_PLAYER(x, y))
13476     return;
13477
13478   struct PlayerInfo *player = PLAYERINFO(x, y);
13479
13480   if (player->is_moving)
13481     return;
13482
13483   for (i = 0; i < NUM_DIRECTIONS; i++)
13484   {
13485     int xx = x + xy[i].x;
13486     int yy = y + xy[i].y;
13487     int border_side = trigger_sides[i][1];
13488     int border_element;
13489
13490     if (!IN_LEV_FIELD(xx, yy))
13491       continue;
13492
13493     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13494       continue;         // center and border element not connected
13495
13496     border_element = Tile[xx][yy];
13497
13498     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13499                                player->index_bit, border_side);
13500     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13501                                         CE_PLAYER_NEXT_TO_X,
13502                                         player->index_bit, border_side);
13503
13504     /* use player element that is initially defined in the level playfield,
13505        not the player element that corresponds to the runtime player number
13506        (example: a level that contains EL_PLAYER_3 as the only player would
13507        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13508
13509     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13510                              CE_NEXT_TO_X, border_side);
13511   }
13512 }
13513
13514 void TestIfPlayerTouchesCustomElement(int x, int y)
13515 {
13516   struct XY *xy = xy_topdown;
13517   static int trigger_sides[4][2] =
13518   {
13519     // center side       border side
13520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13524   };
13525   static int touch_dir[4] =
13526   {
13527     MV_LEFT | MV_RIGHT,
13528     MV_UP   | MV_DOWN,
13529     MV_UP   | MV_DOWN,
13530     MV_LEFT | MV_RIGHT
13531   };
13532   int center_element = Tile[x][y];      // should always be non-moving!
13533   int i;
13534
13535   for (i = 0; i < NUM_DIRECTIONS; i++)
13536   {
13537     int xx = x + xy[i].x;
13538     int yy = y + xy[i].y;
13539     int center_side = trigger_sides[i][0];
13540     int border_side = trigger_sides[i][1];
13541     int border_element;
13542
13543     if (!IN_LEV_FIELD(xx, yy))
13544       continue;
13545
13546     if (IS_PLAYER(x, y))                // player found at center element
13547     {
13548       struct PlayerInfo *player = PLAYERINFO(x, y);
13549
13550       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13551         border_element = Tile[xx][yy];          // may be moving!
13552       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13553         border_element = Tile[xx][yy];
13554       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13555         border_element = MovingOrBlocked2Element(xx, yy);
13556       else
13557         continue;               // center and border element do not touch
13558
13559       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13560                                  player->index_bit, border_side);
13561       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13562                                           CE_PLAYER_TOUCHES_X,
13563                                           player->index_bit, border_side);
13564
13565       {
13566         /* use player element that is initially defined in the level playfield,
13567            not the player element that corresponds to the runtime player number
13568            (example: a level that contains EL_PLAYER_3 as the only player would
13569            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13570         int player_element = PLAYERINFO(x, y)->initial_element;
13571
13572         // as element "X" is the player here, check opposite (center) side
13573         CheckElementChangeBySide(xx, yy, border_element, player_element,
13574                                  CE_TOUCHING_X, center_side);
13575       }
13576     }
13577     else if (IS_PLAYER(xx, yy))         // player found at border element
13578     {
13579       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13580
13581       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13582       {
13583         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13584           continue;             // center and border element do not touch
13585       }
13586
13587       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13588                                  player->index_bit, center_side);
13589       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13590                                           CE_PLAYER_TOUCHES_X,
13591                                           player->index_bit, center_side);
13592
13593       {
13594         /* use player element that is initially defined in the level playfield,
13595            not the player element that corresponds to the runtime player number
13596            (example: a level that contains EL_PLAYER_3 as the only player would
13597            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13598         int player_element = PLAYERINFO(xx, yy)->initial_element;
13599
13600         // as element "X" is the player here, check opposite (border) side
13601         CheckElementChangeBySide(x, y, center_element, player_element,
13602                                  CE_TOUCHING_X, border_side);
13603       }
13604
13605       break;
13606     }
13607   }
13608 }
13609
13610 void TestIfElementNextToCustomElement(int x, int y)
13611 {
13612   struct XY *xy = xy_topdown;
13613   static int trigger_sides[4][2] =
13614   {
13615     // center side      border side
13616     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13617     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13618     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13619     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13620   };
13621   int center_element = Tile[x][y];      // should always be non-moving!
13622   int i;
13623
13624   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13625     return;
13626
13627   for (i = 0; i < NUM_DIRECTIONS; i++)
13628   {
13629     int xx = x + xy[i].x;
13630     int yy = y + xy[i].y;
13631     int border_side = trigger_sides[i][1];
13632     int border_element;
13633
13634     if (!IN_LEV_FIELD(xx, yy))
13635       continue;
13636
13637     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13638       continue;                 // center and border element not connected
13639
13640     border_element = Tile[xx][yy];
13641
13642     // check for change of center element (but change it only once)
13643     if (CheckElementChangeBySide(x, y, center_element, border_element,
13644                                  CE_NEXT_TO_X, border_side))
13645       break;
13646   }
13647 }
13648
13649 void TestIfElementTouchesCustomElement(int x, int y)
13650 {
13651   struct XY *xy = xy_topdown;
13652   static int trigger_sides[4][2] =
13653   {
13654     // center side      border side
13655     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13656     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13657     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13658     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13659   };
13660   static int touch_dir[4] =
13661   {
13662     MV_LEFT | MV_RIGHT,
13663     MV_UP   | MV_DOWN,
13664     MV_UP   | MV_DOWN,
13665     MV_LEFT | MV_RIGHT
13666   };
13667   boolean change_center_element = FALSE;
13668   int center_element = Tile[x][y];      // should always be non-moving!
13669   int border_element_old[NUM_DIRECTIONS];
13670   int i;
13671
13672   for (i = 0; i < NUM_DIRECTIONS; i++)
13673   {
13674     int xx = x + xy[i].x;
13675     int yy = y + xy[i].y;
13676     int border_element;
13677
13678     border_element_old[i] = -1;
13679
13680     if (!IN_LEV_FIELD(xx, yy))
13681       continue;
13682
13683     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13684       border_element = Tile[xx][yy];    // may be moving!
13685     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13686       border_element = Tile[xx][yy];
13687     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13688       border_element = MovingOrBlocked2Element(xx, yy);
13689     else
13690       continue;                 // center and border element do not touch
13691
13692     border_element_old[i] = border_element;
13693   }
13694
13695   for (i = 0; i < NUM_DIRECTIONS; i++)
13696   {
13697     int xx = x + xy[i].x;
13698     int yy = y + xy[i].y;
13699     int center_side = trigger_sides[i][0];
13700     int border_element = border_element_old[i];
13701
13702     if (border_element == -1)
13703       continue;
13704
13705     // check for change of border element
13706     CheckElementChangeBySide(xx, yy, border_element, center_element,
13707                              CE_TOUCHING_X, center_side);
13708
13709     // (center element cannot be player, so we don't have to check this here)
13710   }
13711
13712   for (i = 0; i < NUM_DIRECTIONS; i++)
13713   {
13714     int xx = x + xy[i].x;
13715     int yy = y + xy[i].y;
13716     int border_side = trigger_sides[i][1];
13717     int border_element = border_element_old[i];
13718
13719     if (border_element == -1)
13720       continue;
13721
13722     // check for change of center element (but change it only once)
13723     if (!change_center_element)
13724       change_center_element =
13725         CheckElementChangeBySide(x, y, center_element, border_element,
13726                                  CE_TOUCHING_X, border_side);
13727
13728     if (IS_PLAYER(xx, yy))
13729     {
13730       /* use player element that is initially defined in the level playfield,
13731          not the player element that corresponds to the runtime player number
13732          (example: a level that contains EL_PLAYER_3 as the only player would
13733          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13734       int player_element = PLAYERINFO(xx, yy)->initial_element;
13735
13736       // as element "X" is the player here, check opposite (border) side
13737       CheckElementChangeBySide(x, y, center_element, player_element,
13738                                CE_TOUCHING_X, border_side);
13739     }
13740   }
13741 }
13742
13743 void TestIfElementHitsCustomElement(int x, int y, int direction)
13744 {
13745   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13746   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13747   int hitx = x + dx, hity = y + dy;
13748   int hitting_element = Tile[x][y];
13749   int touched_element;
13750
13751   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13752     return;
13753
13754   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13755                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13756
13757   if (IN_LEV_FIELD(hitx, hity))
13758   {
13759     int opposite_direction = MV_DIR_OPPOSITE(direction);
13760     int hitting_side = direction;
13761     int touched_side = opposite_direction;
13762     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13763                           MovDir[hitx][hity] != direction ||
13764                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13765
13766     object_hit = TRUE;
13767
13768     if (object_hit)
13769     {
13770       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13771                                CE_HITTING_X, touched_side);
13772
13773       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13774                                CE_HIT_BY_X, hitting_side);
13775
13776       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13777                                CE_HIT_BY_SOMETHING, opposite_direction);
13778
13779       if (IS_PLAYER(hitx, hity))
13780       {
13781         /* use player element that is initially defined in the level playfield,
13782            not the player element that corresponds to the runtime player number
13783            (example: a level that contains EL_PLAYER_3 as the only player would
13784            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13785         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13786
13787         CheckElementChangeBySide(x, y, hitting_element, player_element,
13788                                  CE_HITTING_X, touched_side);
13789       }
13790     }
13791   }
13792
13793   // "hitting something" is also true when hitting the playfield border
13794   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13795                            CE_HITTING_SOMETHING, direction);
13796 }
13797
13798 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13799 {
13800   int i, kill_x = -1, kill_y = -1;
13801
13802   int bad_element = -1;
13803   struct XY *test_xy = xy_topdown;
13804   static int test_dir[4] =
13805   {
13806     MV_UP,
13807     MV_LEFT,
13808     MV_RIGHT,
13809     MV_DOWN
13810   };
13811
13812   for (i = 0; i < NUM_DIRECTIONS; i++)
13813   {
13814     int test_x, test_y, test_move_dir, test_element;
13815
13816     test_x = good_x + test_xy[i].x;
13817     test_y = good_y + test_xy[i].y;
13818
13819     if (!IN_LEV_FIELD(test_x, test_y))
13820       continue;
13821
13822     test_move_dir =
13823       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13824
13825     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13826
13827     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13828        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13829     */
13830     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13831         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13832     {
13833       kill_x = test_x;
13834       kill_y = test_y;
13835       bad_element = test_element;
13836
13837       break;
13838     }
13839   }
13840
13841   if (kill_x != -1 || kill_y != -1)
13842   {
13843     if (IS_PLAYER(good_x, good_y))
13844     {
13845       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13846
13847       if (player->shield_deadly_time_left > 0 &&
13848           !IS_INDESTRUCTIBLE(bad_element))
13849         Bang(kill_x, kill_y);
13850       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13851         KillPlayer(player);
13852     }
13853     else
13854       Bang(good_x, good_y);
13855   }
13856 }
13857
13858 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13859 {
13860   int i, kill_x = -1, kill_y = -1;
13861   int bad_element = Tile[bad_x][bad_y];
13862   struct XY *test_xy = xy_topdown;
13863   static int touch_dir[4] =
13864   {
13865     MV_LEFT | MV_RIGHT,
13866     MV_UP   | MV_DOWN,
13867     MV_UP   | MV_DOWN,
13868     MV_LEFT | MV_RIGHT
13869   };
13870   static int test_dir[4] =
13871   {
13872     MV_UP,
13873     MV_LEFT,
13874     MV_RIGHT,
13875     MV_DOWN
13876   };
13877
13878   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13879     return;
13880
13881   for (i = 0; i < NUM_DIRECTIONS; i++)
13882   {
13883     int test_x, test_y, test_move_dir, test_element;
13884
13885     test_x = bad_x + test_xy[i].x;
13886     test_y = bad_y + test_xy[i].y;
13887
13888     if (!IN_LEV_FIELD(test_x, test_y))
13889       continue;
13890
13891     test_move_dir =
13892       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13893
13894     test_element = Tile[test_x][test_y];
13895
13896     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13897        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13898     */
13899     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13900         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13901     {
13902       // good thing is player or penguin that does not move away
13903       if (IS_PLAYER(test_x, test_y))
13904       {
13905         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13906
13907         if (bad_element == EL_ROBOT && player->is_moving)
13908           continue;     // robot does not kill player if he is moving
13909
13910         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13911         {
13912           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13913             continue;           // center and border element do not touch
13914         }
13915
13916         kill_x = test_x;
13917         kill_y = test_y;
13918
13919         break;
13920       }
13921       else if (test_element == EL_PENGUIN)
13922       {
13923         kill_x = test_x;
13924         kill_y = test_y;
13925
13926         break;
13927       }
13928     }
13929   }
13930
13931   if (kill_x != -1 || kill_y != -1)
13932   {
13933     if (IS_PLAYER(kill_x, kill_y))
13934     {
13935       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13936
13937       if (player->shield_deadly_time_left > 0 &&
13938           !IS_INDESTRUCTIBLE(bad_element))
13939         Bang(bad_x, bad_y);
13940       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13941         KillPlayer(player);
13942     }
13943     else
13944       Bang(kill_x, kill_y);
13945   }
13946 }
13947
13948 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13949 {
13950   int bad_element = Tile[bad_x][bad_y];
13951   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13952   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13953   int test_x = bad_x + dx, test_y = bad_y + dy;
13954   int test_move_dir, test_element;
13955   int kill_x = -1, kill_y = -1;
13956
13957   if (!IN_LEV_FIELD(test_x, test_y))
13958     return;
13959
13960   test_move_dir =
13961     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13962
13963   test_element = Tile[test_x][test_y];
13964
13965   if (test_move_dir != bad_move_dir)
13966   {
13967     // good thing can be player or penguin that does not move away
13968     if (IS_PLAYER(test_x, test_y))
13969     {
13970       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13971
13972       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13973          player as being hit when he is moving towards the bad thing, because
13974          the "get hit by" condition would be lost after the player stops) */
13975       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13976         return;         // player moves away from bad thing
13977
13978       kill_x = test_x;
13979       kill_y = test_y;
13980     }
13981     else if (test_element == EL_PENGUIN)
13982     {
13983       kill_x = test_x;
13984       kill_y = test_y;
13985     }
13986   }
13987
13988   if (kill_x != -1 || kill_y != -1)
13989   {
13990     if (IS_PLAYER(kill_x, kill_y))
13991     {
13992       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13993
13994       if (player->shield_deadly_time_left > 0 &&
13995           !IS_INDESTRUCTIBLE(bad_element))
13996         Bang(bad_x, bad_y);
13997       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13998         KillPlayer(player);
13999     }
14000     else
14001       Bang(kill_x, kill_y);
14002   }
14003 }
14004
14005 void TestIfPlayerTouchesBadThing(int x, int y)
14006 {
14007   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14008 }
14009
14010 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14011 {
14012   TestIfGoodThingHitsBadThing(x, y, move_dir);
14013 }
14014
14015 void TestIfBadThingTouchesPlayer(int x, int y)
14016 {
14017   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14018 }
14019
14020 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14021 {
14022   TestIfBadThingHitsGoodThing(x, y, move_dir);
14023 }
14024
14025 void TestIfFriendTouchesBadThing(int x, int y)
14026 {
14027   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14028 }
14029
14030 void TestIfBadThingTouchesFriend(int x, int y)
14031 {
14032   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14033 }
14034
14035 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14036 {
14037   int i, kill_x = bad_x, kill_y = bad_y;
14038   struct XY *xy = xy_topdown;
14039
14040   for (i = 0; i < NUM_DIRECTIONS; i++)
14041   {
14042     int x, y, element;
14043
14044     x = bad_x + xy[i].x;
14045     y = bad_y + xy[i].y;
14046     if (!IN_LEV_FIELD(x, y))
14047       continue;
14048
14049     element = Tile[x][y];
14050     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14051         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14052     {
14053       kill_x = x;
14054       kill_y = y;
14055       break;
14056     }
14057   }
14058
14059   if (kill_x != bad_x || kill_y != bad_y)
14060     Bang(bad_x, bad_y);
14061 }
14062
14063 void KillPlayer(struct PlayerInfo *player)
14064 {
14065   int jx = player->jx, jy = player->jy;
14066
14067   if (!player->active)
14068     return;
14069
14070 #if 0
14071   Debug("game:playing:KillPlayer",
14072         "0: killed == %d, active == %d, reanimated == %d",
14073         player->killed, player->active, player->reanimated);
14074 #endif
14075
14076   /* the following code was introduced to prevent an infinite loop when calling
14077      -> Bang()
14078      -> CheckTriggeredElementChangeExt()
14079      -> ExecuteCustomElementAction()
14080      -> KillPlayer()
14081      -> (infinitely repeating the above sequence of function calls)
14082      which occurs when killing the player while having a CE with the setting
14083      "kill player X when explosion of <player X>"; the solution using a new
14084      field "player->killed" was chosen for backwards compatibility, although
14085      clever use of the fields "player->active" etc. would probably also work */
14086 #if 1
14087   if (player->killed)
14088     return;
14089 #endif
14090
14091   player->killed = TRUE;
14092
14093   // remove accessible field at the player's position
14094   RemoveField(jx, jy);
14095
14096   // deactivate shield (else Bang()/Explode() would not work right)
14097   player->shield_normal_time_left = 0;
14098   player->shield_deadly_time_left = 0;
14099
14100 #if 0
14101   Debug("game:playing:KillPlayer",
14102         "1: killed == %d, active == %d, reanimated == %d",
14103         player->killed, player->active, player->reanimated);
14104 #endif
14105
14106   Bang(jx, jy);
14107
14108 #if 0
14109   Debug("game:playing:KillPlayer",
14110         "2: killed == %d, active == %d, reanimated == %d",
14111         player->killed, player->active, player->reanimated);
14112 #endif
14113
14114   if (player->reanimated)       // killed player may have been reanimated
14115     player->killed = player->reanimated = FALSE;
14116   else
14117     BuryPlayer(player);
14118 }
14119
14120 static void KillPlayerUnlessEnemyProtected(int x, int y)
14121 {
14122   if (!PLAYER_ENEMY_PROTECTED(x, y))
14123     KillPlayer(PLAYERINFO(x, y));
14124 }
14125
14126 static void KillPlayerUnlessExplosionProtected(int x, int y)
14127 {
14128   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14129     KillPlayer(PLAYERINFO(x, y));
14130 }
14131
14132 void BuryPlayer(struct PlayerInfo *player)
14133 {
14134   int jx = player->jx, jy = player->jy;
14135
14136   if (!player->active)
14137     return;
14138
14139   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14140
14141   RemovePlayer(player);
14142
14143   player->buried = TRUE;
14144
14145   if (game.all_players_gone)
14146     game.GameOver = TRUE;
14147 }
14148
14149 void RemovePlayer(struct PlayerInfo *player)
14150 {
14151   int jx = player->jx, jy = player->jy;
14152   int i, found = FALSE;
14153
14154   player->present = FALSE;
14155   player->active = FALSE;
14156
14157   // required for some CE actions (even if the player is not active anymore)
14158   player->MovPos = 0;
14159
14160   if (!ExplodeField[jx][jy])
14161     StorePlayer[jx][jy] = 0;
14162
14163   if (player->is_moving)
14164     TEST_DrawLevelField(player->last_jx, player->last_jy);
14165
14166   for (i = 0; i < MAX_PLAYERS; i++)
14167     if (stored_player[i].active)
14168       found = TRUE;
14169
14170   if (!found)
14171   {
14172     game.all_players_gone = TRUE;
14173     game.GameOver = TRUE;
14174   }
14175
14176   game.exit_x = game.robot_wheel_x = jx;
14177   game.exit_y = game.robot_wheel_y = jy;
14178 }
14179
14180 void ExitPlayer(struct PlayerInfo *player)
14181 {
14182   DrawPlayer(player);   // needed here only to cleanup last field
14183   RemovePlayer(player);
14184
14185   if (game.players_still_needed > 0)
14186     game.players_still_needed--;
14187 }
14188
14189 static void SetFieldForSnapping(int x, int y, int element, int direction,
14190                                 int player_index_bit)
14191 {
14192   struct ElementInfo *ei = &element_info[element];
14193   int direction_bit = MV_DIR_TO_BIT(direction);
14194   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14195   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14196                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14197
14198   Tile[x][y] = EL_ELEMENT_SNAPPING;
14199   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14200   MovDir[x][y] = direction;
14201   Store[x][y] = element;
14202   Store2[x][y] = player_index_bit;
14203
14204   ResetGfxAnimation(x, y);
14205
14206   GfxElement[x][y] = element;
14207   GfxAction[x][y] = action;
14208   GfxDir[x][y] = direction;
14209   GfxFrame[x][y] = -1;
14210 }
14211
14212 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14213                                    int player_index_bit)
14214 {
14215   TestIfElementTouchesCustomElement(x, y);      // for empty space
14216
14217   if (level.finish_dig_collect)
14218   {
14219     int dig_side = MV_DIR_OPPOSITE(direction);
14220     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14221                         CE_PLAYER_COLLECTS_X);
14222
14223     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14224                                         player_index_bit, dig_side);
14225     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14226                                         player_index_bit, dig_side);
14227   }
14228 }
14229
14230 /*
14231   =============================================================================
14232   checkDiagonalPushing()
14233   -----------------------------------------------------------------------------
14234   check if diagonal input device direction results in pushing of object
14235   (by checking if the alternative direction is walkable, diggable, ...)
14236   =============================================================================
14237 */
14238
14239 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14240                                     int x, int y, int real_dx, int real_dy)
14241 {
14242   int jx, jy, dx, dy, xx, yy;
14243
14244   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14245     return TRUE;
14246
14247   // diagonal direction: check alternative direction
14248   jx = player->jx;
14249   jy = player->jy;
14250   dx = x - jx;
14251   dy = y - jy;
14252   xx = jx + (dx == 0 ? real_dx : 0);
14253   yy = jy + (dy == 0 ? real_dy : 0);
14254
14255   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14256 }
14257
14258 /*
14259   =============================================================================
14260   DigField()
14261   -----------------------------------------------------------------------------
14262   x, y:                 field next to player (non-diagonal) to try to dig to
14263   real_dx, real_dy:     direction as read from input device (can be diagonal)
14264   =============================================================================
14265 */
14266
14267 static int DigField(struct PlayerInfo *player,
14268                     int oldx, int oldy, int x, int y,
14269                     int real_dx, int real_dy, int mode)
14270 {
14271   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14272   boolean player_was_pushing = player->is_pushing;
14273   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14274   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14275   int jx = oldx, jy = oldy;
14276   int dx = x - jx, dy = y - jy;
14277   int nextx = x + dx, nexty = y + dy;
14278   int move_direction = (dx == -1 ? MV_LEFT  :
14279                         dx == +1 ? MV_RIGHT :
14280                         dy == -1 ? MV_UP    :
14281                         dy == +1 ? MV_DOWN  : MV_NONE);
14282   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14283   int dig_side = MV_DIR_OPPOSITE(move_direction);
14284   int old_element = Tile[jx][jy];
14285   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14286   int collect_count;
14287
14288   if (is_player)                // function can also be called by EL_PENGUIN
14289   {
14290     if (player->MovPos == 0)
14291     {
14292       player->is_digging = FALSE;
14293       player->is_collecting = FALSE;
14294     }
14295
14296     if (player->MovPos == 0)    // last pushing move finished
14297       player->is_pushing = FALSE;
14298
14299     if (mode == DF_NO_PUSH)     // player just stopped pushing
14300     {
14301       player->is_switching = FALSE;
14302       player->push_delay = -1;
14303
14304       return MP_NO_ACTION;
14305     }
14306   }
14307   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14308     old_element = Back[jx][jy];
14309
14310   // in case of element dropped at player position, check background
14311   else if (Back[jx][jy] != EL_EMPTY &&
14312            game.engine_version >= VERSION_IDENT(2,2,0,0))
14313     old_element = Back[jx][jy];
14314
14315   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14316     return MP_NO_ACTION;        // field has no opening in this direction
14317
14318   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14319     return MP_NO_ACTION;        // field has no opening in this direction
14320
14321   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14322   {
14323     SplashAcid(x, y);
14324
14325     Tile[jx][jy] = player->artwork_element;
14326     InitMovingField(jx, jy, MV_DOWN);
14327     Store[jx][jy] = EL_ACID;
14328     ContinueMoving(jx, jy);
14329     BuryPlayer(player);
14330
14331     return MP_DONT_RUN_INTO;
14332   }
14333
14334   if (player_can_move && DONT_RUN_INTO(element))
14335   {
14336     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14337
14338     return MP_DONT_RUN_INTO;
14339   }
14340
14341   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14342     return MP_NO_ACTION;
14343
14344   collect_count = element_info[element].collect_count_initial;
14345
14346   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14347     return MP_NO_ACTION;
14348
14349   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14350     player_can_move = player_can_move_or_snap;
14351
14352   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14353       game.engine_version >= VERSION_IDENT(2,2,0,0))
14354   {
14355     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14356                                player->index_bit, dig_side);
14357     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14358                                         player->index_bit, dig_side);
14359
14360     if (element == EL_DC_LANDMINE)
14361       Bang(x, y);
14362
14363     if (Tile[x][y] != element)          // field changed by snapping
14364       return MP_ACTION;
14365
14366     return MP_NO_ACTION;
14367   }
14368
14369   if (player->gravity && is_player && !player->is_auto_moving &&
14370       canFallDown(player) && move_direction != MV_DOWN &&
14371       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14372     return MP_NO_ACTION;        // player cannot walk here due to gravity
14373
14374   if (player_can_move &&
14375       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14376   {
14377     int sound_element = SND_ELEMENT(element);
14378     int sound_action = ACTION_WALKING;
14379
14380     if (IS_RND_GATE(element))
14381     {
14382       if (!player->key[RND_GATE_NR(element)])
14383         return MP_NO_ACTION;
14384     }
14385     else if (IS_RND_GATE_GRAY(element))
14386     {
14387       if (!player->key[RND_GATE_GRAY_NR(element)])
14388         return MP_NO_ACTION;
14389     }
14390     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14391     {
14392       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14393         return MP_NO_ACTION;
14394     }
14395     else if (element == EL_EXIT_OPEN ||
14396              element == EL_EM_EXIT_OPEN ||
14397              element == EL_EM_EXIT_OPENING ||
14398              element == EL_STEEL_EXIT_OPEN ||
14399              element == EL_EM_STEEL_EXIT_OPEN ||
14400              element == EL_EM_STEEL_EXIT_OPENING ||
14401              element == EL_SP_EXIT_OPEN ||
14402              element == EL_SP_EXIT_OPENING)
14403     {
14404       sound_action = ACTION_PASSING;    // player is passing exit
14405     }
14406     else if (element == EL_EMPTY)
14407     {
14408       sound_action = ACTION_MOVING;             // nothing to walk on
14409     }
14410
14411     // play sound from background or player, whatever is available
14412     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14413       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14414     else
14415       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14416   }
14417   else if (player_can_move &&
14418            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14419   {
14420     if (!ACCESS_FROM(element, opposite_direction))
14421       return MP_NO_ACTION;      // field not accessible from this direction
14422
14423     if (CAN_MOVE(element))      // only fixed elements can be passed!
14424       return MP_NO_ACTION;
14425
14426     if (IS_EM_GATE(element))
14427     {
14428       if (!player->key[EM_GATE_NR(element)])
14429         return MP_NO_ACTION;
14430     }
14431     else if (IS_EM_GATE_GRAY(element))
14432     {
14433       if (!player->key[EM_GATE_GRAY_NR(element)])
14434         return MP_NO_ACTION;
14435     }
14436     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14437     {
14438       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14439         return MP_NO_ACTION;
14440     }
14441     else if (IS_EMC_GATE(element))
14442     {
14443       if (!player->key[EMC_GATE_NR(element)])
14444         return MP_NO_ACTION;
14445     }
14446     else if (IS_EMC_GATE_GRAY(element))
14447     {
14448       if (!player->key[EMC_GATE_GRAY_NR(element)])
14449         return MP_NO_ACTION;
14450     }
14451     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14452     {
14453       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14454         return MP_NO_ACTION;
14455     }
14456     else if (element == EL_DC_GATE_WHITE ||
14457              element == EL_DC_GATE_WHITE_GRAY ||
14458              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14459     {
14460       if (player->num_white_keys == 0)
14461         return MP_NO_ACTION;
14462
14463       player->num_white_keys--;
14464     }
14465     else if (IS_SP_PORT(element))
14466     {
14467       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14468           element == EL_SP_GRAVITY_PORT_RIGHT ||
14469           element == EL_SP_GRAVITY_PORT_UP ||
14470           element == EL_SP_GRAVITY_PORT_DOWN)
14471         player->gravity = !player->gravity;
14472       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14473                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14474                element == EL_SP_GRAVITY_ON_PORT_UP ||
14475                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14476         player->gravity = TRUE;
14477       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14478                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14479                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14480                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14481         player->gravity = FALSE;
14482     }
14483
14484     // automatically move to the next field with double speed
14485     player->programmed_action = move_direction;
14486
14487     if (player->move_delay_reset_counter == 0)
14488     {
14489       player->move_delay_reset_counter = 2;     // two double speed steps
14490
14491       DOUBLE_PLAYER_SPEED(player);
14492     }
14493
14494     PlayLevelSoundAction(x, y, ACTION_PASSING);
14495   }
14496   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14497   {
14498     RemoveField(x, y);
14499
14500     if (mode != DF_SNAP)
14501     {
14502       GfxElement[x][y] = GFX_ELEMENT(element);
14503       player->is_digging = TRUE;
14504     }
14505
14506     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14507
14508     // use old behaviour for old levels (digging)
14509     if (!level.finish_dig_collect)
14510     {
14511       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14512                                           player->index_bit, dig_side);
14513
14514       // if digging triggered player relocation, finish digging tile
14515       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14516         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14517     }
14518
14519     if (mode == DF_SNAP)
14520     {
14521       if (level.block_snap_field)
14522         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14523       else
14524         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14525
14526       // use old behaviour for old levels (snapping)
14527       if (!level.finish_dig_collect)
14528         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14529                                             player->index_bit, dig_side);
14530     }
14531   }
14532   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14533   {
14534     RemoveField(x, y);
14535
14536     if (is_player && mode != DF_SNAP)
14537     {
14538       GfxElement[x][y] = element;
14539       player->is_collecting = TRUE;
14540     }
14541
14542     if (element == EL_SPEED_PILL)
14543     {
14544       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14545     }
14546     else if (element == EL_EXTRA_TIME && level.time > 0)
14547     {
14548       TimeLeft += level.extra_time;
14549
14550       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14551
14552       DisplayGameControlValues();
14553     }
14554     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14555     {
14556       int shield_time = (element == EL_SHIELD_DEADLY ?
14557                          level.shield_deadly_time :
14558                          level.shield_normal_time);
14559
14560       player->shield_normal_time_left += shield_time;
14561       if (element == EL_SHIELD_DEADLY)
14562         player->shield_deadly_time_left += shield_time;
14563     }
14564     else if (element == EL_DYNAMITE ||
14565              element == EL_EM_DYNAMITE ||
14566              element == EL_SP_DISK_RED)
14567     {
14568       if (player->inventory_size < MAX_INVENTORY_SIZE)
14569         player->inventory_element[player->inventory_size++] = element;
14570
14571       DrawGameDoorValues();
14572     }
14573     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14574     {
14575       player->dynabomb_count++;
14576       player->dynabombs_left++;
14577     }
14578     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14579     {
14580       player->dynabomb_size++;
14581     }
14582     else if (element == EL_DYNABOMB_INCREASE_POWER)
14583     {
14584       player->dynabomb_xl = TRUE;
14585     }
14586     else if (IS_KEY(element))
14587     {
14588       player->key[KEY_NR(element)] = TRUE;
14589
14590       DrawGameDoorValues();
14591     }
14592     else if (element == EL_DC_KEY_WHITE)
14593     {
14594       player->num_white_keys++;
14595
14596       // display white keys?
14597       // DrawGameDoorValues();
14598     }
14599     else if (IS_ENVELOPE(element))
14600     {
14601       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14602
14603       if (!wait_for_snapping)
14604         player->show_envelope = element;
14605     }
14606     else if (element == EL_EMC_LENSES)
14607     {
14608       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14609
14610       RedrawAllInvisibleElementsForLenses();
14611     }
14612     else if (element == EL_EMC_MAGNIFIER)
14613     {
14614       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14615
14616       RedrawAllInvisibleElementsForMagnifier();
14617     }
14618     else if (IS_DROPPABLE(element) ||
14619              IS_THROWABLE(element))     // can be collected and dropped
14620     {
14621       int i;
14622
14623       if (collect_count == 0)
14624         player->inventory_infinite_element = element;
14625       else
14626         for (i = 0; i < collect_count; i++)
14627           if (player->inventory_size < MAX_INVENTORY_SIZE)
14628             player->inventory_element[player->inventory_size++] = element;
14629
14630       DrawGameDoorValues();
14631     }
14632     else if (collect_count > 0)
14633     {
14634       game.gems_still_needed -= collect_count;
14635       if (game.gems_still_needed < 0)
14636         game.gems_still_needed = 0;
14637
14638       game.snapshot.collected_item = TRUE;
14639
14640       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14641
14642       DisplayGameControlValues();
14643     }
14644
14645     RaiseScoreElement(element);
14646     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14647
14648     // use old behaviour for old levels (collecting)
14649     if (!level.finish_dig_collect && is_player)
14650     {
14651       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14652                                           player->index_bit, dig_side);
14653
14654       // if collecting triggered player relocation, finish collecting tile
14655       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14656         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14657     }
14658
14659     if (mode == DF_SNAP)
14660     {
14661       if (level.block_snap_field)
14662         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14663       else
14664         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14665
14666       // use old behaviour for old levels (snapping)
14667       if (!level.finish_dig_collect)
14668         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14669                                             player->index_bit, dig_side);
14670     }
14671   }
14672   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14673   {
14674     if (mode == DF_SNAP && element != EL_BD_ROCK)
14675       return MP_NO_ACTION;
14676
14677     if (CAN_FALL(element) && dy)
14678       return MP_NO_ACTION;
14679
14680     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14681         !(element == EL_SPRING && level.use_spring_bug))
14682       return MP_NO_ACTION;
14683
14684     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14685         ((move_direction & MV_VERTICAL &&
14686           ((element_info[element].move_pattern & MV_LEFT &&
14687             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14688            (element_info[element].move_pattern & MV_RIGHT &&
14689             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14690          (move_direction & MV_HORIZONTAL &&
14691           ((element_info[element].move_pattern & MV_UP &&
14692             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14693            (element_info[element].move_pattern & MV_DOWN &&
14694             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14695       return MP_NO_ACTION;
14696
14697     // do not push elements already moving away faster than player
14698     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14699         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14700       return MP_NO_ACTION;
14701
14702     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14703     {
14704       if (player->push_delay_value == -1 || !player_was_pushing)
14705         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14706     }
14707     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14708     {
14709       if (player->push_delay_value == -1)
14710         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14711     }
14712     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14713     {
14714       if (!player->is_pushing)
14715         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14716     }
14717
14718     player->is_pushing = TRUE;
14719     player->is_active = TRUE;
14720
14721     if (!(IN_LEV_FIELD(nextx, nexty) &&
14722           (IS_FREE(nextx, nexty) ||
14723            (IS_SB_ELEMENT(element) &&
14724             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14725            (IS_CUSTOM_ELEMENT(element) &&
14726             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14727       return MP_NO_ACTION;
14728
14729     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14730       return MP_NO_ACTION;
14731
14732     if (player->push_delay == -1)       // new pushing; restart delay
14733       player->push_delay = 0;
14734
14735     if (player->push_delay < player->push_delay_value &&
14736         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14737         element != EL_SPRING && element != EL_BALLOON)
14738     {
14739       // make sure that there is no move delay before next try to push
14740       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14741         player->move_delay = 0;
14742
14743       return MP_NO_ACTION;
14744     }
14745
14746     if (IS_CUSTOM_ELEMENT(element) &&
14747         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14748     {
14749       if (!DigFieldByCE(nextx, nexty, element))
14750         return MP_NO_ACTION;
14751     }
14752
14753     if (IS_SB_ELEMENT(element))
14754     {
14755       boolean sokoban_task_solved = FALSE;
14756
14757       if (element == EL_SOKOBAN_FIELD_FULL)
14758       {
14759         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14760
14761         IncrementSokobanFieldsNeeded();
14762         IncrementSokobanObjectsNeeded();
14763       }
14764
14765       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14766       {
14767         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14768
14769         DecrementSokobanFieldsNeeded();
14770         DecrementSokobanObjectsNeeded();
14771
14772         // sokoban object was pushed from empty field to sokoban field
14773         if (Back[x][y] == EL_EMPTY)
14774           sokoban_task_solved = TRUE;
14775       }
14776
14777       Tile[x][y] = EL_SOKOBAN_OBJECT;
14778
14779       if (Back[x][y] == Back[nextx][nexty])
14780         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14781       else if (Back[x][y] != 0)
14782         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14783                                     ACTION_EMPTYING);
14784       else
14785         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14786                                     ACTION_FILLING);
14787
14788       if (sokoban_task_solved &&
14789           game.sokoban_fields_still_needed == 0 &&
14790           game.sokoban_objects_still_needed == 0 &&
14791           level.auto_exit_sokoban)
14792       {
14793         game.players_still_needed = 0;
14794
14795         LevelSolved();
14796
14797         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14798       }
14799     }
14800     else
14801       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14802
14803     InitMovingField(x, y, move_direction);
14804     GfxAction[x][y] = ACTION_PUSHING;
14805
14806     if (mode == DF_SNAP)
14807       ContinueMoving(x, y);
14808     else
14809       MovPos[x][y] = (dx != 0 ? dx : dy);
14810
14811     Pushed[x][y] = TRUE;
14812     Pushed[nextx][nexty] = TRUE;
14813
14814     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14815       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14816     else
14817       player->push_delay_value = -1;    // get new value later
14818
14819     // check for element change _after_ element has been pushed
14820     if (game.use_change_when_pushing_bug)
14821     {
14822       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14823                                  player->index_bit, dig_side);
14824       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14825                                           player->index_bit, dig_side);
14826     }
14827   }
14828   else if (IS_SWITCHABLE(element))
14829   {
14830     if (PLAYER_SWITCHING(player, x, y))
14831     {
14832       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14833                                           player->index_bit, dig_side);
14834
14835       return MP_ACTION;
14836     }
14837
14838     player->is_switching = TRUE;
14839     player->switch_x = x;
14840     player->switch_y = y;
14841
14842     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14843
14844     if (element == EL_ROBOT_WHEEL)
14845     {
14846       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14847
14848       game.robot_wheel_x = x;
14849       game.robot_wheel_y = y;
14850       game.robot_wheel_active = TRUE;
14851
14852       TEST_DrawLevelField(x, y);
14853     }
14854     else if (element == EL_SP_TERMINAL)
14855     {
14856       int xx, yy;
14857
14858       SCAN_PLAYFIELD(xx, yy)
14859       {
14860         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14861         {
14862           Bang(xx, yy);
14863         }
14864         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14865         {
14866           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14867
14868           ResetGfxAnimation(xx, yy);
14869           TEST_DrawLevelField(xx, yy);
14870         }
14871       }
14872     }
14873     else if (IS_BELT_SWITCH(element))
14874     {
14875       ToggleBeltSwitch(x, y);
14876     }
14877     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14878              element == EL_SWITCHGATE_SWITCH_DOWN ||
14879              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14880              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14881     {
14882       ToggleSwitchgateSwitch();
14883     }
14884     else if (element == EL_LIGHT_SWITCH ||
14885              element == EL_LIGHT_SWITCH_ACTIVE)
14886     {
14887       ToggleLightSwitch(x, y);
14888     }
14889     else if (element == EL_TIMEGATE_SWITCH ||
14890              element == EL_DC_TIMEGATE_SWITCH)
14891     {
14892       ActivateTimegateSwitch(x, y);
14893     }
14894     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14895              element == EL_BALLOON_SWITCH_RIGHT ||
14896              element == EL_BALLOON_SWITCH_UP    ||
14897              element == EL_BALLOON_SWITCH_DOWN  ||
14898              element == EL_BALLOON_SWITCH_NONE  ||
14899              element == EL_BALLOON_SWITCH_ANY)
14900     {
14901       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14902                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14903                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14904                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14905                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14906                              move_direction);
14907     }
14908     else if (element == EL_LAMP)
14909     {
14910       Tile[x][y] = EL_LAMP_ACTIVE;
14911       game.lights_still_needed--;
14912
14913       ResetGfxAnimation(x, y);
14914       TEST_DrawLevelField(x, y);
14915     }
14916     else if (element == EL_TIME_ORB_FULL)
14917     {
14918       Tile[x][y] = EL_TIME_ORB_EMPTY;
14919
14920       if (level.time > 0 || level.use_time_orb_bug)
14921       {
14922         TimeLeft += level.time_orb_time;
14923         game.no_level_time_limit = FALSE;
14924
14925         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14926
14927         DisplayGameControlValues();
14928       }
14929
14930       ResetGfxAnimation(x, y);
14931       TEST_DrawLevelField(x, y);
14932     }
14933     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14934              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14935     {
14936       int xx, yy;
14937
14938       game.ball_active = !game.ball_active;
14939
14940       SCAN_PLAYFIELD(xx, yy)
14941       {
14942         int e = Tile[xx][yy];
14943
14944         if (game.ball_active)
14945         {
14946           if (e == EL_EMC_MAGIC_BALL)
14947             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14948           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14949             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14950         }
14951         else
14952         {
14953           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14954             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14955           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14956             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14957         }
14958       }
14959     }
14960
14961     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14962                                         player->index_bit, dig_side);
14963
14964     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14965                                         player->index_bit, dig_side);
14966
14967     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14968                                         player->index_bit, dig_side);
14969
14970     return MP_ACTION;
14971   }
14972   else
14973   {
14974     if (!PLAYER_SWITCHING(player, x, y))
14975     {
14976       player->is_switching = TRUE;
14977       player->switch_x = x;
14978       player->switch_y = y;
14979
14980       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14981                                  player->index_bit, dig_side);
14982       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14983                                           player->index_bit, dig_side);
14984
14985       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14986                                  player->index_bit, dig_side);
14987       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14988                                           player->index_bit, dig_side);
14989     }
14990
14991     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14992                                player->index_bit, dig_side);
14993     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14994                                         player->index_bit, dig_side);
14995
14996     return MP_NO_ACTION;
14997   }
14998
14999   player->push_delay = -1;
15000
15001   if (is_player)                // function can also be called by EL_PENGUIN
15002   {
15003     if (Tile[x][y] != element)          // really digged/collected something
15004     {
15005       player->is_collecting = !player->is_digging;
15006       player->is_active = TRUE;
15007
15008       player->last_removed_element = element;
15009     }
15010   }
15011
15012   return MP_MOVING;
15013 }
15014
15015 static boolean DigFieldByCE(int x, int y, int digging_element)
15016 {
15017   int element = Tile[x][y];
15018
15019   if (!IS_FREE(x, y))
15020   {
15021     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15022                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15023                   ACTION_BREAKING);
15024
15025     // no element can dig solid indestructible elements
15026     if (IS_INDESTRUCTIBLE(element) &&
15027         !IS_DIGGABLE(element) &&
15028         !IS_COLLECTIBLE(element))
15029       return FALSE;
15030
15031     if (AmoebaNr[x][y] &&
15032         (element == EL_AMOEBA_FULL ||
15033          element == EL_BD_AMOEBA ||
15034          element == EL_AMOEBA_GROWING))
15035     {
15036       AmoebaCnt[AmoebaNr[x][y]]--;
15037       AmoebaCnt2[AmoebaNr[x][y]]--;
15038     }
15039
15040     if (IS_MOVING(x, y))
15041       RemoveMovingField(x, y);
15042     else
15043     {
15044       RemoveField(x, y);
15045       TEST_DrawLevelField(x, y);
15046     }
15047
15048     // if digged element was about to explode, prevent the explosion
15049     ExplodeField[x][y] = EX_TYPE_NONE;
15050
15051     PlayLevelSoundAction(x, y, action);
15052   }
15053
15054   Store[x][y] = EL_EMPTY;
15055
15056   // this makes it possible to leave the removed element again
15057   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15058     Store[x][y] = element;
15059
15060   return TRUE;
15061 }
15062
15063 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15064 {
15065   int jx = player->jx, jy = player->jy;
15066   int x = jx + dx, y = jy + dy;
15067   int snap_direction = (dx == -1 ? MV_LEFT  :
15068                         dx == +1 ? MV_RIGHT :
15069                         dy == -1 ? MV_UP    :
15070                         dy == +1 ? MV_DOWN  : MV_NONE);
15071   boolean can_continue_snapping = (level.continuous_snapping &&
15072                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15073
15074   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15075     return FALSE;
15076
15077   if (!player->active || !IN_LEV_FIELD(x, y))
15078     return FALSE;
15079
15080   if (dx && dy)
15081     return FALSE;
15082
15083   if (!dx && !dy)
15084   {
15085     if (player->MovPos == 0)
15086       player->is_pushing = FALSE;
15087
15088     player->is_snapping = FALSE;
15089
15090     if (player->MovPos == 0)
15091     {
15092       player->is_moving = FALSE;
15093       player->is_digging = FALSE;
15094       player->is_collecting = FALSE;
15095     }
15096
15097     return FALSE;
15098   }
15099
15100   // prevent snapping with already pressed snap key when not allowed
15101   if (player->is_snapping && !can_continue_snapping)
15102     return FALSE;
15103
15104   player->MovDir = snap_direction;
15105
15106   if (player->MovPos == 0)
15107   {
15108     player->is_moving = FALSE;
15109     player->is_digging = FALSE;
15110     player->is_collecting = FALSE;
15111   }
15112
15113   player->is_dropping = FALSE;
15114   player->is_dropping_pressed = FALSE;
15115   player->drop_pressed_delay = 0;
15116
15117   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15118     return FALSE;
15119
15120   player->is_snapping = TRUE;
15121   player->is_active = TRUE;
15122
15123   if (player->MovPos == 0)
15124   {
15125     player->is_moving = FALSE;
15126     player->is_digging = FALSE;
15127     player->is_collecting = FALSE;
15128   }
15129
15130   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15131     TEST_DrawLevelField(player->last_jx, player->last_jy);
15132
15133   TEST_DrawLevelField(x, y);
15134
15135   return TRUE;
15136 }
15137
15138 static boolean DropElement(struct PlayerInfo *player)
15139 {
15140   int old_element, new_element;
15141   int dropx = player->jx, dropy = player->jy;
15142   int drop_direction = player->MovDir;
15143   int drop_side = drop_direction;
15144   int drop_element = get_next_dropped_element(player);
15145
15146   /* do not drop an element on top of another element; when holding drop key
15147      pressed without moving, dropped element must move away before the next
15148      element can be dropped (this is especially important if the next element
15149      is dynamite, which can be placed on background for historical reasons) */
15150   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15151     return MP_ACTION;
15152
15153   if (IS_THROWABLE(drop_element))
15154   {
15155     dropx += GET_DX_FROM_DIR(drop_direction);
15156     dropy += GET_DY_FROM_DIR(drop_direction);
15157
15158     if (!IN_LEV_FIELD(dropx, dropy))
15159       return FALSE;
15160   }
15161
15162   old_element = Tile[dropx][dropy];     // old element at dropping position
15163   new_element = drop_element;           // default: no change when dropping
15164
15165   // check if player is active, not moving and ready to drop
15166   if (!player->active || player->MovPos || player->drop_delay > 0)
15167     return FALSE;
15168
15169   // check if player has anything that can be dropped
15170   if (new_element == EL_UNDEFINED)
15171     return FALSE;
15172
15173   // only set if player has anything that can be dropped
15174   player->is_dropping_pressed = TRUE;
15175
15176   // check if drop key was pressed long enough for EM style dynamite
15177   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15178     return FALSE;
15179
15180   // check if anything can be dropped at the current position
15181   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15182     return FALSE;
15183
15184   // collected custom elements can only be dropped on empty fields
15185   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15186     return FALSE;
15187
15188   if (old_element != EL_EMPTY)
15189     Back[dropx][dropy] = old_element;   // store old element on this field
15190
15191   ResetGfxAnimation(dropx, dropy);
15192   ResetRandomAnimationValue(dropx, dropy);
15193
15194   if (player->inventory_size > 0 ||
15195       player->inventory_infinite_element != EL_UNDEFINED)
15196   {
15197     if (player->inventory_size > 0)
15198     {
15199       player->inventory_size--;
15200
15201       DrawGameDoorValues();
15202
15203       if (new_element == EL_DYNAMITE)
15204         new_element = EL_DYNAMITE_ACTIVE;
15205       else if (new_element == EL_EM_DYNAMITE)
15206         new_element = EL_EM_DYNAMITE_ACTIVE;
15207       else if (new_element == EL_SP_DISK_RED)
15208         new_element = EL_SP_DISK_RED_ACTIVE;
15209     }
15210
15211     Tile[dropx][dropy] = new_element;
15212
15213     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15214       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15215                           el2img(Tile[dropx][dropy]), 0);
15216
15217     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15218
15219     // needed if previous element just changed to "empty" in the last frame
15220     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15221
15222     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15223                                player->index_bit, drop_side);
15224     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15225                                         CE_PLAYER_DROPS_X,
15226                                         player->index_bit, drop_side);
15227
15228     TestIfElementTouchesCustomElement(dropx, dropy);
15229   }
15230   else          // player is dropping a dyna bomb
15231   {
15232     player->dynabombs_left--;
15233
15234     Tile[dropx][dropy] = new_element;
15235
15236     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15237       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15238                           el2img(Tile[dropx][dropy]), 0);
15239
15240     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15241   }
15242
15243   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15244     InitField_WithBug1(dropx, dropy, FALSE);
15245
15246   new_element = Tile[dropx][dropy];     // element might have changed
15247
15248   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15249       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15250   {
15251     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15252       MovDir[dropx][dropy] = drop_direction;
15253
15254     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15255
15256     // do not cause impact style collision by dropping elements that can fall
15257     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15258   }
15259
15260   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15261   player->is_dropping = TRUE;
15262
15263   player->drop_pressed_delay = 0;
15264   player->is_dropping_pressed = FALSE;
15265
15266   player->drop_x = dropx;
15267   player->drop_y = dropy;
15268
15269   return TRUE;
15270 }
15271
15272 // ----------------------------------------------------------------------------
15273 // game sound playing functions
15274 // ----------------------------------------------------------------------------
15275
15276 static int *loop_sound_frame = NULL;
15277 static int *loop_sound_volume = NULL;
15278
15279 void InitPlayLevelSound(void)
15280 {
15281   int num_sounds = getSoundListSize();
15282
15283   checked_free(loop_sound_frame);
15284   checked_free(loop_sound_volume);
15285
15286   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15287   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15288 }
15289
15290 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15291 {
15292   int sx = SCREENX(x), sy = SCREENY(y);
15293   int volume, stereo_position;
15294   int max_distance = 8;
15295   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15296
15297   if ((!setup.sound_simple && !is_loop_sound) ||
15298       (!setup.sound_loops && is_loop_sound))
15299     return;
15300
15301   if (!IN_LEV_FIELD(x, y) ||
15302       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15303       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15304     return;
15305
15306   volume = SOUND_MAX_VOLUME;
15307
15308   if (!IN_SCR_FIELD(sx, sy))
15309   {
15310     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15311     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15312
15313     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15314   }
15315
15316   stereo_position = (SOUND_MAX_LEFT +
15317                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15318                      (SCR_FIELDX + 2 * max_distance));
15319
15320   if (is_loop_sound)
15321   {
15322     /* This assures that quieter loop sounds do not overwrite louder ones,
15323        while restarting sound volume comparison with each new game frame. */
15324
15325     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15326       return;
15327
15328     loop_sound_volume[nr] = volume;
15329     loop_sound_frame[nr] = FrameCounter;
15330   }
15331
15332   PlaySoundExt(nr, volume, stereo_position, type);
15333 }
15334
15335 static void PlayLevelSound(int x, int y, int nr)
15336 {
15337   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15338 }
15339
15340 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15341 {
15342   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15343                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15344                  y < LEVELY(BY1) ? LEVELY(BY1) :
15345                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15346                  sound_action);
15347 }
15348
15349 static void PlayLevelSoundAction(int x, int y, int action)
15350 {
15351   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15352 }
15353
15354 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15355 {
15356   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15357
15358   if (sound_effect != SND_UNDEFINED)
15359     PlayLevelSound(x, y, sound_effect);
15360 }
15361
15362 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15363                                               int action)
15364 {
15365   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15366
15367   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15368     PlayLevelSound(x, y, sound_effect);
15369 }
15370
15371 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15372 {
15373   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15374
15375   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15376     PlayLevelSound(x, y, sound_effect);
15377 }
15378
15379 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15380 {
15381   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15382
15383   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15384     StopSound(sound_effect);
15385 }
15386
15387 static int getLevelMusicNr(void)
15388 {
15389   int level_pos = level_nr - leveldir_current->first_level;
15390
15391   if (levelset.music[level_nr] != MUS_UNDEFINED)
15392     return levelset.music[level_nr];            // from config file
15393   else
15394     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15395 }
15396
15397 static void FadeLevelSounds(void)
15398 {
15399   FadeSounds();
15400 }
15401
15402 static void FadeLevelMusic(void)
15403 {
15404   int music_nr = getLevelMusicNr();
15405   char *curr_music = getCurrentlyPlayingMusicFilename();
15406   char *next_music = getMusicInfoEntryFilename(music_nr);
15407
15408   if (!strEqual(curr_music, next_music))
15409     FadeMusic();
15410 }
15411
15412 void FadeLevelSoundsAndMusic(void)
15413 {
15414   FadeLevelSounds();
15415   FadeLevelMusic();
15416 }
15417
15418 static void PlayLevelMusic(void)
15419 {
15420   int music_nr = getLevelMusicNr();
15421   char *curr_music = getCurrentlyPlayingMusicFilename();
15422   char *next_music = getMusicInfoEntryFilename(music_nr);
15423
15424   if (!strEqual(curr_music, next_music))
15425     PlayMusicLoop(music_nr);
15426 }
15427
15428 static int getSoundAction_BD(int sample)
15429 {
15430   switch (sample)
15431   {
15432     case GD_S_STONE:
15433     case GD_S_NUT:
15434     case GD_S_DIRT_BALL:
15435     case GD_S_NITRO:
15436     case GD_S_FALLING_WALL:
15437       return ACTION_IMPACT;
15438
15439     case GD_S_NUT_CRACK:
15440       return ACTION_BREAKING;
15441
15442     case GD_S_EXPANDING_WALL:
15443     case GD_S_WALL_REAPPEAR:
15444     case GD_S_SLIME:
15445     case GD_S_LAVA:
15446     case GD_S_ACID_SPREAD:
15447       return ACTION_GROWING;
15448
15449     case GD_S_DIAMOND_COLLECT:
15450     case GD_S_SKELETON_COLLECT:
15451     case GD_S_PNEUMATIC_COLLECT:
15452     case GD_S_BOMB_COLLECT:
15453     case GD_S_CLOCK_COLLECT:
15454     case GD_S_SWEET_COLLECT:
15455     case GD_S_KEY_COLLECT:
15456     case GD_S_DIAMOND_KEY_COLLECT:
15457       return ACTION_COLLECTING;
15458
15459     case GD_S_BOMB_PLACE:
15460     case GD_S_REPLICATOR:
15461       return ACTION_DROPPING;
15462
15463     case GD_S_BLADDER_MOVE:
15464       return ACTION_MOVING;
15465
15466     case GD_S_BLADDER_SPENDER:
15467     case GD_S_BLADDER_CONVERT:
15468     case GD_S_GRAVITY_CHANGE:
15469       return ACTION_CHANGING;
15470
15471     case GD_S_BITER_EAT:
15472       return ACTION_EATING;
15473
15474     case GD_S_DOOR_OPEN:
15475     case GD_S_CRACK:
15476       return ACTION_OPENING;
15477
15478     case GD_S_WALK_EARTH:
15479       return ACTION_DIGGING;
15480
15481     case GD_S_WALK_EMPTY:
15482       return ACTION_WALKING;
15483
15484     case GD_S_SWITCH_BITER:
15485     case GD_S_SWITCH_CREATURES:
15486     case GD_S_SWITCH_GRAVITY:
15487     case GD_S_SWITCH_EXPANDING:
15488     case GD_S_SWITCH_CONVEYOR:
15489     case GD_S_SWITCH_REPLICATOR:
15490     case GD_S_STIRRING:
15491       return ACTION_ACTIVATING;
15492
15493     case GD_S_BOX_PUSH:
15494       return ACTION_PUSHING;
15495
15496     case GD_S_TELEPORTER:
15497       return ACTION_PASSING;
15498
15499     case GD_S_EXPLOSION:
15500     case GD_S_BOMB_EXPLOSION:
15501     case GD_S_GHOST_EXPLOSION:
15502     case GD_S_VOODOO_EXPLOSION:
15503     case GD_S_NITRO_EXPLOSION:
15504       return ACTION_EXPLODING;
15505
15506     case GD_S_COVER:
15507     case GD_S_AMOEBA:
15508     case GD_S_AMOEBA_MAGIC:
15509     case GD_S_MAGIC_WALL:
15510     case GD_S_PNEUMATIC_HAMMER:
15511     case GD_S_WATER:
15512       return ACTION_ACTIVE;
15513
15514     case GD_S_DIAMOND_RANDOM:
15515     case GD_S_DIAMOND_1:
15516     case GD_S_DIAMOND_2:
15517     case GD_S_DIAMOND_3:
15518     case GD_S_DIAMOND_4:
15519     case GD_S_DIAMOND_5:
15520     case GD_S_DIAMOND_6:
15521     case GD_S_DIAMOND_7:
15522     case GD_S_DIAMOND_8:
15523     case GD_S_TIMEOUT_0:
15524     case GD_S_TIMEOUT_1:
15525     case GD_S_TIMEOUT_2:
15526     case GD_S_TIMEOUT_3:
15527     case GD_S_TIMEOUT_4:
15528     case GD_S_TIMEOUT_5:
15529     case GD_S_TIMEOUT_6:
15530     case GD_S_TIMEOUT_7:
15531     case GD_S_TIMEOUT_8:
15532     case GD_S_TIMEOUT_9:
15533     case GD_S_TIMEOUT_10:
15534     case GD_S_BONUS_LIFE:
15535       // kludge to prevent playing as loop sound
15536       return ACTION_OTHER;
15537
15538     case GD_S_FINISHED:
15539       return ACTION_DEFAULT;
15540
15541     default:
15542       return ACTION_DEFAULT;
15543   }
15544 }
15545
15546 static int getSoundEffect_BD(int element_bd, int sample)
15547 {
15548   int sound_action = getSoundAction_BD(sample);
15549   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15550   int nr;
15551
15552   // standard sounds
15553   if (sound_action != ACTION_OTHER &&
15554       sound_action != ACTION_DEFAULT)
15555     return sound_effect;
15556
15557   // special sounds
15558   switch (sample)
15559   {
15560     case GD_S_DIAMOND_RANDOM:
15561       nr = GetSimpleRandom(8);
15562       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15563       break;
15564
15565     case GD_S_DIAMOND_1:
15566     case GD_S_DIAMOND_2:
15567     case GD_S_DIAMOND_3:
15568     case GD_S_DIAMOND_4:
15569     case GD_S_DIAMOND_5:
15570     case GD_S_DIAMOND_6:
15571     case GD_S_DIAMOND_7:
15572     case GD_S_DIAMOND_8:
15573       nr = sample - GD_S_DIAMOND_1;
15574       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15575       break;
15576
15577     case GD_S_TIMEOUT_0:
15578     case GD_S_TIMEOUT_1:
15579     case GD_S_TIMEOUT_2:
15580     case GD_S_TIMEOUT_3:
15581     case GD_S_TIMEOUT_4:
15582     case GD_S_TIMEOUT_5:
15583     case GD_S_TIMEOUT_6:
15584     case GD_S_TIMEOUT_7:
15585     case GD_S_TIMEOUT_8:
15586     case GD_S_TIMEOUT_9:
15587     case GD_S_TIMEOUT_10:
15588       nr = sample - GD_S_TIMEOUT_0;
15589       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15590
15591       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15592         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15593       break;
15594
15595     case GD_S_FINISHED:
15596       sound_effect = SND_GAME_LEVELTIME_BONUS;
15597       break;
15598
15599     case GD_S_BONUS_LIFE:
15600       sound_effect = SND_GAME_HEALTH_BONUS;
15601       break;
15602
15603     default:
15604       sound_effect = SND_UNDEFINED;
15605       break;
15606   }
15607
15608   return sound_effect;
15609 }
15610
15611 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15612 {
15613   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15614   int sound_effect = getSoundEffect_BD(element, sample);
15615   int sound_action = getSoundAction_BD(sample);
15616   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15617   int offset = 0;
15618   int x = xx - offset;
15619   int y = yy - offset;
15620
15621   if (sound_action == ACTION_OTHER)
15622     is_loop_sound = FALSE;
15623
15624   if (sound_effect != SND_UNDEFINED)
15625     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15626 }
15627
15628 void StopSound_BD(int element_bd, int sample)
15629 {
15630   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15631   int sound_effect = getSoundEffect_BD(element, sample);
15632
15633   if (sound_effect != SND_UNDEFINED)
15634     StopSound(sound_effect);
15635 }
15636
15637 boolean isSoundPlaying_BD(int element_bd, int sample)
15638 {
15639   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15640   int sound_effect = getSoundEffect_BD(element, sample);
15641
15642   if (sound_effect != SND_UNDEFINED)
15643     return isSoundPlaying(sound_effect);
15644
15645   return FALSE;
15646 }
15647
15648 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15649 {
15650   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15651   int offset = 0;
15652   int x = xx - offset;
15653   int y = yy - offset;
15654
15655   switch (sample)
15656   {
15657     case SOUND_blank:
15658       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15659       break;
15660
15661     case SOUND_roll:
15662       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15663       break;
15664
15665     case SOUND_stone:
15666       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15667       break;
15668
15669     case SOUND_nut:
15670       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15671       break;
15672
15673     case SOUND_crack:
15674       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15675       break;
15676
15677     case SOUND_bug:
15678       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15679       break;
15680
15681     case SOUND_tank:
15682       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15683       break;
15684
15685     case SOUND_android_clone:
15686       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15687       break;
15688
15689     case SOUND_android_move:
15690       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15691       break;
15692
15693     case SOUND_spring:
15694       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15695       break;
15696
15697     case SOUND_slurp:
15698       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15699       break;
15700
15701     case SOUND_eater:
15702       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15703       break;
15704
15705     case SOUND_eater_eat:
15706       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15707       break;
15708
15709     case SOUND_alien:
15710       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15711       break;
15712
15713     case SOUND_collect:
15714       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15715       break;
15716
15717     case SOUND_diamond:
15718       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15719       break;
15720
15721     case SOUND_squash:
15722       // !!! CHECK THIS !!!
15723 #if 1
15724       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15725 #else
15726       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15727 #endif
15728       break;
15729
15730     case SOUND_wonderfall:
15731       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15732       break;
15733
15734     case SOUND_drip:
15735       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15736       break;
15737
15738     case SOUND_push:
15739       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15740       break;
15741
15742     case SOUND_dirt:
15743       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15744       break;
15745
15746     case SOUND_acid:
15747       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15748       break;
15749
15750     case SOUND_ball:
15751       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15752       break;
15753
15754     case SOUND_slide:
15755       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15756       break;
15757
15758     case SOUND_wonder:
15759       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15760       break;
15761
15762     case SOUND_door:
15763       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15764       break;
15765
15766     case SOUND_exit_open:
15767       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15768       break;
15769
15770     case SOUND_exit_leave:
15771       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15772       break;
15773
15774     case SOUND_dynamite:
15775       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15776       break;
15777
15778     case SOUND_tick:
15779       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15780       break;
15781
15782     case SOUND_press:
15783       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15784       break;
15785
15786     case SOUND_wheel:
15787       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15788       break;
15789
15790     case SOUND_boom:
15791       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15792       break;
15793
15794     case SOUND_die:
15795       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15796       break;
15797
15798     case SOUND_time:
15799       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15800       break;
15801
15802     default:
15803       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15804       break;
15805   }
15806 }
15807
15808 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15809 {
15810   int element = map_element_SP_to_RND(element_sp);
15811   int action = map_action_SP_to_RND(action_sp);
15812   int offset = (setup.sp_show_border_elements ? 0 : 1);
15813   int x = xx - offset;
15814   int y = yy - offset;
15815
15816   PlayLevelSoundElementAction(x, y, element, action);
15817 }
15818
15819 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15820 {
15821   int element = map_element_MM_to_RND(element_mm);
15822   int action = map_action_MM_to_RND(action_mm);
15823   int offset = 0;
15824   int x = xx - offset;
15825   int y = yy - offset;
15826
15827   if (!IS_MM_ELEMENT(element))
15828     element = EL_MM_DEFAULT;
15829
15830   PlayLevelSoundElementAction(x, y, element, action);
15831 }
15832
15833 void PlaySound_MM(int sound_mm)
15834 {
15835   int sound = map_sound_MM_to_RND(sound_mm);
15836
15837   if (sound == SND_UNDEFINED)
15838     return;
15839
15840   PlaySound(sound);
15841 }
15842
15843 void PlaySoundLoop_MM(int sound_mm)
15844 {
15845   int sound = map_sound_MM_to_RND(sound_mm);
15846
15847   if (sound == SND_UNDEFINED)
15848     return;
15849
15850   PlaySoundLoop(sound);
15851 }
15852
15853 void StopSound_MM(int sound_mm)
15854 {
15855   int sound = map_sound_MM_to_RND(sound_mm);
15856
15857   if (sound == SND_UNDEFINED)
15858     return;
15859
15860   StopSound(sound);
15861 }
15862
15863 void RaiseScore(int value)
15864 {
15865   game.score += value;
15866
15867   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15868
15869   DisplayGameControlValues();
15870 }
15871
15872 void RaiseScoreElement(int element)
15873 {
15874   switch (element)
15875   {
15876     case EL_EMERALD:
15877     case EL_BD_DIAMOND:
15878     case EL_EMERALD_YELLOW:
15879     case EL_EMERALD_RED:
15880     case EL_EMERALD_PURPLE:
15881     case EL_SP_INFOTRON:
15882       RaiseScore(level.score[SC_EMERALD]);
15883       break;
15884     case EL_DIAMOND:
15885       RaiseScore(level.score[SC_DIAMOND]);
15886       break;
15887     case EL_CRYSTAL:
15888       RaiseScore(level.score[SC_CRYSTAL]);
15889       break;
15890     case EL_PEARL:
15891       RaiseScore(level.score[SC_PEARL]);
15892       break;
15893     case EL_BUG:
15894     case EL_BD_BUTTERFLY:
15895     case EL_SP_ELECTRON:
15896       RaiseScore(level.score[SC_BUG]);
15897       break;
15898     case EL_SPACESHIP:
15899     case EL_BD_FIREFLY:
15900     case EL_SP_SNIKSNAK:
15901       RaiseScore(level.score[SC_SPACESHIP]);
15902       break;
15903     case EL_YAMYAM:
15904     case EL_DARK_YAMYAM:
15905       RaiseScore(level.score[SC_YAMYAM]);
15906       break;
15907     case EL_ROBOT:
15908       RaiseScore(level.score[SC_ROBOT]);
15909       break;
15910     case EL_PACMAN:
15911       RaiseScore(level.score[SC_PACMAN]);
15912       break;
15913     case EL_NUT:
15914       RaiseScore(level.score[SC_NUT]);
15915       break;
15916     case EL_DYNAMITE:
15917     case EL_EM_DYNAMITE:
15918     case EL_SP_DISK_RED:
15919     case EL_DYNABOMB_INCREASE_NUMBER:
15920     case EL_DYNABOMB_INCREASE_SIZE:
15921     case EL_DYNABOMB_INCREASE_POWER:
15922       RaiseScore(level.score[SC_DYNAMITE]);
15923       break;
15924     case EL_SHIELD_NORMAL:
15925     case EL_SHIELD_DEADLY:
15926       RaiseScore(level.score[SC_SHIELD]);
15927       break;
15928     case EL_EXTRA_TIME:
15929       RaiseScore(level.extra_time_score);
15930       break;
15931     case EL_KEY_1:
15932     case EL_KEY_2:
15933     case EL_KEY_3:
15934     case EL_KEY_4:
15935     case EL_EM_KEY_1:
15936     case EL_EM_KEY_2:
15937     case EL_EM_KEY_3:
15938     case EL_EM_KEY_4:
15939     case EL_EMC_KEY_5:
15940     case EL_EMC_KEY_6:
15941     case EL_EMC_KEY_7:
15942     case EL_EMC_KEY_8:
15943     case EL_DC_KEY_WHITE:
15944       RaiseScore(level.score[SC_KEY]);
15945       break;
15946     default:
15947       RaiseScore(element_info[element].collect_score);
15948       break;
15949   }
15950 }
15951
15952 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15953 {
15954   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15955   {
15956     if (!quick_quit)
15957     {
15958       // prevent short reactivation of overlay buttons while closing door
15959       SetOverlayActive(FALSE);
15960       UnmapGameButtons();
15961
15962       // door may still be open due to skipped or envelope style request
15963       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15964     }
15965
15966     if (network.enabled)
15967     {
15968       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15969     }
15970     else
15971     {
15972       if (quick_quit)
15973         FadeSkipNextFadeIn();
15974
15975       SetGameStatus(GAME_MODE_MAIN);
15976
15977       DrawMainMenu();
15978     }
15979   }
15980   else          // continue playing the game
15981   {
15982     if (tape.playing && tape.deactivate_display)
15983       TapeDeactivateDisplayOff(TRUE);
15984
15985     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15986
15987     if (tape.playing && tape.deactivate_display)
15988       TapeDeactivateDisplayOn();
15989   }
15990 }
15991
15992 void RequestQuitGame(boolean escape_key_pressed)
15993 {
15994   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15995   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15996                         level_editor_test_game);
15997   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15998                           quick_quit || score_info_tape_play);
15999
16000   RequestQuitGameExt(skip_request, quick_quit,
16001                      "Do you really want to quit the game?");
16002 }
16003
16004 static char *getRestartGameMessage(void)
16005 {
16006   boolean play_again = hasStartedNetworkGame();
16007   static char message[MAX_OUTPUT_LINESIZE];
16008   char *game_over_text = "Game over!";
16009   char *play_again_text = " Play it again?";
16010
16011   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16012       game_mm.game_over_message != NULL)
16013     game_over_text = game_mm.game_over_message;
16014
16015   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16016            (play_again ? play_again_text : ""));
16017
16018   return message;
16019 }
16020
16021 static void RequestRestartGame(void)
16022 {
16023   char *message = getRestartGameMessage();
16024   boolean has_started_game = hasStartedNetworkGame();
16025   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16026   int door_state = DOOR_CLOSE_1;
16027
16028   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
16029   {
16030     CloseDoor(door_state);
16031
16032     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16033   }
16034   else
16035   {
16036     // if game was invoked from level editor, also close tape recorder door
16037     if (level_editor_test_game)
16038       door_state = DOOR_CLOSE_ALL;
16039
16040     CloseDoor(door_state);
16041
16042     SetGameStatus(GAME_MODE_MAIN);
16043
16044     DrawMainMenu();
16045   }
16046 }
16047
16048 boolean CheckRestartGame(void)
16049 {
16050   static int game_over_delay = 0;
16051   int game_over_delay_value = 50;
16052   boolean game_over = checkGameFailed();
16053
16054   if (!game_over)
16055   {
16056     game_over_delay = game_over_delay_value;
16057
16058     return FALSE;
16059   }
16060
16061   if (game_over_delay > 0)
16062   {
16063     if (game_over_delay == game_over_delay_value / 2)
16064       PlaySound(SND_GAME_LOSING);
16065
16066     game_over_delay--;
16067
16068     return FALSE;
16069   }
16070
16071   // do not ask to play again if request dialog is already active
16072   if (game.request_active)
16073     return FALSE;
16074
16075   // do not ask to play again if request dialog already handled
16076   if (game.RestartGameRequested)
16077     return FALSE;
16078
16079   // do not ask to play again if game was never actually played
16080   if (!game.GamePlayed)
16081     return FALSE;
16082
16083   // do not ask to play again if this was disabled in setup menu
16084   if (!setup.ask_on_game_over)
16085     return FALSE;
16086
16087   game.RestartGameRequested = TRUE;
16088
16089   RequestRestartGame();
16090
16091   return TRUE;
16092 }
16093
16094 boolean checkGameSolved(void)
16095 {
16096   // set for all game engines if level was solved
16097   return game.LevelSolved_GameEnd;
16098 }
16099
16100 boolean checkGameFailed(void)
16101 {
16102   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16103     return (game_em.game_over && !game_em.level_solved);
16104   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16105     return (game_sp.game_over && !game_sp.level_solved);
16106   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16107     return (game_mm.game_over && !game_mm.level_solved);
16108   else                          // GAME_ENGINE_TYPE_RND
16109     return (game.GameOver && !game.LevelSolved);
16110 }
16111
16112 boolean checkGameEnded(void)
16113 {
16114   return (checkGameSolved() || checkGameFailed());
16115 }
16116
16117
16118 // ----------------------------------------------------------------------------
16119 // random generator functions
16120 // ----------------------------------------------------------------------------
16121
16122 unsigned int InitEngineRandom_RND(int seed)
16123 {
16124   game.num_random_calls = 0;
16125
16126   return InitEngineRandom(seed);
16127 }
16128
16129 unsigned int RND(int max)
16130 {
16131   if (max > 0)
16132   {
16133     game.num_random_calls++;
16134
16135     return GetEngineRandom(max);
16136   }
16137
16138   return 0;
16139 }
16140
16141
16142 // ----------------------------------------------------------------------------
16143 // game engine snapshot handling functions
16144 // ----------------------------------------------------------------------------
16145
16146 struct EngineSnapshotInfo
16147 {
16148   // runtime values for custom element collect score
16149   int collect_score[NUM_CUSTOM_ELEMENTS];
16150
16151   // runtime values for group element choice position
16152   int choice_pos[NUM_GROUP_ELEMENTS];
16153
16154   // runtime values for belt position animations
16155   int belt_graphic[4][NUM_BELT_PARTS];
16156   int belt_anim_mode[4][NUM_BELT_PARTS];
16157 };
16158
16159 static struct EngineSnapshotInfo engine_snapshot_rnd;
16160 static char *snapshot_level_identifier = NULL;
16161 static int snapshot_level_nr = -1;
16162
16163 static void SaveEngineSnapshotValues_RND(void)
16164 {
16165   static int belt_base_active_element[4] =
16166   {
16167     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16168     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16169     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16170     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16171   };
16172   int i, j;
16173
16174   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16175   {
16176     int element = EL_CUSTOM_START + i;
16177
16178     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16179   }
16180
16181   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16182   {
16183     int element = EL_GROUP_START + i;
16184
16185     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16186   }
16187
16188   for (i = 0; i < 4; i++)
16189   {
16190     for (j = 0; j < NUM_BELT_PARTS; j++)
16191     {
16192       int element = belt_base_active_element[i] + j;
16193       int graphic = el2img(element);
16194       int anim_mode = graphic_info[graphic].anim_mode;
16195
16196       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16197       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16198     }
16199   }
16200 }
16201
16202 static void LoadEngineSnapshotValues_RND(void)
16203 {
16204   unsigned int num_random_calls = game.num_random_calls;
16205   int i, j;
16206
16207   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16208   {
16209     int element = EL_CUSTOM_START + i;
16210
16211     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16212   }
16213
16214   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16215   {
16216     int element = EL_GROUP_START + i;
16217
16218     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16219   }
16220
16221   for (i = 0; i < 4; i++)
16222   {
16223     for (j = 0; j < NUM_BELT_PARTS; j++)
16224     {
16225       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16226       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16227
16228       graphic_info[graphic].anim_mode = anim_mode;
16229     }
16230   }
16231
16232   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16233   {
16234     InitRND(tape.random_seed);
16235     for (i = 0; i < num_random_calls; i++)
16236       RND(1);
16237   }
16238
16239   if (game.num_random_calls != num_random_calls)
16240   {
16241     Error("number of random calls out of sync");
16242     Error("number of random calls should be %d", num_random_calls);
16243     Error("number of random calls is %d", game.num_random_calls);
16244
16245     Fail("this should not happen -- please debug");
16246   }
16247 }
16248
16249 void FreeEngineSnapshotSingle(void)
16250 {
16251   FreeSnapshotSingle();
16252
16253   setString(&snapshot_level_identifier, NULL);
16254   snapshot_level_nr = -1;
16255 }
16256
16257 void FreeEngineSnapshotList(void)
16258 {
16259   FreeSnapshotList();
16260 }
16261
16262 static ListNode *SaveEngineSnapshotBuffers(void)
16263 {
16264   ListNode *buffers = NULL;
16265
16266   // copy some special values to a structure better suited for the snapshot
16267
16268   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16269     SaveEngineSnapshotValues_RND();
16270   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16271     SaveEngineSnapshotValues_EM();
16272   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16273     SaveEngineSnapshotValues_SP(&buffers);
16274   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16275     SaveEngineSnapshotValues_MM();
16276
16277   // save values stored in special snapshot structure
16278
16279   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16280     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16281   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16282     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16283   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16284     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16285   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16286     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16287
16288   // save further RND engine values
16289
16290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16293
16294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16295   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16296   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16297   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16299   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16300
16301   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16302   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16303   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16304
16305   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16306
16307   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16308   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16309
16310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16312   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16314   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16318   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16320   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16323   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16324   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16326   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16327   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16328
16329   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16330   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16331
16332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16333   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16335
16336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16337   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16338
16339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16340   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16342   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16343   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16344   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16345
16346   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16347   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16348
16349 #if 0
16350   ListNode *node = engine_snapshot_list_rnd;
16351   int num_bytes = 0;
16352
16353   while (node != NULL)
16354   {
16355     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16356
16357     node = node->next;
16358   }
16359
16360   Debug("game:playing:SaveEngineSnapshotBuffers",
16361         "size of engine snapshot: %d bytes", num_bytes);
16362 #endif
16363
16364   return buffers;
16365 }
16366
16367 void SaveEngineSnapshotSingle(void)
16368 {
16369   ListNode *buffers = SaveEngineSnapshotBuffers();
16370
16371   // finally save all snapshot buffers to single snapshot
16372   SaveSnapshotSingle(buffers);
16373
16374   // save level identification information
16375   setString(&snapshot_level_identifier, leveldir_current->identifier);
16376   snapshot_level_nr = level_nr;
16377 }
16378
16379 boolean CheckSaveEngineSnapshotToList(void)
16380 {
16381   boolean save_snapshot =
16382     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16383      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16384       game.snapshot.changed_action) ||
16385      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16386       game.snapshot.collected_item));
16387
16388   game.snapshot.changed_action = FALSE;
16389   game.snapshot.collected_item = FALSE;
16390   game.snapshot.save_snapshot = save_snapshot;
16391
16392   return save_snapshot;
16393 }
16394
16395 void SaveEngineSnapshotToList(void)
16396 {
16397   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16398       tape.quick_resume)
16399     return;
16400
16401   ListNode *buffers = SaveEngineSnapshotBuffers();
16402
16403   // finally save all snapshot buffers to snapshot list
16404   SaveSnapshotToList(buffers);
16405 }
16406
16407 void SaveEngineSnapshotToListInitial(void)
16408 {
16409   FreeEngineSnapshotList();
16410
16411   SaveEngineSnapshotToList();
16412 }
16413
16414 static void LoadEngineSnapshotValues(void)
16415 {
16416   // restore special values from snapshot structure
16417
16418   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16419     LoadEngineSnapshotValues_RND();
16420   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16421     LoadEngineSnapshotValues_EM();
16422   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16423     LoadEngineSnapshotValues_SP();
16424   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16425     LoadEngineSnapshotValues_MM();
16426 }
16427
16428 void LoadEngineSnapshotSingle(void)
16429 {
16430   LoadSnapshotSingle();
16431
16432   LoadEngineSnapshotValues();
16433 }
16434
16435 static void LoadEngineSnapshot_Undo(int steps)
16436 {
16437   LoadSnapshotFromList_Older(steps);
16438
16439   LoadEngineSnapshotValues();
16440 }
16441
16442 static void LoadEngineSnapshot_Redo(int steps)
16443 {
16444   LoadSnapshotFromList_Newer(steps);
16445
16446   LoadEngineSnapshotValues();
16447 }
16448
16449 boolean CheckEngineSnapshotSingle(void)
16450 {
16451   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16452           snapshot_level_nr == level_nr);
16453 }
16454
16455 boolean CheckEngineSnapshotList(void)
16456 {
16457   return CheckSnapshotList();
16458 }
16459
16460
16461 // ---------- new game button stuff -------------------------------------------
16462
16463 static struct
16464 {
16465   int graphic;
16466   struct XY *pos;
16467   int gadget_id;
16468   boolean *setup_value;
16469   boolean allowed_on_tape;
16470   boolean is_touch_button;
16471   char *infotext;
16472 } gamebutton_info[NUM_GAME_BUTTONS] =
16473 {
16474   {
16475     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16476     GAME_CTRL_ID_STOP,                          NULL,
16477     TRUE, FALSE,                                "stop game"
16478   },
16479   {
16480     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16481     GAME_CTRL_ID_PAUSE,                         NULL,
16482     TRUE, FALSE,                                "pause game"
16483   },
16484   {
16485     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16486     GAME_CTRL_ID_PLAY,                          NULL,
16487     TRUE, FALSE,                                "play game"
16488   },
16489   {
16490     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16491     GAME_CTRL_ID_UNDO,                          NULL,
16492     TRUE, FALSE,                                "undo step"
16493   },
16494   {
16495     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16496     GAME_CTRL_ID_REDO,                          NULL,
16497     TRUE, FALSE,                                "redo step"
16498   },
16499   {
16500     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16501     GAME_CTRL_ID_SAVE,                          NULL,
16502     TRUE, FALSE,                                "save game"
16503   },
16504   {
16505     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16506     GAME_CTRL_ID_PAUSE2,                        NULL,
16507     TRUE, FALSE,                                "pause game"
16508   },
16509   {
16510     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16511     GAME_CTRL_ID_LOAD,                          NULL,
16512     TRUE, FALSE,                                "load game"
16513   },
16514   {
16515     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16516     GAME_CTRL_ID_RESTART,                       NULL,
16517     TRUE, FALSE,                                "restart game"
16518   },
16519   {
16520     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16521     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16522     FALSE, FALSE,                               "stop game"
16523   },
16524   {
16525     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16526     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16527     FALSE, FALSE,                               "pause game"
16528   },
16529   {
16530     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16531     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16532     FALSE, FALSE,                               "play game"
16533   },
16534   {
16535     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16536     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16537     FALSE, FALSE,                               "restart game"
16538   },
16539   {
16540     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16541     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16542     FALSE, TRUE,                                "stop game"
16543   },
16544   {
16545     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16546     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16547     FALSE, TRUE,                                "pause game"
16548   },
16549   {
16550     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16551     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16552     FALSE, TRUE,                                "restart game"
16553   },
16554   {
16555     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16556     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16557     TRUE, FALSE,                                "background music on/off"
16558   },
16559   {
16560     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16561     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16562     TRUE, FALSE,                                "sound loops on/off"
16563   },
16564   {
16565     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16566     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16567     TRUE, FALSE,                                "normal sounds on/off"
16568   },
16569   {
16570     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16571     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16572     FALSE, FALSE,                               "background music on/off"
16573   },
16574   {
16575     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16576     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16577     FALSE, FALSE,                               "sound loops on/off"
16578   },
16579   {
16580     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16581     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16582     FALSE, FALSE,                               "normal sounds on/off"
16583   }
16584 };
16585
16586 void CreateGameButtons(void)
16587 {
16588   int i;
16589
16590   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16591   {
16592     int graphic = gamebutton_info[i].graphic;
16593     struct GraphicInfo *gfx = &graphic_info[graphic];
16594     struct XY *pos = gamebutton_info[i].pos;
16595     struct GadgetInfo *gi;
16596     int button_type;
16597     boolean checked;
16598     unsigned int event_mask;
16599     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16600     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16601     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16602     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16603     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16604     int gd_x   = gfx->src_x;
16605     int gd_y   = gfx->src_y;
16606     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16607     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16608     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16609     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16610     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16611     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16612     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16613     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16614     int id = i;
16615
16616     // do not use touch buttons if overlay touch buttons are disabled
16617     if (is_touch_button && !setup.touch.overlay_buttons)
16618       continue;
16619
16620     if (gfx->bitmap == NULL)
16621     {
16622       game_gadget[id] = NULL;
16623
16624       continue;
16625     }
16626
16627     if (id == GAME_CTRL_ID_STOP ||
16628         id == GAME_CTRL_ID_PANEL_STOP ||
16629         id == GAME_CTRL_ID_TOUCH_STOP ||
16630         id == GAME_CTRL_ID_PLAY ||
16631         id == GAME_CTRL_ID_PANEL_PLAY ||
16632         id == GAME_CTRL_ID_SAVE ||
16633         id == GAME_CTRL_ID_LOAD ||
16634         id == GAME_CTRL_ID_RESTART ||
16635         id == GAME_CTRL_ID_PANEL_RESTART ||
16636         id == GAME_CTRL_ID_TOUCH_RESTART)
16637     {
16638       button_type = GD_TYPE_NORMAL_BUTTON;
16639       checked = FALSE;
16640       event_mask = GD_EVENT_RELEASED;
16641     }
16642     else if (id == GAME_CTRL_ID_UNDO ||
16643              id == GAME_CTRL_ID_REDO)
16644     {
16645       button_type = GD_TYPE_NORMAL_BUTTON;
16646       checked = FALSE;
16647       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16648     }
16649     else
16650     {
16651       button_type = GD_TYPE_CHECK_BUTTON;
16652       checked = (gamebutton_info[i].setup_value != NULL ?
16653                  *gamebutton_info[i].setup_value : FALSE);
16654       event_mask = GD_EVENT_PRESSED;
16655     }
16656
16657     gi = CreateGadget(GDI_CUSTOM_ID, id,
16658                       GDI_IMAGE_ID, graphic,
16659                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16660                       GDI_X, base_x + x,
16661                       GDI_Y, base_y + y,
16662                       GDI_WIDTH, gfx->width,
16663                       GDI_HEIGHT, gfx->height,
16664                       GDI_TYPE, button_type,
16665                       GDI_STATE, GD_BUTTON_UNPRESSED,
16666                       GDI_CHECKED, checked,
16667                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16668                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16669                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16670                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16671                       GDI_DIRECT_DRAW, FALSE,
16672                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16673                       GDI_EVENT_MASK, event_mask,
16674                       GDI_CALLBACK_ACTION, HandleGameButtons,
16675                       GDI_END);
16676
16677     if (gi == NULL)
16678       Fail("cannot create gadget");
16679
16680     game_gadget[id] = gi;
16681   }
16682 }
16683
16684 void FreeGameButtons(void)
16685 {
16686   int i;
16687
16688   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16689     FreeGadget(game_gadget[i]);
16690 }
16691
16692 static void UnmapGameButtonsAtSamePosition(int id)
16693 {
16694   int i;
16695
16696   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16697     if (i != id &&
16698         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16699         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16700       UnmapGadget(game_gadget[i]);
16701 }
16702
16703 static void UnmapGameButtonsAtSamePosition_All(void)
16704 {
16705   if (setup.show_load_save_buttons)
16706   {
16707     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16708     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16709     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16710   }
16711   else if (setup.show_undo_redo_buttons)
16712   {
16713     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16714     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16715     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16716   }
16717   else
16718   {
16719     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16720     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16721     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16722
16723     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16724     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16725     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16726   }
16727 }
16728
16729 void MapLoadSaveButtons(void)
16730 {
16731   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16732   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16733
16734   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16735   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16736 }
16737
16738 void MapUndoRedoButtons(void)
16739 {
16740   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16741   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16742
16743   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16744   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16745 }
16746
16747 void ModifyPauseButtons(void)
16748 {
16749   static int ids[] =
16750   {
16751     GAME_CTRL_ID_PAUSE,
16752     GAME_CTRL_ID_PAUSE2,
16753     GAME_CTRL_ID_PANEL_PAUSE,
16754     GAME_CTRL_ID_TOUCH_PAUSE,
16755     -1
16756   };
16757   int i;
16758
16759   // do not redraw pause button on closed door (may happen when restarting game)
16760   if (!(GetDoorState() & DOOR_OPEN_1))
16761     return;
16762
16763   for (i = 0; ids[i] > -1; i++)
16764     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16765 }
16766
16767 static void MapGameButtonsExt(boolean on_tape)
16768 {
16769   int i;
16770
16771   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16772   {
16773     if ((i == GAME_CTRL_ID_UNDO ||
16774          i == GAME_CTRL_ID_REDO) &&
16775         game_status != GAME_MODE_PLAYING)
16776       continue;
16777
16778     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16779       MapGadget(game_gadget[i]);
16780   }
16781
16782   UnmapGameButtonsAtSamePosition_All();
16783
16784   RedrawGameButtons();
16785 }
16786
16787 static void UnmapGameButtonsExt(boolean on_tape)
16788 {
16789   int i;
16790
16791   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16792     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16793       UnmapGadget(game_gadget[i]);
16794 }
16795
16796 static void RedrawGameButtonsExt(boolean on_tape)
16797 {
16798   int i;
16799
16800   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16801     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16802       RedrawGadget(game_gadget[i]);
16803 }
16804
16805 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16806 {
16807   if (gi == NULL)
16808     return;
16809
16810   gi->checked = state;
16811 }
16812
16813 static void RedrawSoundButtonGadget(int id)
16814 {
16815   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16816              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16817              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16818              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16819              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16820              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16821              id);
16822
16823   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16824   RedrawGadget(game_gadget[id2]);
16825 }
16826
16827 void MapGameButtons(void)
16828 {
16829   MapGameButtonsExt(FALSE);
16830 }
16831
16832 void UnmapGameButtons(void)
16833 {
16834   UnmapGameButtonsExt(FALSE);
16835 }
16836
16837 void RedrawGameButtons(void)
16838 {
16839   RedrawGameButtonsExt(FALSE);
16840 }
16841
16842 void MapGameButtonsOnTape(void)
16843 {
16844   MapGameButtonsExt(TRUE);
16845 }
16846
16847 void UnmapGameButtonsOnTape(void)
16848 {
16849   UnmapGameButtonsExt(TRUE);
16850 }
16851
16852 void RedrawGameButtonsOnTape(void)
16853 {
16854   RedrawGameButtonsExt(TRUE);
16855 }
16856
16857 static void GameUndoRedoExt(void)
16858 {
16859   ClearPlayerAction();
16860
16861   tape.pausing = TRUE;
16862
16863   RedrawPlayfield();
16864   UpdateAndDisplayGameControlValues();
16865
16866   DrawCompleteVideoDisplay();
16867   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16868   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16869   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16870
16871   ModifyPauseButtons();
16872
16873   BackToFront();
16874 }
16875
16876 static void GameUndo(int steps)
16877 {
16878   if (!CheckEngineSnapshotList())
16879     return;
16880
16881   int tape_property_bits = tape.property_bits;
16882
16883   LoadEngineSnapshot_Undo(steps);
16884
16885   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16886
16887   GameUndoRedoExt();
16888 }
16889
16890 static void GameRedo(int steps)
16891 {
16892   if (!CheckEngineSnapshotList())
16893     return;
16894
16895   int tape_property_bits = tape.property_bits;
16896
16897   LoadEngineSnapshot_Redo(steps);
16898
16899   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16900
16901   GameUndoRedoExt();
16902 }
16903
16904 static void HandleGameButtonsExt(int id, int button)
16905 {
16906   static boolean game_undo_executed = FALSE;
16907   int steps = BUTTON_STEPSIZE(button);
16908   boolean handle_game_buttons =
16909     (game_status == GAME_MODE_PLAYING ||
16910      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16911
16912   if (!handle_game_buttons)
16913     return;
16914
16915   switch (id)
16916   {
16917     case GAME_CTRL_ID_STOP:
16918     case GAME_CTRL_ID_PANEL_STOP:
16919     case GAME_CTRL_ID_TOUCH_STOP:
16920       TapeStopGame();
16921
16922       break;
16923
16924     case GAME_CTRL_ID_PAUSE:
16925     case GAME_CTRL_ID_PAUSE2:
16926     case GAME_CTRL_ID_PANEL_PAUSE:
16927     case GAME_CTRL_ID_TOUCH_PAUSE:
16928       if (network.enabled && game_status == GAME_MODE_PLAYING)
16929       {
16930         if (tape.pausing)
16931           SendToServer_ContinuePlaying();
16932         else
16933           SendToServer_PausePlaying();
16934       }
16935       else
16936         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16937
16938       game_undo_executed = FALSE;
16939
16940       break;
16941
16942     case GAME_CTRL_ID_PLAY:
16943     case GAME_CTRL_ID_PANEL_PLAY:
16944       if (game_status == GAME_MODE_MAIN)
16945       {
16946         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16947       }
16948       else if (tape.pausing)
16949       {
16950         if (network.enabled)
16951           SendToServer_ContinuePlaying();
16952         else
16953           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16954       }
16955       break;
16956
16957     case GAME_CTRL_ID_UNDO:
16958       // Important: When using "save snapshot when collecting an item" mode,
16959       // load last (current) snapshot for first "undo" after pressing "pause"
16960       // (else the last-but-one snapshot would be loaded, because the snapshot
16961       // pointer already points to the last snapshot when pressing "pause",
16962       // which is fine for "every step/move" mode, but not for "every collect")
16963       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16964           !game_undo_executed)
16965         steps--;
16966
16967       game_undo_executed = TRUE;
16968
16969       GameUndo(steps);
16970       break;
16971
16972     case GAME_CTRL_ID_REDO:
16973       GameRedo(steps);
16974       break;
16975
16976     case GAME_CTRL_ID_SAVE:
16977       TapeQuickSave();
16978       break;
16979
16980     case GAME_CTRL_ID_LOAD:
16981       TapeQuickLoad();
16982       break;
16983
16984     case GAME_CTRL_ID_RESTART:
16985     case GAME_CTRL_ID_PANEL_RESTART:
16986     case GAME_CTRL_ID_TOUCH_RESTART:
16987       TapeRestartGame();
16988
16989       break;
16990
16991     case SOUND_CTRL_ID_MUSIC:
16992     case SOUND_CTRL_ID_PANEL_MUSIC:
16993       if (setup.sound_music)
16994       { 
16995         setup.sound_music = FALSE;
16996
16997         FadeMusic();
16998       }
16999       else if (audio.music_available)
17000       { 
17001         setup.sound = setup.sound_music = TRUE;
17002
17003         SetAudioMode(setup.sound);
17004
17005         if (game_status == GAME_MODE_PLAYING)
17006           PlayLevelMusic();
17007       }
17008
17009       RedrawSoundButtonGadget(id);
17010
17011       break;
17012
17013     case SOUND_CTRL_ID_LOOPS:
17014     case SOUND_CTRL_ID_PANEL_LOOPS:
17015       if (setup.sound_loops)
17016         setup.sound_loops = FALSE;
17017       else if (audio.loops_available)
17018       {
17019         setup.sound = setup.sound_loops = TRUE;
17020
17021         SetAudioMode(setup.sound);
17022       }
17023
17024       RedrawSoundButtonGadget(id);
17025
17026       break;
17027
17028     case SOUND_CTRL_ID_SIMPLE:
17029     case SOUND_CTRL_ID_PANEL_SIMPLE:
17030       if (setup.sound_simple)
17031         setup.sound_simple = FALSE;
17032       else if (audio.sound_available)
17033       {
17034         setup.sound = setup.sound_simple = TRUE;
17035
17036         SetAudioMode(setup.sound);
17037       }
17038
17039       RedrawSoundButtonGadget(id);
17040
17041       break;
17042
17043     default:
17044       break;
17045   }
17046 }
17047
17048 static void HandleGameButtons(struct GadgetInfo *gi)
17049 {
17050   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17051 }
17052
17053 void HandleSoundButtonKeys(Key key)
17054 {
17055   if (key == setup.shortcut.sound_simple)
17056     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17057   else if (key == setup.shortcut.sound_loops)
17058     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17059   else if (key == setup.shortcut.sound_music)
17060     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17061 }