cleanup of BD style game elements in level editor
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_GEMS_NEEDED                  2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_NEEDED,
250     &game.panel.gems_needed,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void InitFieldForEngine_RND(int x, int y)
1839 {
1840   int element = Tile[x][y];
1841
1842   // convert BD engine elements to corresponding R'n'D engine elements
1843   element = (element == EL_BD_EMPTY             ? EL_EMPTY :
1844              element == EL_BD_PLAYER            ? EL_PLAYER_1 :
1845              element == EL_BD_INBOX             ? EL_PLAYER_1 :
1846              element == EL_BD_SAND              ? EL_SAND :
1847              element == EL_BD_STEELWALL         ? EL_STEELWALL :
1848              element == EL_BD_EXIT_CLOSED       ? EL_EXIT_CLOSED :
1849              element == EL_BD_EXIT_OPEN         ? EL_EXIT_OPEN :
1850              element);
1851
1852   Tile[x][y] = element;
1853 }
1854
1855 static void InitFieldForEngine(int x, int y)
1856 {
1857   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1858     InitFieldForEngine_RND(x, y);
1859 }
1860
1861 static void InitField(int x, int y, boolean init_game)
1862 {
1863   int element = Tile[x][y];
1864
1865   switch (element)
1866   {
1867     case EL_SP_MURPHY:
1868     case EL_PLAYER_1:
1869     case EL_PLAYER_2:
1870     case EL_PLAYER_3:
1871     case EL_PLAYER_4:
1872       InitPlayerField(x, y, element, init_game);
1873       break;
1874
1875     case EL_SOKOBAN_FIELD_PLAYER:
1876       element = Tile[x][y] = EL_PLAYER_1;
1877       InitField(x, y, init_game);
1878
1879       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1880       InitField(x, y, init_game);
1881       break;
1882
1883     case EL_SOKOBAN_FIELD_EMPTY:
1884       IncrementSokobanFieldsNeeded();
1885       break;
1886
1887     case EL_SOKOBAN_OBJECT:
1888       IncrementSokobanObjectsNeeded();
1889       break;
1890
1891     case EL_STONEBLOCK:
1892       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1893         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1894       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1895         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1896       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1897         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1898       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1899         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1900       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1901         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1902       break;
1903
1904     case EL_BUG:
1905     case EL_BUG_RIGHT:
1906     case EL_BUG_UP:
1907     case EL_BUG_LEFT:
1908     case EL_BUG_DOWN:
1909     case EL_SPACESHIP:
1910     case EL_SPACESHIP_RIGHT:
1911     case EL_SPACESHIP_UP:
1912     case EL_SPACESHIP_LEFT:
1913     case EL_SPACESHIP_DOWN:
1914     case EL_BD_BUTTERFLY:
1915     case EL_BD_BUTTERFLY_RIGHT:
1916     case EL_BD_BUTTERFLY_UP:
1917     case EL_BD_BUTTERFLY_LEFT:
1918     case EL_BD_BUTTERFLY_DOWN:
1919     case EL_BD_FIREFLY:
1920     case EL_BD_FIREFLY_RIGHT:
1921     case EL_BD_FIREFLY_UP:
1922     case EL_BD_FIREFLY_LEFT:
1923     case EL_BD_FIREFLY_DOWN:
1924     case EL_PACMAN_RIGHT:
1925     case EL_PACMAN_UP:
1926     case EL_PACMAN_LEFT:
1927     case EL_PACMAN_DOWN:
1928     case EL_YAMYAM:
1929     case EL_YAMYAM_LEFT:
1930     case EL_YAMYAM_RIGHT:
1931     case EL_YAMYAM_UP:
1932     case EL_YAMYAM_DOWN:
1933     case EL_DARK_YAMYAM:
1934     case EL_ROBOT:
1935     case EL_PACMAN:
1936     case EL_SP_SNIKSNAK:
1937     case EL_SP_ELECTRON:
1938     case EL_MOLE:
1939     case EL_MOLE_LEFT:
1940     case EL_MOLE_RIGHT:
1941     case EL_MOLE_UP:
1942     case EL_MOLE_DOWN:
1943     case EL_SPRING_LEFT:
1944     case EL_SPRING_RIGHT:
1945       InitMovDir(x, y);
1946       break;
1947
1948     case EL_AMOEBA_FULL:
1949     case EL_BD_AMOEBA:
1950       InitAmoebaNr(x, y);
1951       break;
1952
1953     case EL_AMOEBA_DROP:
1954       if (y == lev_fieldy - 1)
1955       {
1956         Tile[x][y] = EL_AMOEBA_GROWING;
1957         Store[x][y] = EL_AMOEBA_WET;
1958       }
1959       break;
1960
1961     case EL_DYNAMITE_ACTIVE:
1962     case EL_SP_DISK_RED_ACTIVE:
1963     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1964     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1965     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1966     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1967       MovDelay[x][y] = 96;
1968       break;
1969
1970     case EL_EM_DYNAMITE_ACTIVE:
1971       MovDelay[x][y] = 32;
1972       break;
1973
1974     case EL_LAMP:
1975       game.lights_still_needed++;
1976       break;
1977
1978     case EL_PENGUIN:
1979       game.friends_still_needed++;
1980       break;
1981
1982     case EL_PIG:
1983     case EL_DRAGON:
1984       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1985       break;
1986
1987     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1988     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1989     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1990     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1991     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1992     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1993     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1994     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1995     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1996     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1997     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1998     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1999       if (init_game)
2000       {
2001         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2002         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2003         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2004
2005         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2006         {
2007           game.belt_dir[belt_nr] = belt_dir;
2008           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2009         }
2010         else    // more than one switch -- set it like the first switch
2011         {
2012           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2013         }
2014       }
2015       break;
2016
2017     case EL_LIGHT_SWITCH_ACTIVE:
2018       if (init_game)
2019         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2020       break;
2021
2022     case EL_INVISIBLE_STEELWALL:
2023     case EL_INVISIBLE_WALL:
2024     case EL_INVISIBLE_SAND:
2025       if (game.light_time_left > 0 ||
2026           game.lenses_time_left > 0)
2027         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2028       break;
2029
2030     case EL_EMC_MAGIC_BALL:
2031       if (game.ball_active)
2032         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2033       break;
2034
2035     case EL_EMC_MAGIC_BALL_SWITCH:
2036       if (game.ball_active)
2037         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2038       break;
2039
2040     case EL_TRIGGER_PLAYER:
2041     case EL_TRIGGER_ELEMENT:
2042     case EL_TRIGGER_CE_VALUE:
2043     case EL_TRIGGER_CE_SCORE:
2044     case EL_SELF:
2045     case EL_ANY_ELEMENT:
2046     case EL_CURRENT_CE_VALUE:
2047     case EL_CURRENT_CE_SCORE:
2048     case EL_PREV_CE_1:
2049     case EL_PREV_CE_2:
2050     case EL_PREV_CE_3:
2051     case EL_PREV_CE_4:
2052     case EL_PREV_CE_5:
2053     case EL_PREV_CE_6:
2054     case EL_PREV_CE_7:
2055     case EL_PREV_CE_8:
2056     case EL_NEXT_CE_1:
2057     case EL_NEXT_CE_2:
2058     case EL_NEXT_CE_3:
2059     case EL_NEXT_CE_4:
2060     case EL_NEXT_CE_5:
2061     case EL_NEXT_CE_6:
2062     case EL_NEXT_CE_7:
2063     case EL_NEXT_CE_8:
2064       // reference elements should not be used on the playfield
2065       Tile[x][y] = EL_EMPTY;
2066       break;
2067
2068     default:
2069       if (IS_CUSTOM_ELEMENT(element))
2070       {
2071         if (CAN_MOVE(element))
2072           InitMovDir(x, y);
2073
2074         if (!element_info[element].use_last_ce_value || init_game)
2075           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2076       }
2077       else if (IS_GROUP_ELEMENT(element))
2078       {
2079         Tile[x][y] = GetElementFromGroupElement(element);
2080
2081         InitField(x, y, init_game);
2082       }
2083       else if (IS_EMPTY_ELEMENT(element))
2084       {
2085         GfxElementEmpty[x][y] = element;
2086         Tile[x][y] = EL_EMPTY;
2087
2088         if (element_info[element].use_gfx_element)
2089           game.use_masked_elements = TRUE;
2090       }
2091
2092       break;
2093   }
2094
2095   if (!init_game)
2096     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2097 }
2098
2099 static void InitField_WithBug1(int x, int y, boolean init_game)
2100 {
2101   InitField(x, y, init_game);
2102
2103   // not needed to call InitMovDir() -- already done by InitField()!
2104   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2105       CAN_MOVE(Tile[x][y]))
2106     InitMovDir(x, y);
2107 }
2108
2109 static void InitField_WithBug2(int x, int y, boolean init_game)
2110 {
2111   int old_element = Tile[x][y];
2112
2113   InitField(x, y, init_game);
2114
2115   // not needed to call InitMovDir() -- already done by InitField()!
2116   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2117       CAN_MOVE(old_element) &&
2118       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2119     InitMovDir(x, y);
2120
2121   /* this case is in fact a combination of not less than three bugs:
2122      first, it calls InitMovDir() for elements that can move, although this is
2123      already done by InitField(); then, it checks the element that was at this
2124      field _before_ the call to InitField() (which can change it); lastly, it
2125      was not called for "mole with direction" elements, which were treated as
2126      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2127   */
2128 }
2129
2130 static int get_key_element_from_nr(int key_nr)
2131 {
2132   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2133                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                           EL_EM_KEY_1 : EL_KEY_1);
2135
2136   return key_base_element + key_nr;
2137 }
2138
2139 static int get_next_dropped_element(struct PlayerInfo *player)
2140 {
2141   return (player->inventory_size > 0 ?
2142           player->inventory_element[player->inventory_size - 1] :
2143           player->inventory_infinite_element != EL_UNDEFINED ?
2144           player->inventory_infinite_element :
2145           player->dynabombs_left > 0 ?
2146           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2147           EL_UNDEFINED);
2148 }
2149
2150 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2151 {
2152   // pos >= 0: get element from bottom of the stack;
2153   // pos <  0: get element from top of the stack
2154
2155   if (pos < 0)
2156   {
2157     int min_inventory_size = -pos;
2158     int inventory_pos = player->inventory_size - min_inventory_size;
2159     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2160
2161     return (player->inventory_size >= min_inventory_size ?
2162             player->inventory_element[inventory_pos] :
2163             player->inventory_infinite_element != EL_UNDEFINED ?
2164             player->inventory_infinite_element :
2165             player->dynabombs_left >= min_dynabombs_left ?
2166             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2167             EL_UNDEFINED);
2168   }
2169   else
2170   {
2171     int min_dynabombs_left = pos + 1;
2172     int min_inventory_size = pos + 1 - player->dynabombs_left;
2173     int inventory_pos = pos - player->dynabombs_left;
2174
2175     return (player->inventory_infinite_element != EL_UNDEFINED ?
2176             player->inventory_infinite_element :
2177             player->dynabombs_left >= min_dynabombs_left ?
2178             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2179             player->inventory_size >= min_inventory_size ?
2180             player->inventory_element[inventory_pos] :
2181             EL_UNDEFINED);
2182   }
2183 }
2184
2185 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2186 {
2187   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2188   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2189   int compare_result;
2190
2191   if (gpo1->sort_priority != gpo2->sort_priority)
2192     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2193   else
2194     compare_result = gpo1->nr - gpo2->nr;
2195
2196   return compare_result;
2197 }
2198
2199 int getPlayerInventorySize(int player_nr)
2200 {
2201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202     return game_em.ply[player_nr]->dynamite;
2203   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2204     return game_sp.red_disk_count;
2205   else
2206     return stored_player[player_nr].inventory_size;
2207 }
2208
2209 static void InitGameControlValues(void)
2210 {
2211   int i;
2212
2213   for (i = 0; game_panel_controls[i].nr != -1; i++)
2214   {
2215     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2216     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2217     struct TextPosInfo *pos = gpc->pos;
2218     int nr = gpc->nr;
2219     int type = gpc->type;
2220
2221     if (nr != i)
2222     {
2223       Error("'game_panel_controls' structure corrupted at %d", i);
2224
2225       Fail("this should not happen -- please debug");
2226     }
2227
2228     // force update of game controls after initialization
2229     gpc->value = gpc->last_value = -1;
2230     gpc->frame = gpc->last_frame = -1;
2231     gpc->gfx_frame = -1;
2232
2233     // determine panel value width for later calculation of alignment
2234     if (type == TYPE_INTEGER || type == TYPE_STRING)
2235     {
2236       pos->width = pos->size * getFontWidth(pos->font);
2237       pos->height = getFontHeight(pos->font);
2238     }
2239     else if (type == TYPE_ELEMENT)
2240     {
2241       pos->width = pos->size;
2242       pos->height = pos->size;
2243     }
2244
2245     // fill structure for game panel draw order
2246     gpo->nr = gpc->nr;
2247     gpo->sort_priority = pos->sort_priority;
2248   }
2249
2250   // sort game panel controls according to sort_priority and control number
2251   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2252         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2253 }
2254
2255 static void UpdatePlayfieldElementCount(void)
2256 {
2257   boolean use_element_count = FALSE;
2258   int i, j, x, y;
2259
2260   // first check if it is needed at all to calculate playfield element count
2261   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2262     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2263       use_element_count = TRUE;
2264
2265   if (!use_element_count)
2266     return;
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269     element_info[i].element_count = 0;
2270
2271   SCAN_PLAYFIELD(x, y)
2272   {
2273     element_info[Tile[x][y]].element_count++;
2274   }
2275
2276   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2277     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2278       if (IS_IN_GROUP(j, i))
2279         element_info[EL_GROUP_START + i].element_count +=
2280           element_info[j].element_count;
2281 }
2282
2283 static void UpdateGameControlValues(void)
2284 {
2285   int i, k;
2286   int time = (game.LevelSolved ?
2287               game.LevelSolved_CountingTime :
2288               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2289               game_bd.time_played :
2290               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2291               game_em.lev->time :
2292               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2293               game_sp.time_played :
2294               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2295               game_mm.energy_left :
2296               game.no_level_time_limit ? TimePlayed : TimeLeft);
2297   int score = (game.LevelSolved ?
2298                game.LevelSolved_CountingScore :
2299                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2300                game_bd.score :
2301                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2302                game_em.lev->score :
2303                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2304                game_sp.score :
2305                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2306                game_mm.score :
2307                game.score);
2308   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2309               game_bd.gems_still_needed :
2310               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2311               game_em.lev->gems_needed :
2312               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2313               game_sp.infotrons_still_needed :
2314               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2315               game_mm.kettles_still_needed :
2316               game.gems_still_needed);
2317   int gems_needed = level.gems_needed;
2318   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2319                         game_bd.game->cave->diamonds_collected :
2320                         gems_needed - gems);
2321   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2322                     game_bd.game->cave->diamond_value :
2323                     level.score[SC_EMERALD]);
2324   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2325                      game_bd.gems_still_needed > 0 :
2326                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2327                      game_em.lev->gems_needed > 0 :
2328                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2329                      game_sp.infotrons_still_needed > 0 :
2330                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2331                      game_mm.kettles_still_needed > 0 ||
2332                      game_mm.lights_still_needed > 0 :
2333                      game.gems_still_needed > 0 ||
2334                      game.sokoban_fields_still_needed > 0 ||
2335                      game.sokoban_objects_still_needed > 0 ||
2336                      game.lights_still_needed > 0);
2337   int health = (game.LevelSolved ?
2338                 game.LevelSolved_CountingHealth :
2339                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2340                 MM_HEALTH(game_mm.laser_overload_value) :
2341                 game.health);
2342   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2343
2344   UpdatePlayfieldElementCount();
2345
2346   // update game panel control values
2347
2348   // used instead of "level_nr" (for network games)
2349   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2350   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2351   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2352   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2353   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2354
2355   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2356   for (i = 0; i < MAX_NUM_KEYS; i++)
2357     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2358   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2359   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2360
2361   if (game.centered_player_nr == -1)
2362   {
2363     for (i = 0; i < MAX_PLAYERS; i++)
2364     {
2365       // only one player in Supaplex game engine
2366       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2367         break;
2368
2369       for (k = 0; k < MAX_NUM_KEYS; k++)
2370       {
2371         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2372         {
2373           if (game_em.ply[i]->keys & (1 << k))
2374             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2375               get_key_element_from_nr(k);
2376         }
2377         else if (stored_player[i].key[k])
2378           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2379             get_key_element_from_nr(k);
2380       }
2381
2382       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2383         getPlayerInventorySize(i);
2384
2385       if (stored_player[i].num_white_keys > 0)
2386         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2387           EL_DC_KEY_WHITE;
2388
2389       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2390         stored_player[i].num_white_keys;
2391     }
2392   }
2393   else
2394   {
2395     int player_nr = game.centered_player_nr;
2396
2397     for (k = 0; k < MAX_NUM_KEYS; k++)
2398     {
2399       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2400       {
2401         if (game_em.ply[player_nr]->keys & (1 << k))
2402           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2403             get_key_element_from_nr(k);
2404       }
2405       else if (stored_player[player_nr].key[k])
2406         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2407           get_key_element_from_nr(k);
2408     }
2409
2410     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2411       getPlayerInventorySize(player_nr);
2412
2413     if (stored_player[player_nr].num_white_keys > 0)
2414       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2415
2416     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2417       stored_player[player_nr].num_white_keys;
2418   }
2419
2420   // re-arrange keys on game panel, if needed or if defined by style settings
2421   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2422   {
2423     int nr = GAME_PANEL_KEY_1 + i;
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2425     struct TextPosInfo *pos = gpc->pos;
2426
2427     // skip check if key is not in the player's inventory
2428     if (gpc->value == EL_EMPTY)
2429       continue;
2430
2431     // check if keys should be arranged on panel from left to right
2432     if (pos->style == STYLE_LEFTMOST_POSITION)
2433     {
2434       // check previous key positions (left from current key)
2435       for (k = 0; k < i; k++)
2436       {
2437         int nr_new = GAME_PANEL_KEY_1 + k;
2438
2439         if (game_panel_controls[nr_new].value == EL_EMPTY)
2440         {
2441           game_panel_controls[nr_new].value = gpc->value;
2442           gpc->value = EL_EMPTY;
2443
2444           break;
2445         }
2446       }
2447     }
2448
2449     // check if "undefined" keys can be placed at some other position
2450     if (pos->x == -1 && pos->y == -1)
2451     {
2452       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2453
2454       // 1st try: display key at the same position as normal or EM keys
2455       if (game_panel_controls[nr_new].value == EL_EMPTY)
2456       {
2457         game_panel_controls[nr_new].value = gpc->value;
2458       }
2459       else
2460       {
2461         // 2nd try: display key at the next free position in the key panel
2462         for (k = 0; k < STD_NUM_KEYS; k++)
2463         {
2464           nr_new = GAME_PANEL_KEY_1 + k;
2465
2466           if (game_panel_controls[nr_new].value == EL_EMPTY)
2467           {
2468             game_panel_controls[nr_new].value = gpc->value;
2469
2470             break;
2471           }
2472         }
2473       }
2474     }
2475   }
2476
2477   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2478   {
2479     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2480       get_inventory_element_from_pos(local_player, i);
2481     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2482       get_inventory_element_from_pos(local_player, -i - 1);
2483   }
2484
2485   game_panel_controls[GAME_PANEL_SCORE].value = score;
2486   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2487
2488   game_panel_controls[GAME_PANEL_TIME].value = time;
2489
2490   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2491   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2492   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2493
2494   if (level.time == 0)
2495     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2496   else
2497     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2498
2499   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2500   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2501
2502   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2503
2504   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2505     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2506      EL_EMPTY);
2507   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2508     local_player->shield_normal_time_left;
2509   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2510     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2511      EL_EMPTY);
2512   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2513     local_player->shield_deadly_time_left;
2514
2515   game_panel_controls[GAME_PANEL_EXIT].value =
2516     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2517
2518   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2519     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2520   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2521     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2522      EL_EMC_MAGIC_BALL_SWITCH);
2523
2524   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2525     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2526   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2527     game.light_time_left;
2528
2529   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2530     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2531   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2532     game.timegate_time_left;
2533
2534   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2535     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2536
2537   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2538     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2539   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2540     game.lenses_time_left;
2541
2542   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2543     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2544   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2545     game.magnify_time_left;
2546
2547   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2548     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2549      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2550      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2551      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2552      EL_BALLOON_SWITCH_NONE);
2553
2554   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2555     local_player->dynabomb_count;
2556   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2557     local_player->dynabomb_size;
2558   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2559     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2560
2561   game_panel_controls[GAME_PANEL_PENGUINS].value =
2562     game.friends_still_needed;
2563
2564   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2565     game.sokoban_objects_still_needed;
2566   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2567     game.sokoban_fields_still_needed;
2568
2569   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2570     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2571
2572   for (i = 0; i < NUM_BELTS; i++)
2573   {
2574     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2575       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2576        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2577     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2578       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2579   }
2580
2581   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2582     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2583   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2584     game.magic_wall_time_left;
2585
2586   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2587     local_player->gravity;
2588
2589   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2590     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2591
2592   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2593     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2594       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2595        game.panel.element[i].id : EL_UNDEFINED);
2596
2597   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2598     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2599       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2600        element_info[game.panel.element_count[i].id].element_count : 0);
2601
2602   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2603     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2604       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2605        element_info[game.panel.ce_score[i].id].collect_score : 0);
2606
2607   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2608     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2609       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2610        element_info[game.panel.ce_score_element[i].id].collect_score :
2611        EL_UNDEFINED);
2612
2613   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2614   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2615   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2616
2617   // update game panel control frames
2618
2619   for (i = 0; game_panel_controls[i].nr != -1; i++)
2620   {
2621     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2622
2623     if (gpc->type == TYPE_ELEMENT)
2624     {
2625       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2626       {
2627         int last_anim_random_frame = gfx.anim_random_frame;
2628         int element = gpc->value;
2629         int graphic = el2panelimg(element);
2630         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2631                                sync_random_frame :
2632                                graphic_info[graphic].anim_global_anim_sync ?
2633                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2634
2635         if (gpc->value != gpc->last_value)
2636         {
2637           gpc->gfx_frame = 0;
2638           gpc->gfx_random = init_gfx_random;
2639         }
2640         else
2641         {
2642           gpc->gfx_frame++;
2643
2644           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2645               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2646             gpc->gfx_random = init_gfx_random;
2647         }
2648
2649         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2650           gfx.anim_random_frame = gpc->gfx_random;
2651
2652         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2653           gpc->gfx_frame = element_info[element].collect_score;
2654
2655         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2656
2657         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2658           gfx.anim_random_frame = last_anim_random_frame;
2659       }
2660     }
2661     else if (gpc->type == TYPE_GRAPHIC)
2662     {
2663       if (gpc->graphic != IMG_UNDEFINED)
2664       {
2665         int last_anim_random_frame = gfx.anim_random_frame;
2666         int graphic = gpc->graphic;
2667         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2668                                sync_random_frame :
2669                                graphic_info[graphic].anim_global_anim_sync ?
2670                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2671
2672         if (gpc->value != gpc->last_value)
2673         {
2674           gpc->gfx_frame = 0;
2675           gpc->gfx_random = init_gfx_random;
2676         }
2677         else
2678         {
2679           gpc->gfx_frame++;
2680
2681           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2682               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2683             gpc->gfx_random = init_gfx_random;
2684         }
2685
2686         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2687           gfx.anim_random_frame = gpc->gfx_random;
2688
2689         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2690
2691         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2692           gfx.anim_random_frame = last_anim_random_frame;
2693       }
2694     }
2695   }
2696 }
2697
2698 static void DisplayGameControlValues(void)
2699 {
2700   boolean redraw_panel = FALSE;
2701   int i;
2702
2703   for (i = 0; game_panel_controls[i].nr != -1; i++)
2704   {
2705     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2706
2707     if (PANEL_DEACTIVATED(gpc->pos))
2708       continue;
2709
2710     if (gpc->value == gpc->last_value &&
2711         gpc->frame == gpc->last_frame)
2712       continue;
2713
2714     redraw_panel = TRUE;
2715   }
2716
2717   if (!redraw_panel)
2718     return;
2719
2720   // copy default game door content to main double buffer
2721
2722   // !!! CHECK AGAIN !!!
2723   SetPanelBackground();
2724   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2725   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2726
2727   // redraw game control buttons
2728   RedrawGameButtons();
2729
2730   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2731
2732   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2733   {
2734     int nr = game_panel_order[i].nr;
2735     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2736     struct TextPosInfo *pos = gpc->pos;
2737     int type = gpc->type;
2738     int value = gpc->value;
2739     int frame = gpc->frame;
2740     int size = pos->size;
2741     int font = pos->font;
2742     boolean draw_masked = pos->draw_masked;
2743     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2744
2745     if (PANEL_DEACTIVATED(pos))
2746       continue;
2747
2748     if (pos->class == get_hash_from_string("extra_panel_items") &&
2749         !setup.prefer_extra_panel_items)
2750       continue;
2751
2752     gpc->last_value = value;
2753     gpc->last_frame = frame;
2754
2755     if (type == TYPE_INTEGER)
2756     {
2757       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2758           nr == GAME_PANEL_INVENTORY_COUNT ||
2759           nr == GAME_PANEL_SCORE ||
2760           nr == GAME_PANEL_HIGHSCORE ||
2761           nr == GAME_PANEL_TIME)
2762       {
2763         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2764
2765         if (use_dynamic_size)           // use dynamic number of digits
2766         {
2767           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2768                               nr == GAME_PANEL_INVENTORY_COUNT ||
2769                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2770           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2771                           nr == GAME_PANEL_INVENTORY_COUNT ||
2772                           nr == GAME_PANEL_TIME ? 1 : 2);
2773           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2774                        nr == GAME_PANEL_INVENTORY_COUNT ||
2775                        nr == GAME_PANEL_TIME ? 3 : 5);
2776           int size2 = size1 + size_add;
2777           int font1 = pos->font;
2778           int font2 = pos->font_alt;
2779
2780           size = (value < value_change ? size1 : size2);
2781           font = (value < value_change ? font1 : font2);
2782         }
2783       }
2784
2785       // correct text size if "digits" is zero or less
2786       if (size <= 0)
2787         size = strlen(int2str(value, size));
2788
2789       // dynamically correct text alignment
2790       pos->width = size * getFontWidth(font);
2791
2792       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2793                   int2str(value, size), font, mask_mode);
2794     }
2795     else if (type == TYPE_ELEMENT)
2796     {
2797       int element, graphic;
2798       Bitmap *src_bitmap;
2799       int src_x, src_y;
2800       int width, height;
2801       int dst_x = PANEL_XPOS(pos);
2802       int dst_y = PANEL_YPOS(pos);
2803
2804       if (value != EL_UNDEFINED && value != EL_EMPTY)
2805       {
2806         element = value;
2807         graphic = el2panelimg(value);
2808
2809 #if 0
2810         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2811               element, EL_NAME(element), size);
2812 #endif
2813
2814         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2815           size = TILESIZE;
2816
2817         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2818                               &src_x, &src_y);
2819
2820         width  = graphic_info[graphic].width  * size / TILESIZE;
2821         height = graphic_info[graphic].height * size / TILESIZE;
2822
2823         if (draw_masked)
2824           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2825                            dst_x, dst_y);
2826         else
2827           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2828                      dst_x, dst_y);
2829       }
2830     }
2831     else if (type == TYPE_GRAPHIC)
2832     {
2833       int graphic        = gpc->graphic;
2834       int graphic_active = gpc->graphic_active;
2835       Bitmap *src_bitmap;
2836       int src_x, src_y;
2837       int width, height;
2838       int dst_x = PANEL_XPOS(pos);
2839       int dst_y = PANEL_YPOS(pos);
2840       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2841                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2842
2843       if (graphic != IMG_UNDEFINED && !skip)
2844       {
2845         if (pos->style == STYLE_REVERSE)
2846           value = 100 - value;
2847
2848         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2849
2850         if (pos->direction & MV_HORIZONTAL)
2851         {
2852           width  = graphic_info[graphic_active].width * value / 100;
2853           height = graphic_info[graphic_active].height;
2854
2855           if (pos->direction == MV_LEFT)
2856           {
2857             src_x += graphic_info[graphic_active].width - width;
2858             dst_x += graphic_info[graphic_active].width - width;
2859           }
2860         }
2861         else
2862         {
2863           width  = graphic_info[graphic_active].width;
2864           height = graphic_info[graphic_active].height * value / 100;
2865
2866           if (pos->direction == MV_UP)
2867           {
2868             src_y += graphic_info[graphic_active].height - height;
2869             dst_y += graphic_info[graphic_active].height - height;
2870           }
2871         }
2872
2873         if (draw_masked)
2874           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2875                            dst_x, dst_y);
2876         else
2877           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2878                      dst_x, dst_y);
2879
2880         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2881
2882         if (pos->direction & MV_HORIZONTAL)
2883         {
2884           if (pos->direction == MV_RIGHT)
2885           {
2886             src_x += width;
2887             dst_x += width;
2888           }
2889           else
2890           {
2891             dst_x = PANEL_XPOS(pos);
2892           }
2893
2894           width = graphic_info[graphic].width - width;
2895         }
2896         else
2897         {
2898           if (pos->direction == MV_DOWN)
2899           {
2900             src_y += height;
2901             dst_y += height;
2902           }
2903           else
2904           {
2905             dst_y = PANEL_YPOS(pos);
2906           }
2907
2908           height = graphic_info[graphic].height - height;
2909         }
2910
2911         if (draw_masked)
2912           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2913                            dst_x, dst_y);
2914         else
2915           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2916                      dst_x, dst_y);
2917       }
2918     }
2919     else if (type == TYPE_STRING)
2920     {
2921       boolean active = (value != 0);
2922       char *state_normal = "off";
2923       char *state_active = "on";
2924       char *state = (active ? state_active : state_normal);
2925       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2926                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2927                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2928                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2929
2930       if (nr == GAME_PANEL_GRAVITY_STATE)
2931       {
2932         int font1 = pos->font;          // (used for normal state)
2933         int font2 = pos->font_alt;      // (used for active state)
2934
2935         font = (active ? font2 : font1);
2936       }
2937
2938       if (s != NULL)
2939       {
2940         char *s_cut;
2941
2942         if (size <= 0)
2943         {
2944           // don't truncate output if "chars" is zero or less
2945           size = strlen(s);
2946
2947           // dynamically correct text alignment
2948           pos->width = size * getFontWidth(font);
2949         }
2950
2951         s_cut = getStringCopyN(s, size);
2952
2953         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2954                     s_cut, font, mask_mode);
2955
2956         free(s_cut);
2957       }
2958     }
2959
2960     redraw_mask |= REDRAW_DOOR_1;
2961   }
2962
2963   SetGameStatus(GAME_MODE_PLAYING);
2964 }
2965
2966 void UpdateAndDisplayGameControlValues(void)
2967 {
2968   if (tape.deactivate_display)
2969     return;
2970
2971   UpdateGameControlValues();
2972   DisplayGameControlValues();
2973 }
2974
2975 void UpdateGameDoorValues(void)
2976 {
2977   UpdateGameControlValues();
2978 }
2979
2980 void DrawGameDoorValues(void)
2981 {
2982   DisplayGameControlValues();
2983 }
2984
2985
2986 // ============================================================================
2987 // InitGameEngine()
2988 // ----------------------------------------------------------------------------
2989 // initialize game engine due to level / tape version number
2990 // ============================================================================
2991
2992 static void InitGameEngine(void)
2993 {
2994   int i, j, k, l, x, y;
2995
2996   // set game engine from tape file when re-playing, else from level file
2997   game.engine_version = (tape.playing ? tape.engine_version :
2998                          level.game_version);
2999
3000   // set single or multi-player game mode (needed for re-playing tapes)
3001   game.team_mode = setup.team_mode;
3002
3003   if (tape.playing)
3004   {
3005     int num_players = 0;
3006
3007     for (i = 0; i < MAX_PLAYERS; i++)
3008       if (tape.player_participates[i])
3009         num_players++;
3010
3011     // multi-player tapes contain input data for more than one player
3012     game.team_mode = (num_players > 1);
3013   }
3014
3015 #if 0
3016   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3017         level.game_version);
3018   Debug("game:init:level", "          tape.file_version   == %06d",
3019         tape.file_version);
3020   Debug("game:init:level", "          tape.game_version   == %06d",
3021         tape.game_version);
3022   Debug("game:init:level", "          tape.engine_version == %06d",
3023         tape.engine_version);
3024   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3025         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3026 #endif
3027
3028   // --------------------------------------------------------------------------
3029   // set flags for bugs and changes according to active game engine version
3030   // --------------------------------------------------------------------------
3031
3032   /*
3033     Summary of bugfix:
3034     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3035
3036     Bug was introduced in version:
3037     2.0.1
3038
3039     Bug was fixed in version:
3040     4.2.0.0
3041
3042     Description:
3043     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3044     but the property "can fall" was missing, which caused some levels to be
3045     unsolvable. This was fixed in version 4.2.0.0.
3046
3047     Affected levels/tapes:
3048     An example for a tape that was fixed by this bugfix is tape 029 from the
3049     level set "rnd_sam_bateman".
3050     The wrong behaviour will still be used for all levels or tapes that were
3051     created/recorded with it. An example for this is tape 023 from the level
3052     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3053   */
3054
3055   boolean use_amoeba_dropping_cannot_fall_bug =
3056     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3057       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3058      (tape.playing &&
3059       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3060       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3061
3062   /*
3063     Summary of bugfix/change:
3064     Fixed move speed of elements entering or leaving magic wall.
3065
3066     Fixed/changed in version:
3067     2.0.1
3068
3069     Description:
3070     Before 2.0.1, move speed of elements entering or leaving magic wall was
3071     twice as fast as it is now.
3072     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3073
3074     Affected levels/tapes:
3075     The first condition is generally needed for all levels/tapes before version
3076     2.0.1, which might use the old behaviour before it was changed; known tapes
3077     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3078     The second condition is an exception from the above case and is needed for
3079     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3080     above, but before it was known that this change would break tapes like the
3081     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3082     although the engine version while recording maybe was before 2.0.1. There
3083     are a lot of tapes that are affected by this exception, like tape 006 from
3084     the level set "rnd_conor_mancone".
3085   */
3086
3087   boolean use_old_move_stepsize_for_magic_wall =
3088     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3089      !(tape.playing &&
3090        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3091        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3092
3093   /*
3094     Summary of bugfix/change:
3095     Fixed handling for custom elements that change when pushed by the player.
3096
3097     Fixed/changed in version:
3098     3.1.0
3099
3100     Description:
3101     Before 3.1.0, custom elements that "change when pushing" changed directly
3102     after the player started pushing them (until then handled in "DigField()").
3103     Since 3.1.0, these custom elements are not changed until the "pushing"
3104     move of the element is finished (now handled in "ContinueMoving()").
3105
3106     Affected levels/tapes:
3107     The first condition is generally needed for all levels/tapes before version
3108     3.1.0, which might use the old behaviour before it was changed; known tapes
3109     that are affected are some tapes from the level set "Walpurgis Gardens" by
3110     Jamie Cullen.
3111     The second condition is an exception from the above case and is needed for
3112     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3113     above (including some development versions of 3.1.0), but before it was
3114     known that this change would break tapes like the above and was fixed in
3115     3.1.1, so that the changed behaviour was active although the engine version
3116     while recording maybe was before 3.1.0. There is at least one tape that is
3117     affected by this exception, which is the tape for the one-level set "Bug
3118     Machine" by Juergen Bonhagen.
3119   */
3120
3121   game.use_change_when_pushing_bug =
3122     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3123      !(tape.playing &&
3124        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3125        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3126
3127   /*
3128     Summary of bugfix/change:
3129     Fixed handling for blocking the field the player leaves when moving.
3130
3131     Fixed/changed in version:
3132     3.1.1
3133
3134     Description:
3135     Before 3.1.1, when "block last field when moving" was enabled, the field
3136     the player is leaving when moving was blocked for the time of the move,
3137     and was directly unblocked afterwards. This resulted in the last field
3138     being blocked for exactly one less than the number of frames of one player
3139     move. Additionally, even when blocking was disabled, the last field was
3140     blocked for exactly one frame.
3141     Since 3.1.1, due to changes in player movement handling, the last field
3142     is not blocked at all when blocking is disabled. When blocking is enabled,
3143     the last field is blocked for exactly the number of frames of one player
3144     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3145     last field is blocked for exactly one more than the number of frames of
3146     one player move.
3147
3148     Affected levels/tapes:
3149     (!!! yet to be determined -- probably many !!!)
3150   */
3151
3152   game.use_block_last_field_bug =
3153     (game.engine_version < VERSION_IDENT(3,1,1,0));
3154
3155   /* various special flags and settings for native Emerald Mine game engine */
3156
3157   game_em.use_single_button =
3158     (game.engine_version > VERSION_IDENT(4,0,0,2));
3159
3160   game_em.use_push_delay =
3161     (game.engine_version > VERSION_IDENT(4,3,7,1));
3162
3163   game_em.use_snap_key_bug =
3164     (game.engine_version < VERSION_IDENT(4,0,1,0));
3165
3166   game_em.use_random_bug =
3167     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3168
3169   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3170
3171   game_em.use_old_explosions            = use_old_em_engine;
3172   game_em.use_old_android               = use_old_em_engine;
3173   game_em.use_old_push_elements         = use_old_em_engine;
3174   game_em.use_old_push_into_acid        = use_old_em_engine;
3175
3176   game_em.use_wrap_around               = !use_old_em_engine;
3177
3178   // --------------------------------------------------------------------------
3179
3180   // set maximal allowed number of custom element changes per game frame
3181   game.max_num_changes_per_frame = 1;
3182
3183   // default scan direction: scan playfield from top/left to bottom/right
3184   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3185
3186   // dynamically adjust element properties according to game engine version
3187   InitElementPropertiesEngine(game.engine_version);
3188
3189   // ---------- initialize special element properties -------------------------
3190
3191   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3192   if (use_amoeba_dropping_cannot_fall_bug)
3193     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3194
3195   // ---------- initialize player's initial move delay ------------------------
3196
3197   // dynamically adjust player properties according to level information
3198   for (i = 0; i < MAX_PLAYERS; i++)
3199     game.initial_move_delay_value[i] =
3200       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3201
3202   // dynamically adjust player properties according to game engine version
3203   for (i = 0; i < MAX_PLAYERS; i++)
3204     game.initial_move_delay[i] =
3205       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3206        game.initial_move_delay_value[i] : 0);
3207
3208   // ---------- initialize player's initial push delay ------------------------
3209
3210   // dynamically adjust player properties according to game engine version
3211   game.initial_push_delay_value =
3212     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3213
3214   // ---------- initialize changing elements ----------------------------------
3215
3216   // initialize changing elements information
3217   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[i];
3220
3221     // this pointer might have been changed in the level editor
3222     ei->change = &ei->change_page[0];
3223
3224     if (!IS_CUSTOM_ELEMENT(i))
3225     {
3226       ei->change->target_element = EL_EMPTY_SPACE;
3227       ei->change->delay_fixed = 0;
3228       ei->change->delay_random = 0;
3229       ei->change->delay_frames = 1;
3230     }
3231
3232     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3233     {
3234       ei->has_change_event[j] = FALSE;
3235
3236       ei->event_page_nr[j] = 0;
3237       ei->event_page[j] = &ei->change_page[0];
3238     }
3239   }
3240
3241   // add changing elements from pre-defined list
3242   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3243   {
3244     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3245     struct ElementInfo *ei = &element_info[ch_delay->element];
3246
3247     ei->change->target_element       = ch_delay->target_element;
3248     ei->change->delay_fixed          = ch_delay->change_delay;
3249
3250     ei->change->pre_change_function  = ch_delay->pre_change_function;
3251     ei->change->change_function      = ch_delay->change_function;
3252     ei->change->post_change_function = ch_delay->post_change_function;
3253
3254     ei->change->can_change = TRUE;
3255     ei->change->can_change_or_has_action = TRUE;
3256
3257     ei->has_change_event[CE_DELAY] = TRUE;
3258
3259     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3260     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3261   }
3262
3263   // ---------- initialize if element can trigger global animations -----------
3264
3265   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3266   {
3267     struct ElementInfo *ei = &element_info[i];
3268
3269     ei->has_anim_event = FALSE;
3270   }
3271
3272   InitGlobalAnimEventsForCustomElements();
3273
3274   // ---------- initialize internal run-time variables ------------------------
3275
3276   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       ei->change_page[j].can_change_or_has_action =
3283         (ei->change_page[j].can_change |
3284          ei->change_page[j].has_action);
3285     }
3286   }
3287
3288   // add change events from custom element configuration
3289   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3290   {
3291     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3292
3293     for (j = 0; j < ei->num_change_pages; j++)
3294     {
3295       if (!ei->change_page[j].can_change_or_has_action)
3296         continue;
3297
3298       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3299       {
3300         // only add event page for the first page found with this event
3301         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3302         {
3303           ei->has_change_event[k] = TRUE;
3304
3305           ei->event_page_nr[k] = j;
3306           ei->event_page[k] = &ei->change_page[j];
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize reference elements in change conditions ------------
3313
3314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3315   {
3316     int element = EL_CUSTOM_START + i;
3317     struct ElementInfo *ei = &element_info[element];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       int trigger_element = ei->change_page[j].initial_trigger_element;
3322
3323       if (trigger_element >= EL_PREV_CE_8 &&
3324           trigger_element <= EL_NEXT_CE_8)
3325         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3326
3327       ei->change_page[j].trigger_element = trigger_element;
3328     }
3329   }
3330
3331   // ---------- initialize run-time trigger player and element ----------------
3332
3333   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334   {
3335     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336
3337     for (j = 0; j < ei->num_change_pages; j++)
3338     {
3339       struct ElementChangeInfo *change = &ei->change_page[j];
3340
3341       change->actual_trigger_element = EL_EMPTY;
3342       change->actual_trigger_player = EL_EMPTY;
3343       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3344       change->actual_trigger_side = CH_SIDE_NONE;
3345       change->actual_trigger_ce_value = 0;
3346       change->actual_trigger_ce_score = 0;
3347       change->actual_trigger_x = -1;
3348       change->actual_trigger_y = -1;
3349     }
3350   }
3351
3352   // ---------- initialize trigger events -------------------------------------
3353
3354   // initialize trigger events information
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3357       trigger_events[i][j] = FALSE;
3358
3359   // add trigger events from element change event properties
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361   {
3362     struct ElementInfo *ei = &element_info[i];
3363
3364     for (j = 0; j < ei->num_change_pages; j++)
3365     {
3366       struct ElementChangeInfo *change = &ei->change_page[j];
3367
3368       if (!change->can_change_or_has_action)
3369         continue;
3370
3371       if (change->has_event[CE_BY_OTHER_ACTION])
3372       {
3373         int trigger_element = change->trigger_element;
3374
3375         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3376         {
3377           if (change->has_event[k])
3378           {
3379             if (IS_GROUP_ELEMENT(trigger_element))
3380             {
3381               struct ElementGroupInfo *group =
3382                 element_info[trigger_element].group;
3383
3384               for (l = 0; l < group->num_elements_resolved; l++)
3385                 trigger_events[group->element_resolved[l]][k] = TRUE;
3386             }
3387             else if (trigger_element == EL_ANY_ELEMENT)
3388               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3389                 trigger_events[l][k] = TRUE;
3390             else
3391               trigger_events[trigger_element][k] = TRUE;
3392           }
3393         }
3394       }
3395     }
3396   }
3397
3398   // ---------- initialize push delay -----------------------------------------
3399
3400   // initialize push delay values to default
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402   {
3403     if (!IS_CUSTOM_ELEMENT(i))
3404     {
3405       // set default push delay values (corrected since version 3.0.7-1)
3406       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3407       {
3408         element_info[i].push_delay_fixed = 2;
3409         element_info[i].push_delay_random = 8;
3410       }
3411       else
3412       {
3413         element_info[i].push_delay_fixed = 8;
3414         element_info[i].push_delay_random = 8;
3415       }
3416     }
3417   }
3418
3419   // set push delay value for certain elements from pre-defined list
3420   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3421   {
3422     int e = push_delay_list[i].element;
3423
3424     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3425     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3426   }
3427
3428   // set push delay value for Supaplex elements for newer engine versions
3429   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3430   {
3431     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432     {
3433       if (IS_SP_ELEMENT(i))
3434       {
3435         // set SP push delay to just enough to push under a falling zonk
3436         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3437
3438         element_info[i].push_delay_fixed  = delay;
3439         element_info[i].push_delay_random = 0;
3440       }
3441     }
3442   }
3443
3444   // ---------- initialize move stepsize --------------------------------------
3445
3446   // initialize move stepsize values to default
3447   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3448     if (!IS_CUSTOM_ELEMENT(i))
3449       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3450
3451   // set move stepsize value for certain elements from pre-defined list
3452   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3453   {
3454     int e = move_stepsize_list[i].element;
3455
3456     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3457
3458     // set move stepsize value for certain elements for older engine versions
3459     if (use_old_move_stepsize_for_magic_wall)
3460     {
3461       if (e == EL_MAGIC_WALL_FILLING ||
3462           e == EL_MAGIC_WALL_EMPTYING ||
3463           e == EL_BD_MAGIC_WALL_FILLING ||
3464           e == EL_BD_MAGIC_WALL_EMPTYING)
3465         element_info[e].move_stepsize *= 2;
3466     }
3467   }
3468
3469   // ---------- initialize collect score --------------------------------------
3470
3471   // initialize collect score values for custom elements from initial value
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473     if (IS_CUSTOM_ELEMENT(i))
3474       element_info[i].collect_score = element_info[i].collect_score_initial;
3475
3476   // ---------- initialize collect count --------------------------------------
3477
3478   // initialize collect count values for non-custom elements
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (!IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_count_initial = 0;
3482
3483   // add collect count values for all elements from pre-defined list
3484   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485     element_info[collect_count_list[i].element].collect_count_initial =
3486       collect_count_list[i].count;
3487
3488   // ---------- initialize access direction -----------------------------------
3489
3490   // initialize access direction values to default (access from every side)
3491   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492     if (!IS_CUSTOM_ELEMENT(i))
3493       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3494
3495   // set access direction value for certain elements from pre-defined list
3496   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497     element_info[access_direction_list[i].element].access_direction =
3498       access_direction_list[i].direction;
3499
3500   // ---------- initialize explosion content ----------------------------------
3501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502   {
3503     if (IS_CUSTOM_ELEMENT(i))
3504       continue;
3505
3506     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3507     {
3508       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3509
3510       element_info[i].content.e[x][y] =
3511         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513          i == EL_PLAYER_3 ? EL_EMERALD :
3514          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515          i == EL_MOLE ? EL_EMERALD_RED :
3516          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521          i == EL_WALL_EMERALD ? EL_EMERALD :
3522          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527          i == EL_WALL_PEARL ? EL_PEARL :
3528          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3529          EL_EMPTY);
3530     }
3531   }
3532
3533   // ---------- initialize recursion detection --------------------------------
3534   recursion_loop_depth = 0;
3535   recursion_loop_detected = FALSE;
3536   recursion_loop_element = EL_UNDEFINED;
3537
3538   // ---------- initialize graphics engine ------------------------------------
3539   game.scroll_delay_value =
3540     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3542      !setup.forced_scroll_delay           ? 0 :
3543      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3544   if (game.forced_scroll_delay_value == -1)
3545     game.scroll_delay_value =
3546       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3547
3548   // ---------- initialize game engine snapshots ------------------------------
3549   for (i = 0; i < MAX_PLAYERS; i++)
3550     game.snapshot.last_action[i] = 0;
3551   game.snapshot.changed_action = FALSE;
3552   game.snapshot.collected_item = FALSE;
3553   game.snapshot.mode =
3554     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3555      SNAPSHOT_MODE_EVERY_STEP :
3556      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3557      SNAPSHOT_MODE_EVERY_MOVE :
3558      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3559      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3560   game.snapshot.save_snapshot = FALSE;
3561
3562   // ---------- initialize level time for Supaplex engine ---------------------
3563   // Supaplex levels with time limit currently unsupported -- should be added
3564   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3565     level.time = 0;
3566
3567   // ---------- initialize flags for handling game actions --------------------
3568
3569   // set flags for game actions to default values
3570   game.use_key_actions = TRUE;
3571   game.use_mouse_actions = FALSE;
3572
3573   // when using Mirror Magic game engine, handle mouse events only
3574   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3575   {
3576     game.use_key_actions = FALSE;
3577     game.use_mouse_actions = TRUE;
3578   }
3579
3580   // check for custom elements with mouse click events
3581   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3582   {
3583     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3584     {
3585       int element = EL_CUSTOM_START + i;
3586
3587       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3588           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3589           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3590           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3591         game.use_mouse_actions = TRUE;
3592     }
3593   }
3594 }
3595
3596 static int get_num_special_action(int element, int action_first,
3597                                   int action_last)
3598 {
3599   int num_special_action = 0;
3600   int i, j;
3601
3602   for (i = action_first; i <= action_last; i++)
3603   {
3604     boolean found = FALSE;
3605
3606     for (j = 0; j < NUM_DIRECTIONS; j++)
3607       if (el_act_dir2img(element, i, j) !=
3608           el_act_dir2img(element, ACTION_DEFAULT, j))
3609         found = TRUE;
3610
3611     if (found)
3612       num_special_action++;
3613     else
3614       break;
3615   }
3616
3617   return num_special_action;
3618 }
3619
3620
3621 // ============================================================================
3622 // InitGame()
3623 // ----------------------------------------------------------------------------
3624 // initialize and start new game
3625 // ============================================================================
3626
3627 #if DEBUG_INIT_PLAYER
3628 static void DebugPrintPlayerStatus(char *message)
3629 {
3630   int i;
3631
3632   if (!options.debug)
3633     return;
3634
3635   Debug("game:init:player", "%s:", message);
3636
3637   for (i = 0; i < MAX_PLAYERS; i++)
3638   {
3639     struct PlayerInfo *player = &stored_player[i];
3640
3641     Debug("game:init:player",
3642           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3643           i + 1,
3644           player->present,
3645           player->connected,
3646           player->connected_locally,
3647           player->connected_network,
3648           player->active,
3649           (local_player == player ? " (local player)" : ""));
3650   }
3651 }
3652 #endif
3653
3654 void InitGame(void)
3655 {
3656   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3657   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3658   int fade_mask = REDRAW_FIELD;
3659   boolean restarting = (game_status == GAME_MODE_PLAYING);
3660   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3661   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3662   int initial_move_dir = MV_DOWN;
3663   int i, j, x, y;
3664
3665   // required here to update video display before fading (FIX THIS)
3666   DrawMaskedBorder(REDRAW_DOOR_2);
3667
3668   if (!game.restart_level)
3669     CloseDoor(DOOR_CLOSE_1);
3670
3671   if (restarting)
3672   {
3673     // force fading out global animations displayed during game play
3674     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3675   }
3676   else
3677   {
3678     SetGameStatus(GAME_MODE_PLAYING);
3679   }
3680
3681   if (level_editor_test_game)
3682     FadeSkipNextFadeOut();
3683   else
3684     FadeSetEnterScreen();
3685
3686   if (CheckFadeAll())
3687     fade_mask = REDRAW_ALL;
3688
3689   FadeLevelSoundsAndMusic();
3690
3691   ExpireSoundLoops(TRUE);
3692
3693   FadeOut(fade_mask);
3694
3695   if (restarting)
3696   {
3697     // force restarting global animations displayed during game play
3698     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3699
3700     // this is required for "transforming" fade modes like cross-fading
3701     // (else global animations will be stopped, but not restarted here)
3702     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3703
3704     SetGameStatus(GAME_MODE_PLAYING);
3705   }
3706
3707   if (level_editor_test_game)
3708     FadeSkipNextFadeIn();
3709
3710   // needed if different viewport properties defined for playing
3711   ChangeViewportPropertiesIfNeeded();
3712
3713   ClearField();
3714
3715   DrawCompleteVideoDisplay();
3716
3717   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3718
3719   InitGameEngine();
3720   InitGameControlValues();
3721
3722   if (tape.recording)
3723   {
3724     // initialize tape actions from game when recording tape
3725     tape.use_key_actions   = game.use_key_actions;
3726     tape.use_mouse_actions = game.use_mouse_actions;
3727
3728     // initialize visible playfield size when recording tape (for team mode)
3729     tape.scr_fieldx = SCR_FIELDX;
3730     tape.scr_fieldy = SCR_FIELDY;
3731   }
3732
3733   // don't play tapes over network
3734   network_playing = (network.enabled && !tape.playing);
3735
3736   for (i = 0; i < MAX_PLAYERS; i++)
3737   {
3738     struct PlayerInfo *player = &stored_player[i];
3739
3740     player->index_nr = i;
3741     player->index_bit = (1 << i);
3742     player->element_nr = EL_PLAYER_1 + i;
3743
3744     player->present = FALSE;
3745     player->active = FALSE;
3746     player->mapped = FALSE;
3747
3748     player->killed = FALSE;
3749     player->reanimated = FALSE;
3750     player->buried = FALSE;
3751
3752     player->action = 0;
3753     player->effective_action = 0;
3754     player->programmed_action = 0;
3755     player->snap_action = 0;
3756
3757     player->mouse_action.lx = 0;
3758     player->mouse_action.ly = 0;
3759     player->mouse_action.button = 0;
3760     player->mouse_action.button_hint = 0;
3761
3762     player->effective_mouse_action.lx = 0;
3763     player->effective_mouse_action.ly = 0;
3764     player->effective_mouse_action.button = 0;
3765     player->effective_mouse_action.button_hint = 0;
3766
3767     for (j = 0; j < MAX_NUM_KEYS; j++)
3768       player->key[j] = FALSE;
3769
3770     player->num_white_keys = 0;
3771
3772     player->dynabomb_count = 0;
3773     player->dynabomb_size = 1;
3774     player->dynabombs_left = 0;
3775     player->dynabomb_xl = FALSE;
3776
3777     player->MovDir = initial_move_dir;
3778     player->MovPos = 0;
3779     player->GfxPos = 0;
3780     player->GfxDir = initial_move_dir;
3781     player->GfxAction = ACTION_DEFAULT;
3782     player->Frame = 0;
3783     player->StepFrame = 0;
3784
3785     player->initial_element = player->element_nr;
3786     player->artwork_element =
3787       (level.use_artwork_element[i] ? level.artwork_element[i] :
3788        player->element_nr);
3789     player->use_murphy = FALSE;
3790
3791     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3792     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3793
3794     player->gravity = level.initial_player_gravity[i];
3795
3796     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3797
3798     player->actual_frame_counter.count = 0;
3799     player->actual_frame_counter.value = 1;
3800
3801     player->step_counter = 0;
3802
3803     player->last_move_dir = initial_move_dir;
3804
3805     player->is_active = FALSE;
3806
3807     player->is_waiting = FALSE;
3808     player->is_moving = FALSE;
3809     player->is_auto_moving = FALSE;
3810     player->is_digging = FALSE;
3811     player->is_snapping = FALSE;
3812     player->is_collecting = FALSE;
3813     player->is_pushing = FALSE;
3814     player->is_switching = FALSE;
3815     player->is_dropping = FALSE;
3816     player->is_dropping_pressed = FALSE;
3817
3818     player->is_bored = FALSE;
3819     player->is_sleeping = FALSE;
3820
3821     player->was_waiting = TRUE;
3822     player->was_moving = FALSE;
3823     player->was_snapping = FALSE;
3824     player->was_dropping = FALSE;
3825
3826     player->force_dropping = FALSE;
3827
3828     player->frame_counter_bored = -1;
3829     player->frame_counter_sleeping = -1;
3830
3831     player->anim_delay_counter = 0;
3832     player->post_delay_counter = 0;
3833
3834     player->dir_waiting = initial_move_dir;
3835     player->action_waiting = ACTION_DEFAULT;
3836     player->last_action_waiting = ACTION_DEFAULT;
3837     player->special_action_bored = ACTION_DEFAULT;
3838     player->special_action_sleeping = ACTION_DEFAULT;
3839
3840     player->switch_x = -1;
3841     player->switch_y = -1;
3842
3843     player->drop_x = -1;
3844     player->drop_y = -1;
3845
3846     player->show_envelope = 0;
3847
3848     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3849
3850     player->push_delay       = -1;      // initialized when pushing starts
3851     player->push_delay_value = game.initial_push_delay_value;
3852
3853     player->drop_delay = 0;
3854     player->drop_pressed_delay = 0;
3855
3856     player->last_jx = -1;
3857     player->last_jy = -1;
3858     player->jx = -1;
3859     player->jy = -1;
3860
3861     player->shield_normal_time_left = 0;
3862     player->shield_deadly_time_left = 0;
3863
3864     player->last_removed_element = EL_UNDEFINED;
3865
3866     player->inventory_infinite_element = EL_UNDEFINED;
3867     player->inventory_size = 0;
3868
3869     if (level.use_initial_inventory[i])
3870     {
3871       for (j = 0; j < level.initial_inventory_size[i]; j++)
3872       {
3873         int element = level.initial_inventory_content[i][j];
3874         int collect_count = element_info[element].collect_count_initial;
3875         int k;
3876
3877         if (!IS_CUSTOM_ELEMENT(element))
3878           collect_count = 1;
3879
3880         if (collect_count == 0)
3881           player->inventory_infinite_element = element;
3882         else
3883           for (k = 0; k < collect_count; k++)
3884             if (player->inventory_size < MAX_INVENTORY_SIZE)
3885               player->inventory_element[player->inventory_size++] = element;
3886       }
3887     }
3888
3889     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3890     SnapField(player, 0, 0);
3891
3892     map_player_action[i] = i;
3893   }
3894
3895   network_player_action_received = FALSE;
3896
3897   // initial null action
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900
3901   FrameCounter = 0;
3902   TimeFrames = 0;
3903   TimePlayed = 0;
3904   TimeLeft = level.time;
3905
3906   TapeTimeFrames = 0;
3907   TapeTime = 0;
3908
3909   ScreenMovDir = MV_NONE;
3910   ScreenMovPos = 0;
3911   ScreenGfxPos = 0;
3912
3913   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3914
3915   game.robot_wheel_x = -1;
3916   game.robot_wheel_y = -1;
3917
3918   game.exit_x = -1;
3919   game.exit_y = -1;
3920
3921   game.all_players_gone = FALSE;
3922
3923   game.LevelSolved = FALSE;
3924   game.GameOver = FALSE;
3925
3926   game.GamePlayed = !tape.playing;
3927
3928   game.LevelSolved_GameWon = FALSE;
3929   game.LevelSolved_GameEnd = FALSE;
3930   game.LevelSolved_SaveTape = FALSE;
3931   game.LevelSolved_SaveScore = FALSE;
3932
3933   game.LevelSolved_CountingTime = 0;
3934   game.LevelSolved_CountingScore = 0;
3935   game.LevelSolved_CountingHealth = 0;
3936
3937   game.RestartGameRequested = FALSE;
3938
3939   game.panel.active = TRUE;
3940
3941   game.no_level_time_limit = (level.time == 0);
3942   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3943
3944   game.yamyam_content_nr = 0;
3945   game.robot_wheel_active = FALSE;
3946   game.magic_wall_active = FALSE;
3947   game.magic_wall_time_left = 0;
3948   game.light_time_left = 0;
3949   game.timegate_time_left = 0;
3950   game.switchgate_pos = 0;
3951   game.wind_direction = level.wind_direction_initial;
3952
3953   game.time_final = 0;
3954   game.score_time_final = 0;
3955
3956   game.score = 0;
3957   game.score_final = 0;
3958
3959   game.health = MAX_HEALTH;
3960   game.health_final = MAX_HEALTH;
3961
3962   game.gems_still_needed = level.gems_needed;
3963   game.sokoban_fields_still_needed = 0;
3964   game.sokoban_objects_still_needed = 0;
3965   game.lights_still_needed = 0;
3966   game.players_still_needed = 0;
3967   game.friends_still_needed = 0;
3968
3969   game.lenses_time_left = 0;
3970   game.magnify_time_left = 0;
3971
3972   game.ball_active = level.ball_active_initial;
3973   game.ball_content_nr = 0;
3974
3975   game.explosions_delayed = TRUE;
3976
3977   game.envelope_active = FALSE;
3978
3979   // special case: set custom artwork setting to initial value
3980   game.use_masked_elements = game.use_masked_elements_initial;
3981
3982   for (i = 0; i < NUM_BELTS; i++)
3983   {
3984     game.belt_dir[i] = MV_NONE;
3985     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3986   }
3987
3988   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3989     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3990
3991 #if DEBUG_INIT_PLAYER
3992   DebugPrintPlayerStatus("Player status at level initialization");
3993 #endif
3994
3995   SCAN_PLAYFIELD(x, y)
3996   {
3997     Tile[x][y] = Last[x][y] = level.field[x][y];
3998     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3999     ChangeDelay[x][y] = 0;
4000     ChangePage[x][y] = -1;
4001     CustomValue[x][y] = 0;              // initialized in InitField()
4002     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4003     AmoebaNr[x][y] = 0;
4004     WasJustMoving[x][y] = 0;
4005     WasJustFalling[x][y] = 0;
4006     CheckCollision[x][y] = 0;
4007     CheckImpact[x][y] = 0;
4008     Stop[x][y] = FALSE;
4009     Pushed[x][y] = FALSE;
4010
4011     ChangeCount[x][y] = 0;
4012     ChangeEvent[x][y] = -1;
4013
4014     ExplodePhase[x][y] = 0;
4015     ExplodeDelay[x][y] = 0;
4016     ExplodeField[x][y] = EX_TYPE_NONE;
4017
4018     RunnerVisit[x][y] = 0;
4019     PlayerVisit[x][y] = 0;
4020
4021     GfxFrame[x][y] = 0;
4022     GfxRandom[x][y] = INIT_GFX_RANDOM();
4023     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4024     GfxElement[x][y] = EL_UNDEFINED;
4025     GfxElementEmpty[x][y] = EL_EMPTY;
4026     GfxAction[x][y] = ACTION_DEFAULT;
4027     GfxDir[x][y] = MV_NONE;
4028     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4029   }
4030
4031   SCAN_PLAYFIELD(x, y)
4032   {
4033     InitFieldForEngine(x, y);
4034
4035     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4036       emulate_bd = FALSE;
4037     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4038       emulate_sp = FALSE;
4039
4040     InitField(x, y, TRUE);
4041
4042     ResetGfxAnimation(x, y);
4043   }
4044
4045   InitBeltMovement();
4046
4047   // required if level does not contain any "empty space" element
4048   if (element_info[EL_EMPTY].use_gfx_element)
4049     game.use_masked_elements = TRUE;
4050
4051   for (i = 0; i < MAX_PLAYERS; i++)
4052   {
4053     struct PlayerInfo *player = &stored_player[i];
4054
4055     // set number of special actions for bored and sleeping animation
4056     player->num_special_action_bored =
4057       get_num_special_action(player->artwork_element,
4058                              ACTION_BORING_1, ACTION_BORING_LAST);
4059     player->num_special_action_sleeping =
4060       get_num_special_action(player->artwork_element,
4061                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4062   }
4063
4064   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4065                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4066
4067   // initialize type of slippery elements
4068   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4069   {
4070     if (!IS_CUSTOM_ELEMENT(i))
4071     {
4072       // default: elements slip down either to the left or right randomly
4073       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4074
4075       // SP style elements prefer to slip down on the left side
4076       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4077         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4078
4079       // BD style elements prefer to slip down on the left side
4080       if (game.emulation == EMU_BOULDERDASH)
4081         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4082     }
4083   }
4084
4085   // initialize explosion and ignition delay
4086   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4087   {
4088     if (!IS_CUSTOM_ELEMENT(i))
4089     {
4090       int num_phase = 8;
4091       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4092                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4093                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4094       int last_phase = (num_phase + 1) * delay;
4095       int half_phase = (num_phase / 2) * delay;
4096
4097       element_info[i].explosion_delay = last_phase - 1;
4098       element_info[i].ignition_delay = half_phase;
4099
4100       if (i == EL_BLACK_ORB)
4101         element_info[i].ignition_delay = 1;
4102     }
4103   }
4104
4105   // correct non-moving belts to start moving left
4106   for (i = 0; i < NUM_BELTS; i++)
4107     if (game.belt_dir[i] == MV_NONE)
4108       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4109
4110 #if USE_NEW_PLAYER_ASSIGNMENTS
4111   // use preferred player also in local single-player mode
4112   if (!network.enabled && !game.team_mode)
4113   {
4114     int new_index_nr = setup.network_player_nr;
4115
4116     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4117     {
4118       for (i = 0; i < MAX_PLAYERS; i++)
4119         stored_player[i].connected_locally = FALSE;
4120
4121       stored_player[new_index_nr].connected_locally = TRUE;
4122     }
4123   }
4124
4125   for (i = 0; i < MAX_PLAYERS; i++)
4126   {
4127     stored_player[i].connected = FALSE;
4128
4129     // in network game mode, the local player might not be the first player
4130     if (stored_player[i].connected_locally)
4131       local_player = &stored_player[i];
4132   }
4133
4134   if (!network.enabled)
4135     local_player->connected = TRUE;
4136
4137   if (tape.playing)
4138   {
4139     for (i = 0; i < MAX_PLAYERS; i++)
4140       stored_player[i].connected = tape.player_participates[i];
4141   }
4142   else if (network.enabled)
4143   {
4144     // add team mode players connected over the network (needed for correct
4145     // assignment of player figures from level to locally playing players)
4146
4147     for (i = 0; i < MAX_PLAYERS; i++)
4148       if (stored_player[i].connected_network)
4149         stored_player[i].connected = TRUE;
4150   }
4151   else if (game.team_mode)
4152   {
4153     // try to guess locally connected team mode players (needed for correct
4154     // assignment of player figures from level to locally playing players)
4155
4156     for (i = 0; i < MAX_PLAYERS; i++)
4157       if (setup.input[i].use_joystick ||
4158           setup.input[i].key.left != KSYM_UNDEFINED)
4159         stored_player[i].connected = TRUE;
4160   }
4161
4162 #if DEBUG_INIT_PLAYER
4163   DebugPrintPlayerStatus("Player status after level initialization");
4164 #endif
4165
4166 #if DEBUG_INIT_PLAYER
4167   Debug("game:init:player", "Reassigning players ...");
4168 #endif
4169
4170   // check if any connected player was not found in playfield
4171   for (i = 0; i < MAX_PLAYERS; i++)
4172   {
4173     struct PlayerInfo *player = &stored_player[i];
4174
4175     if (player->connected && !player->present)
4176     {
4177       struct PlayerInfo *field_player = NULL;
4178
4179 #if DEBUG_INIT_PLAYER
4180       Debug("game:init:player",
4181             "- looking for field player for player %d ...", i + 1);
4182 #endif
4183
4184       // assign first free player found that is present in the playfield
4185
4186       // first try: look for unmapped playfield player that is not connected
4187       for (j = 0; j < MAX_PLAYERS; j++)
4188         if (field_player == NULL &&
4189             stored_player[j].present &&
4190             !stored_player[j].mapped &&
4191             !stored_player[j].connected)
4192           field_player = &stored_player[j];
4193
4194       // second try: look for *any* unmapped playfield player
4195       for (j = 0; j < MAX_PLAYERS; j++)
4196         if (field_player == NULL &&
4197             stored_player[j].present &&
4198             !stored_player[j].mapped)
4199           field_player = &stored_player[j];
4200
4201       if (field_player != NULL)
4202       {
4203         int jx = field_player->jx, jy = field_player->jy;
4204
4205 #if DEBUG_INIT_PLAYER
4206         Debug("game:init:player", "- found player %d",
4207               field_player->index_nr + 1);
4208 #endif
4209
4210         player->present = FALSE;
4211         player->active = FALSE;
4212
4213         field_player->present = TRUE;
4214         field_player->active = TRUE;
4215
4216         /*
4217         player->initial_element = field_player->initial_element;
4218         player->artwork_element = field_player->artwork_element;
4219
4220         player->block_last_field       = field_player->block_last_field;
4221         player->block_delay_adjustment = field_player->block_delay_adjustment;
4222         */
4223
4224         StorePlayer[jx][jy] = field_player->element_nr;
4225
4226         field_player->jx = field_player->last_jx = jx;
4227         field_player->jy = field_player->last_jy = jy;
4228
4229         if (local_player == player)
4230           local_player = field_player;
4231
4232         map_player_action[field_player->index_nr] = i;
4233
4234         field_player->mapped = TRUE;
4235
4236 #if DEBUG_INIT_PLAYER
4237         Debug("game:init:player", "- map_player_action[%d] == %d",
4238               field_player->index_nr + 1, i + 1);
4239 #endif
4240       }
4241     }
4242
4243     if (player->connected && player->present)
4244       player->mapped = TRUE;
4245   }
4246
4247 #if DEBUG_INIT_PLAYER
4248   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4249 #endif
4250
4251 #else
4252
4253   // check if any connected player was not found in playfield
4254   for (i = 0; i < MAX_PLAYERS; i++)
4255   {
4256     struct PlayerInfo *player = &stored_player[i];
4257
4258     if (player->connected && !player->present)
4259     {
4260       for (j = 0; j < MAX_PLAYERS; j++)
4261       {
4262         struct PlayerInfo *field_player = &stored_player[j];
4263         int jx = field_player->jx, jy = field_player->jy;
4264
4265         // assign first free player found that is present in the playfield
4266         if (field_player->present && !field_player->connected)
4267         {
4268           player->present = TRUE;
4269           player->active = TRUE;
4270
4271           field_player->present = FALSE;
4272           field_player->active = FALSE;
4273
4274           player->initial_element = field_player->initial_element;
4275           player->artwork_element = field_player->artwork_element;
4276
4277           player->block_last_field       = field_player->block_last_field;
4278           player->block_delay_adjustment = field_player->block_delay_adjustment;
4279
4280           StorePlayer[jx][jy] = player->element_nr;
4281
4282           player->jx = player->last_jx = jx;
4283           player->jy = player->last_jy = jy;
4284
4285           break;
4286         }
4287       }
4288     }
4289   }
4290 #endif
4291
4292 #if 0
4293   Debug("game:init:player", "local_player->present == %d",
4294         local_player->present);
4295 #endif
4296
4297   // set focus to local player for network games, else to all players
4298   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4299   game.centered_player_nr_next = game.centered_player_nr;
4300   game.set_centered_player = FALSE;
4301   game.set_centered_player_wrap = FALSE;
4302
4303   if (network_playing && tape.recording)
4304   {
4305     // store client dependent player focus when recording network games
4306     tape.centered_player_nr_next = game.centered_player_nr_next;
4307     tape.set_centered_player = TRUE;
4308   }
4309
4310   if (tape.playing)
4311   {
4312     // when playing a tape, eliminate all players who do not participate
4313
4314 #if USE_NEW_PLAYER_ASSIGNMENTS
4315
4316     if (!game.team_mode)
4317     {
4318       for (i = 0; i < MAX_PLAYERS; i++)
4319       {
4320         if (stored_player[i].active &&
4321             !tape.player_participates[map_player_action[i]])
4322         {
4323           struct PlayerInfo *player = &stored_player[i];
4324           int jx = player->jx, jy = player->jy;
4325
4326 #if DEBUG_INIT_PLAYER
4327           Debug("game:init:player", "Removing player %d at (%d, %d)",
4328                 i + 1, jx, jy);
4329 #endif
4330
4331           player->active = FALSE;
4332           StorePlayer[jx][jy] = 0;
4333           Tile[jx][jy] = EL_EMPTY;
4334         }
4335       }
4336     }
4337
4338 #else
4339
4340     for (i = 0; i < MAX_PLAYERS; i++)
4341     {
4342       if (stored_player[i].active &&
4343           !tape.player_participates[i])
4344       {
4345         struct PlayerInfo *player = &stored_player[i];
4346         int jx = player->jx, jy = player->jy;
4347
4348         player->active = FALSE;
4349         StorePlayer[jx][jy] = 0;
4350         Tile[jx][jy] = EL_EMPTY;
4351       }
4352     }
4353 #endif
4354   }
4355   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4356   {
4357     // when in single player mode, eliminate all but the local player
4358
4359     for (i = 0; i < MAX_PLAYERS; i++)
4360     {
4361       struct PlayerInfo *player = &stored_player[i];
4362
4363       if (player->active && player != local_player)
4364       {
4365         int jx = player->jx, jy = player->jy;
4366
4367         player->active = FALSE;
4368         player->present = FALSE;
4369
4370         StorePlayer[jx][jy] = 0;
4371         Tile[jx][jy] = EL_EMPTY;
4372       }
4373     }
4374   }
4375
4376   for (i = 0; i < MAX_PLAYERS; i++)
4377     if (stored_player[i].active)
4378       game.players_still_needed++;
4379
4380   if (level.solved_by_one_player)
4381     game.players_still_needed = 1;
4382
4383   // when recording the game, store which players take part in the game
4384   if (tape.recording)
4385   {
4386 #if USE_NEW_PLAYER_ASSIGNMENTS
4387     for (i = 0; i < MAX_PLAYERS; i++)
4388       if (stored_player[i].connected)
4389         tape.player_participates[i] = TRUE;
4390 #else
4391     for (i = 0; i < MAX_PLAYERS; i++)
4392       if (stored_player[i].active)
4393         tape.player_participates[i] = TRUE;
4394 #endif
4395   }
4396
4397 #if DEBUG_INIT_PLAYER
4398   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4399 #endif
4400
4401   if (BorderElement == EL_EMPTY)
4402   {
4403     SBX_Left = 0;
4404     SBX_Right = lev_fieldx - SCR_FIELDX;
4405     SBY_Upper = 0;
4406     SBY_Lower = lev_fieldy - SCR_FIELDY;
4407   }
4408   else
4409   {
4410     SBX_Left = -1;
4411     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4412     SBY_Upper = -1;
4413     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4414   }
4415
4416   if (full_lev_fieldx <= SCR_FIELDX)
4417     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4418   if (full_lev_fieldy <= SCR_FIELDY)
4419     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4420
4421   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4422     SBX_Left--;
4423   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4424     SBY_Upper--;
4425
4426   // if local player not found, look for custom element that might create
4427   // the player (make some assumptions about the right custom element)
4428   if (!local_player->present)
4429   {
4430     int start_x = 0, start_y = 0;
4431     int found_rating = 0;
4432     int found_element = EL_UNDEFINED;
4433     int player_nr = local_player->index_nr;
4434
4435     SCAN_PLAYFIELD(x, y)
4436     {
4437       int element = Tile[x][y];
4438       int content;
4439       int xx, yy;
4440       boolean is_player;
4441
4442       if (level.use_start_element[player_nr] &&
4443           level.start_element[player_nr] == element &&
4444           found_rating < 4)
4445       {
4446         start_x = x;
4447         start_y = y;
4448
4449         found_rating = 4;
4450         found_element = element;
4451       }
4452
4453       if (!IS_CUSTOM_ELEMENT(element))
4454         continue;
4455
4456       if (CAN_CHANGE(element))
4457       {
4458         for (i = 0; i < element_info[element].num_change_pages; i++)
4459         {
4460           // check for player created from custom element as single target
4461           content = element_info[element].change_page[i].target_element;
4462           is_player = IS_PLAYER_ELEMENT(content);
4463
4464           if (is_player && (found_rating < 3 ||
4465                             (found_rating == 3 && element < found_element)))
4466           {
4467             start_x = x;
4468             start_y = y;
4469
4470             found_rating = 3;
4471             found_element = element;
4472           }
4473         }
4474       }
4475
4476       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4477       {
4478         // check for player created from custom element as explosion content
4479         content = element_info[element].content.e[xx][yy];
4480         is_player = IS_PLAYER_ELEMENT(content);
4481
4482         if (is_player && (found_rating < 2 ||
4483                           (found_rating == 2 && element < found_element)))
4484         {
4485           start_x = x + xx - 1;
4486           start_y = y + yy - 1;
4487
4488           found_rating = 2;
4489           found_element = element;
4490         }
4491
4492         if (!CAN_CHANGE(element))
4493           continue;
4494
4495         for (i = 0; i < element_info[element].num_change_pages; i++)
4496         {
4497           // check for player created from custom element as extended target
4498           content =
4499             element_info[element].change_page[i].target_content.e[xx][yy];
4500
4501           is_player = IS_PLAYER_ELEMENT(content);
4502
4503           if (is_player && (found_rating < 1 ||
4504                             (found_rating == 1 && element < found_element)))
4505           {
4506             start_x = x + xx - 1;
4507             start_y = y + yy - 1;
4508
4509             found_rating = 1;
4510             found_element = element;
4511           }
4512         }
4513       }
4514     }
4515
4516     scroll_x = SCROLL_POSITION_X(start_x);
4517     scroll_y = SCROLL_POSITION_Y(start_y);
4518   }
4519   else
4520   {
4521     scroll_x = SCROLL_POSITION_X(local_player->jx);
4522     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4523   }
4524
4525   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4526     scroll_x = game.forced_scroll_x;
4527   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4528     scroll_y = game.forced_scroll_y;
4529
4530   // !!! FIX THIS (START) !!!
4531   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4532   {
4533     InitGameEngine_BD();
4534   }
4535   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4536   {
4537     InitGameEngine_EM();
4538   }
4539   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4540   {
4541     InitGameEngine_SP();
4542   }
4543   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4544   {
4545     InitGameEngine_MM();
4546   }
4547   else
4548   {
4549     DrawLevel(REDRAW_FIELD);
4550     DrawAllPlayers();
4551
4552     // after drawing the level, correct some elements
4553     if (game.timegate_time_left == 0)
4554       CloseAllOpenTimegates();
4555   }
4556
4557   // blit playfield from scroll buffer to normal back buffer for fading in
4558   BlitScreenToBitmap(backbuffer);
4559   // !!! FIX THIS (END) !!!
4560
4561   DrawMaskedBorder(fade_mask);
4562
4563   FadeIn(fade_mask);
4564
4565 #if 1
4566   // full screen redraw is required at this point in the following cases:
4567   // - special editor door undrawn when game was started from level editor
4568   // - drawing area (playfield) was changed and has to be removed completely
4569   redraw_mask = REDRAW_ALL;
4570   BackToFront();
4571 #endif
4572
4573   if (!game.restart_level)
4574   {
4575     // copy default game door content to main double buffer
4576
4577     // !!! CHECK AGAIN !!!
4578     SetPanelBackground();
4579     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4580     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4581   }
4582
4583   SetPanelBackground();
4584   SetDrawBackgroundMask(REDRAW_DOOR_1);
4585
4586   UpdateAndDisplayGameControlValues();
4587
4588   if (!game.restart_level)
4589   {
4590     UnmapGameButtons();
4591     UnmapTapeButtons();
4592
4593     FreeGameButtons();
4594     CreateGameButtons();
4595
4596     MapGameButtons();
4597     MapTapeButtons();
4598
4599     // copy actual game door content to door double buffer for OpenDoor()
4600     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4601
4602     OpenDoor(DOOR_OPEN_ALL);
4603
4604     KeyboardAutoRepeatOffUnlessAutoplay();
4605
4606 #if DEBUG_INIT_PLAYER
4607     DebugPrintPlayerStatus("Player status (final)");
4608 #endif
4609   }
4610
4611   UnmapAllGadgets();
4612
4613   MapGameButtons();
4614   MapTapeButtons();
4615
4616   if (!game.restart_level && !tape.playing)
4617   {
4618     LevelStats_incPlayed(level_nr);
4619
4620     SaveLevelSetup_SeriesInfo();
4621   }
4622
4623   game.restart_level = FALSE;
4624   game.request_active = FALSE;
4625
4626   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4627     InitGameActions_MM();
4628
4629   SaveEngineSnapshotToListInitial();
4630
4631   if (!game.restart_level)
4632   {
4633     PlaySound(SND_GAME_STARTING);
4634
4635     if (setup.sound_music)
4636       PlayLevelMusic();
4637   }
4638
4639   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4640 }
4641
4642 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4643                         int actual_player_x, int actual_player_y)
4644 {
4645   // this is used for non-R'n'D game engines to update certain engine values
4646
4647   // needed to determine if sounds are played within the visible screen area
4648   scroll_x = actual_scroll_x;
4649   scroll_y = actual_scroll_y;
4650
4651   // needed to get player position for "follow finger" playing input method
4652   local_player->jx = actual_player_x;
4653   local_player->jy = actual_player_y;
4654 }
4655
4656 void InitMovDir(int x, int y)
4657 {
4658   int i, element = Tile[x][y];
4659   static int xy[4][2] =
4660   {
4661     {  0, +1 },
4662     { +1,  0 },
4663     {  0, -1 },
4664     { -1,  0 }
4665   };
4666   static int direction[3][4] =
4667   {
4668     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4669     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4670     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4671   };
4672
4673   switch (element)
4674   {
4675     case EL_BUG_RIGHT:
4676     case EL_BUG_UP:
4677     case EL_BUG_LEFT:
4678     case EL_BUG_DOWN:
4679       Tile[x][y] = EL_BUG;
4680       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4681       break;
4682
4683     case EL_SPACESHIP_RIGHT:
4684     case EL_SPACESHIP_UP:
4685     case EL_SPACESHIP_LEFT:
4686     case EL_SPACESHIP_DOWN:
4687       Tile[x][y] = EL_SPACESHIP;
4688       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4689       break;
4690
4691     case EL_BD_BUTTERFLY_RIGHT:
4692     case EL_BD_BUTTERFLY_UP:
4693     case EL_BD_BUTTERFLY_LEFT:
4694     case EL_BD_BUTTERFLY_DOWN:
4695       Tile[x][y] = EL_BD_BUTTERFLY;
4696       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4697       break;
4698
4699     case EL_BD_FIREFLY_RIGHT:
4700     case EL_BD_FIREFLY_UP:
4701     case EL_BD_FIREFLY_LEFT:
4702     case EL_BD_FIREFLY_DOWN:
4703       Tile[x][y] = EL_BD_FIREFLY;
4704       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4705       break;
4706
4707     case EL_PACMAN_RIGHT:
4708     case EL_PACMAN_UP:
4709     case EL_PACMAN_LEFT:
4710     case EL_PACMAN_DOWN:
4711       Tile[x][y] = EL_PACMAN;
4712       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4713       break;
4714
4715     case EL_YAMYAM_LEFT:
4716     case EL_YAMYAM_RIGHT:
4717     case EL_YAMYAM_UP:
4718     case EL_YAMYAM_DOWN:
4719       Tile[x][y] = EL_YAMYAM;
4720       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4721       break;
4722
4723     case EL_SP_SNIKSNAK:
4724       MovDir[x][y] = MV_UP;
4725       break;
4726
4727     case EL_SP_ELECTRON:
4728       MovDir[x][y] = MV_LEFT;
4729       break;
4730
4731     case EL_MOLE_LEFT:
4732     case EL_MOLE_RIGHT:
4733     case EL_MOLE_UP:
4734     case EL_MOLE_DOWN:
4735       Tile[x][y] = EL_MOLE;
4736       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4737       break;
4738
4739     case EL_SPRING_LEFT:
4740     case EL_SPRING_RIGHT:
4741       Tile[x][y] = EL_SPRING;
4742       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4743       break;
4744
4745     default:
4746       if (IS_CUSTOM_ELEMENT(element))
4747       {
4748         struct ElementInfo *ei = &element_info[element];
4749         int move_direction_initial = ei->move_direction_initial;
4750         int move_pattern = ei->move_pattern;
4751
4752         if (move_direction_initial == MV_START_PREVIOUS)
4753         {
4754           if (MovDir[x][y] != MV_NONE)
4755             return;
4756
4757           move_direction_initial = MV_START_AUTOMATIC;
4758         }
4759
4760         if (move_direction_initial == MV_START_RANDOM)
4761           MovDir[x][y] = 1 << RND(4);
4762         else if (move_direction_initial & MV_ANY_DIRECTION)
4763           MovDir[x][y] = move_direction_initial;
4764         else if (move_pattern == MV_ALL_DIRECTIONS ||
4765                  move_pattern == MV_TURNING_LEFT ||
4766                  move_pattern == MV_TURNING_RIGHT ||
4767                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4768                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4769                  move_pattern == MV_TURNING_RANDOM)
4770           MovDir[x][y] = 1 << RND(4);
4771         else if (move_pattern == MV_HORIZONTAL)
4772           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4773         else if (move_pattern == MV_VERTICAL)
4774           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4775         else if (move_pattern & MV_ANY_DIRECTION)
4776           MovDir[x][y] = element_info[element].move_pattern;
4777         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4778                  move_pattern == MV_ALONG_RIGHT_SIDE)
4779         {
4780           // use random direction as default start direction
4781           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4782             MovDir[x][y] = 1 << RND(4);
4783
4784           for (i = 0; i < NUM_DIRECTIONS; i++)
4785           {
4786             int x1 = x + xy[i][0];
4787             int y1 = y + xy[i][1];
4788
4789             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4790             {
4791               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4792                 MovDir[x][y] = direction[0][i];
4793               else
4794                 MovDir[x][y] = direction[1][i];
4795
4796               break;
4797             }
4798           }
4799         }                
4800       }
4801       else
4802       {
4803         MovDir[x][y] = 1 << RND(4);
4804
4805         if (element != EL_BUG &&
4806             element != EL_SPACESHIP &&
4807             element != EL_BD_BUTTERFLY &&
4808             element != EL_BD_FIREFLY)
4809           break;
4810
4811         for (i = 0; i < NUM_DIRECTIONS; i++)
4812         {
4813           int x1 = x + xy[i][0];
4814           int y1 = y + xy[i][1];
4815
4816           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4817           {
4818             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4819             {
4820               MovDir[x][y] = direction[0][i];
4821               break;
4822             }
4823             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4824                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4825             {
4826               MovDir[x][y] = direction[1][i];
4827               break;
4828             }
4829           }
4830         }
4831       }
4832       break;
4833   }
4834
4835   GfxDir[x][y] = MovDir[x][y];
4836 }
4837
4838 void InitAmoebaNr(int x, int y)
4839 {
4840   int i;
4841   int group_nr = AmoebaNeighbourNr(x, y);
4842
4843   if (group_nr == 0)
4844   {
4845     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4846     {
4847       if (AmoebaCnt[i] == 0)
4848       {
4849         group_nr = i;
4850         break;
4851       }
4852     }
4853   }
4854
4855   AmoebaNr[x][y] = group_nr;
4856   AmoebaCnt[group_nr]++;
4857   AmoebaCnt2[group_nr]++;
4858 }
4859
4860 static void LevelSolved_SetFinalGameValues(void)
4861 {
4862   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4863                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4864   game.score_time_final = (level.use_step_counter ? TimePlayed :
4865                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4866
4867   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4868                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4869                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4870                       game.score);
4871
4872   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4873                        MM_HEALTH(game_mm.laser_overload_value) :
4874                        game.health);
4875
4876   game.LevelSolved_CountingTime = game.time_final;
4877   game.LevelSolved_CountingScore = game.score_final;
4878   game.LevelSolved_CountingHealth = game.health_final;
4879 }
4880
4881 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4882 {
4883   game.LevelSolved_CountingTime = time;
4884   game.LevelSolved_CountingScore = score;
4885   game.LevelSolved_CountingHealth = health;
4886
4887   game_panel_controls[GAME_PANEL_TIME].value = time;
4888   game_panel_controls[GAME_PANEL_SCORE].value = score;
4889   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4890
4891   DisplayGameControlValues();
4892 }
4893
4894 static void LevelSolved(void)
4895 {
4896   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4897       game.players_still_needed > 0)
4898     return;
4899
4900   game.LevelSolved = TRUE;
4901   game.GameOver = TRUE;
4902
4903   tape.solved = TRUE;
4904
4905   // needed here to display correct panel values while player walks into exit
4906   LevelSolved_SetFinalGameValues();
4907 }
4908
4909 static boolean AdvanceToNextLevel(void)
4910 {
4911   if (setup.increment_levels &&
4912       level_nr < leveldir_current->last_level &&
4913       !network_playing)
4914   {
4915     level_nr++;         // advance to next level
4916     TapeErase();        // start with empty tape
4917
4918     if (setup.auto_play_next_level)
4919     {
4920       scores.continue_playing = TRUE;
4921       scores.next_level_nr = level_nr;
4922
4923       LoadLevel(level_nr);
4924
4925       SaveLevelSetup_SeriesInfo();
4926     }
4927
4928     return TRUE;
4929   }
4930
4931   return FALSE;
4932 }
4933
4934 void GameWon(void)
4935 {
4936   static int time_count_steps;
4937   static int time, time_final;
4938   static float score, score_final; // needed for time score < 10 for 10 seconds
4939   static int health, health_final;
4940   static int game_over_delay_1 = 0;
4941   static int game_over_delay_2 = 0;
4942   static int game_over_delay_3 = 0;
4943   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4944   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4945
4946   if (!game.LevelSolved_GameWon)
4947   {
4948     int i;
4949
4950     // do not start end game actions before the player stops moving (to exit)
4951     if (local_player->active && local_player->MovPos)
4952       return;
4953
4954     // calculate final game values after player finished walking into exit
4955     LevelSolved_SetFinalGameValues();
4956
4957     game.LevelSolved_GameWon = TRUE;
4958     game.LevelSolved_SaveTape = tape.recording;
4959     game.LevelSolved_SaveScore = !tape.playing;
4960
4961     if (!tape.playing)
4962     {
4963       LevelStats_incSolved(level_nr);
4964
4965       SaveLevelSetup_SeriesInfo();
4966     }
4967
4968     if (tape.auto_play)         // tape might already be stopped here
4969       tape.auto_play_level_solved = TRUE;
4970
4971     TapeStop();
4972
4973     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4974     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4975     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4976
4977     time = time_final = game.time_final;
4978     score = score_final = game.score_final;
4979     health = health_final = game.health_final;
4980
4981     // update game panel values before (delayed) counting of score (if any)
4982     LevelSolved_DisplayFinalGameValues(time, score, health);
4983
4984     // if level has time score defined, calculate new final game values
4985     if (time_score > 0)
4986     {
4987       int time_final_max = 999;
4988       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4989       int time_frames = 0;
4990       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4991       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4992
4993       if (TimeLeft > 0)
4994       {
4995         time_final = 0;
4996         time_frames = time_frames_left;
4997       }
4998       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4999       {
5000         time_final = time_final_max;
5001         time_frames = time_frames_final_max - time_frames_played;
5002       }
5003
5004       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
5005
5006       time_count_steps = MAX(1, ABS(time_final - time) / 100);
5007
5008       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
5009       {
5010         // keep previous values (final values already processed here)
5011         time_final = time;
5012         score_final = score;
5013       }
5014       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
5015       {
5016         health_final = 0;
5017         score_final += health * time_score;
5018       }
5019
5020       game.score_final = score_final;
5021       game.health_final = health_final;
5022     }
5023
5024     // if not counting score after game, immediately update game panel values
5025     if (level_editor_test_game || !setup.count_score_after_game ||
5026         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5027     {
5028       time = time_final;
5029       score = score_final;
5030
5031       LevelSolved_DisplayFinalGameValues(time, score, health);
5032     }
5033
5034     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5035     {
5036       // check if last player has left the level
5037       if (game.exit_x >= 0 &&
5038           game.exit_y >= 0)
5039       {
5040         int x = game.exit_x;
5041         int y = game.exit_y;
5042         int element = Tile[x][y];
5043
5044         // close exit door after last player
5045         if ((game.all_players_gone &&
5046              (element == EL_EXIT_OPEN ||
5047               element == EL_SP_EXIT_OPEN ||
5048               element == EL_STEEL_EXIT_OPEN)) ||
5049             element == EL_EM_EXIT_OPEN ||
5050             element == EL_EM_STEEL_EXIT_OPEN)
5051         {
5052
5053           Tile[x][y] =
5054             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5055              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5056              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5057              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5058              EL_EM_STEEL_EXIT_CLOSING);
5059
5060           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5061         }
5062
5063         // player disappears
5064         DrawLevelField(x, y);
5065       }
5066
5067       for (i = 0; i < MAX_PLAYERS; i++)
5068       {
5069         struct PlayerInfo *player = &stored_player[i];
5070
5071         if (player->present)
5072         {
5073           RemovePlayer(player);
5074
5075           // player disappears
5076           DrawLevelField(player->jx, player->jy);
5077         }
5078       }
5079     }
5080
5081     PlaySound(SND_GAME_WINNING);
5082   }
5083
5084   if (setup.count_score_after_game)
5085   {
5086     if (time != time_final)
5087     {
5088       if (game_over_delay_1 > 0)
5089       {
5090         game_over_delay_1--;
5091
5092         return;
5093       }
5094
5095       int time_to_go = ABS(time_final - time);
5096       int time_count_dir = (time < time_final ? +1 : -1);
5097
5098       if (time_to_go < time_count_steps)
5099         time_count_steps = 1;
5100
5101       time  += time_count_steps * time_count_dir;
5102       score += time_count_steps * time_score;
5103
5104       // set final score to correct rounding differences after counting score
5105       if (time == time_final)
5106         score = score_final;
5107
5108       LevelSolved_DisplayFinalGameValues(time, score, health);
5109
5110       if (time == time_final)
5111         StopSound(SND_GAME_LEVELTIME_BONUS);
5112       else if (setup.sound_loops)
5113         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5114       else
5115         PlaySound(SND_GAME_LEVELTIME_BONUS);
5116
5117       return;
5118     }
5119
5120     if (health != health_final)
5121     {
5122       if (game_over_delay_2 > 0)
5123       {
5124         game_over_delay_2--;
5125
5126         return;
5127       }
5128
5129       int health_count_dir = (health < health_final ? +1 : -1);
5130
5131       health += health_count_dir;
5132       score  += time_score;
5133
5134       LevelSolved_DisplayFinalGameValues(time, score, health);
5135
5136       if (health == health_final)
5137         StopSound(SND_GAME_LEVELTIME_BONUS);
5138       else if (setup.sound_loops)
5139         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5140       else
5141         PlaySound(SND_GAME_LEVELTIME_BONUS);
5142
5143       return;
5144     }
5145   }
5146
5147   game.panel.active = FALSE;
5148
5149   if (game_over_delay_3 > 0)
5150   {
5151     game_over_delay_3--;
5152
5153     return;
5154   }
5155
5156   GameEnd();
5157 }
5158
5159 void GameEnd(void)
5160 {
5161   // used instead of "level_nr" (needed for network games)
5162   int last_level_nr = levelset.level_nr;
5163   boolean tape_saved = FALSE;
5164
5165   // Important note: This function is not only called after "GameWon()", but also after
5166   // "game over" (if automatically asking for restarting the game is disabled in setup)
5167
5168   if (game.LevelSolved)
5169     game.LevelSolved_GameEnd = TRUE;
5170
5171   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5172   {
5173     // make sure that request dialog to save tape does not open door again
5174     if (!global.use_envelope_request)
5175       CloseDoor(DOOR_CLOSE_1);
5176
5177     // ask to save tape
5178     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5179
5180     // set unique basename for score tape (also saved in high score table)
5181     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5182   }
5183
5184   // if no tape is to be saved, close both doors simultaneously
5185   CloseDoor(DOOR_CLOSE_ALL);
5186
5187   if (level_editor_test_game || score_info_tape_play)
5188   {
5189     SetGameStatus(GAME_MODE_MAIN);
5190
5191     DrawMainMenu();
5192
5193     return;
5194   }
5195
5196   if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission))
5197   {
5198     SetGameStatus(GAME_MODE_MAIN);
5199
5200     DrawMainMenu();
5201
5202     return;
5203   }
5204
5205   if (level_nr == leveldir_current->handicap_level)
5206   {
5207     leveldir_current->handicap_level++;
5208
5209     SaveLevelSetup_SeriesInfo();
5210   }
5211
5212   // save score and score tape before potentially erasing tape below
5213   if (game.LevelSolved_SaveScore)
5214     NewHighScore(last_level_nr, tape_saved);
5215
5216   // increment and load next level (if possible and not configured otherwise)
5217   AdvanceToNextLevel();
5218
5219   if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game)
5220   {
5221     SetGameStatus(GAME_MODE_SCORES);
5222
5223     DrawHallOfFame(last_level_nr);
5224   }
5225   else if (scores.continue_playing)
5226   {
5227     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5228   }
5229   else
5230   {
5231     SetGameStatus(GAME_MODE_MAIN);
5232
5233     DrawMainMenu();
5234   }
5235 }
5236
5237 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5238                          boolean one_score_entry_per_name)
5239 {
5240   int i;
5241
5242   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5243     return -1;
5244
5245   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5246   {
5247     struct ScoreEntry *entry = &list->entry[i];
5248     boolean score_is_better = (new_entry->score >  entry->score);
5249     boolean score_is_equal  = (new_entry->score == entry->score);
5250     boolean time_is_better  = (new_entry->time  <  entry->time);
5251     boolean time_is_equal   = (new_entry->time  == entry->time);
5252     boolean better_by_score = (score_is_better ||
5253                                (score_is_equal && time_is_better));
5254     boolean better_by_time  = (time_is_better ||
5255                                (time_is_equal && score_is_better));
5256     boolean is_better = (level.rate_time_over_score ? better_by_time :
5257                          better_by_score);
5258     boolean entry_is_empty = (entry->score == 0 &&
5259                               entry->time == 0);
5260
5261     // prevent adding server score entries if also existing in local score file
5262     // (special case: historic score entries have an empty tape basename entry)
5263     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5264         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5265     {
5266       // add fields from server score entry not stored in local score entry
5267       // (currently, this means setting platform, version and country fields;
5268       // in rare cases, this may also correct an invalid score value, as
5269       // historic scores might have been truncated to 16-bit values locally)
5270       *entry = *new_entry;
5271
5272       return -1;
5273     }
5274
5275     if (is_better || entry_is_empty)
5276     {
5277       // player has made it to the hall of fame
5278
5279       if (i < MAX_SCORE_ENTRIES - 1)
5280       {
5281         int m = MAX_SCORE_ENTRIES - 1;
5282         int l;
5283
5284         if (one_score_entry_per_name)
5285         {
5286           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5287             if (strEqual(list->entry[l].name, new_entry->name))
5288               m = l;
5289
5290           if (m == i)   // player's new highscore overwrites his old one
5291             goto put_into_list;
5292         }
5293
5294         for (l = m; l > i; l--)
5295           list->entry[l] = list->entry[l - 1];
5296       }
5297
5298       put_into_list:
5299
5300       *entry = *new_entry;
5301
5302       return i;
5303     }
5304     else if (one_score_entry_per_name &&
5305              strEqual(entry->name, new_entry->name))
5306     {
5307       // player already in high score list with better score or time
5308
5309       return -1;
5310     }
5311   }
5312
5313   // special case: new score is beyond the last high score list position
5314   return MAX_SCORE_ENTRIES;
5315 }
5316
5317 void NewHighScore(int level_nr, boolean tape_saved)
5318 {
5319   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5320   boolean one_per_name = FALSE;
5321
5322   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5323   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5324
5325   new_entry.score = game.score_final;
5326   new_entry.time = game.score_time_final;
5327
5328   LoadScore(level_nr);
5329
5330   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
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] = new_entry;
5338
5339     // store last added local score entry (before merging server scores)
5340     scores.last_added_local = scores.last_added;
5341
5342     return;
5343   }
5344
5345   if (scores.last_added < 0)
5346     return;
5347
5348   SaveScore(level_nr);
5349
5350   // store last added local score entry (before merging server scores)
5351   scores.last_added_local = scores.last_added;
5352
5353   if (!game.LevelSolved_SaveTape)
5354     return;
5355
5356   SaveScoreTape(level_nr);
5357
5358   if (setup.ask_for_using_api_server)
5359   {
5360     setup.use_api_server =
5361       Request("Upload your score and tape to the high score server?", REQ_ASK);
5362
5363     if (!setup.use_api_server)
5364       Request("Not using high score server! Use setup menu to enable again!",
5365               REQ_CONFIRM);
5366
5367     runtime.use_api_server = setup.use_api_server;
5368
5369     // after asking for using API server once, do not ask again
5370     setup.ask_for_using_api_server = FALSE;
5371
5372     SaveSetup_ServerSetup();
5373   }
5374
5375   SaveServerScore(level_nr, tape_saved);
5376 }
5377
5378 void MergeServerScore(void)
5379 {
5380   struct ScoreEntry last_added_entry;
5381   boolean one_per_name = FALSE;
5382   int i;
5383
5384   if (scores.last_added >= 0)
5385     last_added_entry = scores.entry[scores.last_added];
5386
5387   for (i = 0; i < server_scores.num_entries; i++)
5388   {
5389     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5390
5391     if (pos >= 0 && pos <= scores.last_added)
5392       scores.last_added++;
5393   }
5394
5395   if (scores.last_added >= MAX_SCORE_ENTRIES)
5396   {
5397     scores.last_added = MAX_SCORE_ENTRIES - 1;
5398     scores.force_last_added = TRUE;
5399
5400     scores.entry[scores.last_added] = last_added_entry;
5401   }
5402 }
5403
5404 static int getElementMoveStepsizeExt(int x, int y, int direction)
5405 {
5406   int element = Tile[x][y];
5407   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5408   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5409   int horiz_move = (dx != 0);
5410   int sign = (horiz_move ? dx : dy);
5411   int step = sign * element_info[element].move_stepsize;
5412
5413   // special values for move stepsize for spring and things on conveyor belt
5414   if (horiz_move)
5415   {
5416     if (CAN_FALL(element) &&
5417         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5418       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5419     else if (element == EL_SPRING)
5420       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5421   }
5422
5423   return step;
5424 }
5425
5426 static int getElementMoveStepsize(int x, int y)
5427 {
5428   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5429 }
5430
5431 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5432 {
5433   if (player->GfxAction != action || player->GfxDir != dir)
5434   {
5435     player->GfxAction = action;
5436     player->GfxDir = dir;
5437     player->Frame = 0;
5438     player->StepFrame = 0;
5439   }
5440 }
5441
5442 static void ResetGfxFrame(int x, int y)
5443 {
5444   // profiling showed that "autotest" spends 10~20% of its time in this function
5445   if (DrawingDeactivatedField())
5446     return;
5447
5448   int element = Tile[x][y];
5449   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5450
5451   if (graphic_info[graphic].anim_global_sync)
5452     GfxFrame[x][y] = FrameCounter;
5453   else if (graphic_info[graphic].anim_global_anim_sync)
5454     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5455   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5456     GfxFrame[x][y] = CustomValue[x][y];
5457   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5458     GfxFrame[x][y] = element_info[element].collect_score;
5459   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5460     GfxFrame[x][y] = ChangeDelay[x][y];
5461 }
5462
5463 static void ResetGfxAnimation(int x, int y)
5464 {
5465   GfxAction[x][y] = ACTION_DEFAULT;
5466   GfxDir[x][y] = MovDir[x][y];
5467   GfxFrame[x][y] = 0;
5468
5469   ResetGfxFrame(x, y);
5470 }
5471
5472 static void ResetRandomAnimationValue(int x, int y)
5473 {
5474   GfxRandom[x][y] = INIT_GFX_RANDOM();
5475 }
5476
5477 static void InitMovingField(int x, int y, int direction)
5478 {
5479   int element = Tile[x][y];
5480   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5481   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5482   int newx = x + dx;
5483   int newy = y + dy;
5484   boolean is_moving_before, is_moving_after;
5485
5486   // check if element was/is moving or being moved before/after mode change
5487   is_moving_before = (WasJustMoving[x][y] != 0);
5488   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5489
5490   // reset animation only for moving elements which change direction of moving
5491   // or which just started or stopped moving
5492   // (else CEs with property "can move" / "not moving" are reset each frame)
5493   if (is_moving_before != is_moving_after ||
5494       direction != MovDir[x][y])
5495     ResetGfxAnimation(x, y);
5496
5497   MovDir[x][y] = direction;
5498   GfxDir[x][y] = direction;
5499
5500   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5501                      direction == MV_DOWN && CAN_FALL(element) ?
5502                      ACTION_FALLING : ACTION_MOVING);
5503
5504   // this is needed for CEs with property "can move" / "not moving"
5505
5506   if (is_moving_after)
5507   {
5508     if (Tile[newx][newy] == EL_EMPTY)
5509       Tile[newx][newy] = EL_BLOCKED;
5510
5511     MovDir[newx][newy] = MovDir[x][y];
5512
5513     CustomValue[newx][newy] = CustomValue[x][y];
5514
5515     GfxFrame[newx][newy] = GfxFrame[x][y];
5516     GfxRandom[newx][newy] = GfxRandom[x][y];
5517     GfxAction[newx][newy] = GfxAction[x][y];
5518     GfxDir[newx][newy] = GfxDir[x][y];
5519   }
5520 }
5521
5522 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5523 {
5524   int direction = MovDir[x][y];
5525   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5526   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5527
5528   *goes_to_x = newx;
5529   *goes_to_y = newy;
5530 }
5531
5532 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5533 {
5534   int direction = MovDir[x][y];
5535   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5536   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5537
5538   *comes_from_x = oldx;
5539   *comes_from_y = oldy;
5540 }
5541
5542 static int MovingOrBlocked2Element(int x, int y)
5543 {
5544   int element = Tile[x][y];
5545
5546   if (element == EL_BLOCKED)
5547   {
5548     int oldx, oldy;
5549
5550     Blocked2Moving(x, y, &oldx, &oldy);
5551
5552     return Tile[oldx][oldy];
5553   }
5554
5555   return element;
5556 }
5557
5558 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5559 {
5560   // like MovingOrBlocked2Element(), but if element is moving
5561   // and (x, y) is the field the moving element is just leaving,
5562   // return EL_BLOCKED instead of the element value
5563   int element = Tile[x][y];
5564
5565   if (IS_MOVING(x, y))
5566   {
5567     if (element == EL_BLOCKED)
5568     {
5569       int oldx, oldy;
5570
5571       Blocked2Moving(x, y, &oldx, &oldy);
5572       return Tile[oldx][oldy];
5573     }
5574     else
5575       return EL_BLOCKED;
5576   }
5577   else
5578     return element;
5579 }
5580
5581 static void RemoveField(int x, int y)
5582 {
5583   Tile[x][y] = EL_EMPTY;
5584
5585   MovPos[x][y] = 0;
5586   MovDir[x][y] = 0;
5587   MovDelay[x][y] = 0;
5588
5589   CustomValue[x][y] = 0;
5590
5591   AmoebaNr[x][y] = 0;
5592   ChangeDelay[x][y] = 0;
5593   ChangePage[x][y] = -1;
5594   Pushed[x][y] = FALSE;
5595
5596   GfxElement[x][y] = EL_UNDEFINED;
5597   GfxAction[x][y] = ACTION_DEFAULT;
5598   GfxDir[x][y] = MV_NONE;
5599 }
5600
5601 static void RemoveMovingField(int x, int y)
5602 {
5603   int oldx = x, oldy = y, newx = x, newy = y;
5604   int element = Tile[x][y];
5605   int next_element = EL_UNDEFINED;
5606
5607   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5608     return;
5609
5610   if (IS_MOVING(x, y))
5611   {
5612     Moving2Blocked(x, y, &newx, &newy);
5613
5614     if (Tile[newx][newy] != EL_BLOCKED)
5615     {
5616       // element is moving, but target field is not free (blocked), but
5617       // already occupied by something different (example: acid pool);
5618       // in this case, only remove the moving field, but not the target
5619
5620       RemoveField(oldx, oldy);
5621
5622       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5623
5624       TEST_DrawLevelField(oldx, oldy);
5625
5626       return;
5627     }
5628   }
5629   else if (element == EL_BLOCKED)
5630   {
5631     Blocked2Moving(x, y, &oldx, &oldy);
5632     if (!IS_MOVING(oldx, oldy))
5633       return;
5634   }
5635
5636   if (element == EL_BLOCKED &&
5637       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5638        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5639        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5640        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5641        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5642        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5643     next_element = get_next_element(Tile[oldx][oldy]);
5644
5645   RemoveField(oldx, oldy);
5646   RemoveField(newx, newy);
5647
5648   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5649
5650   if (next_element != EL_UNDEFINED)
5651     Tile[oldx][oldy] = next_element;
5652
5653   TEST_DrawLevelField(oldx, oldy);
5654   TEST_DrawLevelField(newx, newy);
5655 }
5656
5657 void DrawDynamite(int x, int y)
5658 {
5659   int sx = SCREENX(x), sy = SCREENY(y);
5660   int graphic = el2img(Tile[x][y]);
5661   int frame;
5662
5663   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5664     return;
5665
5666   if (IS_WALKABLE_INSIDE(Back[x][y]))
5667     return;
5668
5669   if (Back[x][y])
5670     DrawLevelElement(x, y, Back[x][y]);
5671   else if (Store[x][y])
5672     DrawLevelElement(x, y, Store[x][y]);
5673   else if (game.use_masked_elements)
5674     DrawLevelElement(x, y, EL_EMPTY);
5675
5676   frame = getGraphicAnimationFrameXY(graphic, x, y);
5677
5678   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5679     DrawGraphicThruMask(sx, sy, graphic, frame);
5680   else
5681     DrawGraphic(sx, sy, graphic, frame);
5682 }
5683
5684 static void CheckDynamite(int x, int y)
5685 {
5686   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5687   {
5688     MovDelay[x][y]--;
5689
5690     if (MovDelay[x][y] != 0)
5691     {
5692       DrawDynamite(x, y);
5693       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5694
5695       return;
5696     }
5697   }
5698
5699   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5700
5701   Bang(x, y);
5702 }
5703
5704 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5705 {
5706   boolean num_checked_players = 0;
5707   int i;
5708
5709   for (i = 0; i < MAX_PLAYERS; i++)
5710   {
5711     if (stored_player[i].active)
5712     {
5713       int sx = stored_player[i].jx;
5714       int sy = stored_player[i].jy;
5715
5716       if (num_checked_players == 0)
5717       {
5718         *sx1 = *sx2 = sx;
5719         *sy1 = *sy2 = sy;
5720       }
5721       else
5722       {
5723         *sx1 = MIN(*sx1, sx);
5724         *sy1 = MIN(*sy1, sy);
5725         *sx2 = MAX(*sx2, sx);
5726         *sy2 = MAX(*sy2, sy);
5727       }
5728
5729       num_checked_players++;
5730     }
5731   }
5732 }
5733
5734 static boolean checkIfAllPlayersFitToScreen_RND(void)
5735 {
5736   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5737
5738   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5739
5740   return (sx2 - sx1 < SCR_FIELDX &&
5741           sy2 - sy1 < SCR_FIELDY);
5742 }
5743
5744 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5745 {
5746   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5747
5748   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5749
5750   *sx = (sx1 + sx2) / 2;
5751   *sy = (sy1 + sy2) / 2;
5752 }
5753
5754 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5755                                boolean center_screen, boolean quick_relocation)
5756 {
5757   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5758   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5759   boolean no_delay = (tape.warp_forward);
5760   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5761   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5762   int new_scroll_x, new_scroll_y;
5763
5764   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5765   {
5766     // case 1: quick relocation inside visible screen (without scrolling)
5767
5768     RedrawPlayfield();
5769
5770     return;
5771   }
5772
5773   if (!level.shifted_relocation || center_screen)
5774   {
5775     // relocation _with_ centering of screen
5776
5777     new_scroll_x = SCROLL_POSITION_X(x);
5778     new_scroll_y = SCROLL_POSITION_Y(y);
5779   }
5780   else
5781   {
5782     // relocation _without_ centering of screen
5783
5784     // apply distance between old and new player position to scroll position
5785     int shifted_scroll_x = scroll_x + (x - old_x);
5786     int shifted_scroll_y = scroll_y + (y - old_y);
5787
5788     // make sure that shifted scroll position does not scroll beyond screen
5789     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5790     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5791
5792     // special case for teleporting from one end of the playfield to the other
5793     // (this kludge prevents the destination area to be shifted by half a tile
5794     // against the source destination for even screen width or screen height;
5795     // probably most useful when used with high "game.forced_scroll_delay_value"
5796     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5797     if (quick_relocation)
5798     {
5799       if (EVEN(SCR_FIELDX))
5800       {
5801         // relocate (teleport) between left and right border (half or full)
5802         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5803           new_scroll_x = SBX_Right;
5804         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5805           new_scroll_x = SBX_Right - 1;
5806         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5807           new_scroll_x = SBX_Left;
5808         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5809           new_scroll_x = SBX_Left + 1;
5810       }
5811
5812       if (EVEN(SCR_FIELDY))
5813       {
5814         // relocate (teleport) between top and bottom border (half or full)
5815         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5816           new_scroll_y = SBY_Lower;
5817         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5818           new_scroll_y = SBY_Lower - 1;
5819         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5820           new_scroll_y = SBY_Upper;
5821         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5822           new_scroll_y = SBY_Upper + 1;
5823       }
5824     }
5825   }
5826
5827   if (quick_relocation)
5828   {
5829     // case 2: quick relocation (redraw without visible scrolling)
5830
5831     scroll_x = new_scroll_x;
5832     scroll_y = new_scroll_y;
5833
5834     RedrawPlayfield();
5835
5836     return;
5837   }
5838
5839   // case 3: visible relocation (with scrolling to new position)
5840
5841   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5842
5843   SetVideoFrameDelay(wait_delay_value);
5844
5845   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5846   {
5847     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5848     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5849
5850     if (dx == 0 && dy == 0)             // no scrolling needed at all
5851       break;
5852
5853     scroll_x -= dx;
5854     scroll_y -= dy;
5855
5856     // set values for horizontal/vertical screen scrolling (half tile size)
5857     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5858     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5859     int pos_x = dx * TILEX / 2;
5860     int pos_y = dy * TILEY / 2;
5861     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5862     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5863
5864     ScrollLevel(dx, dy);
5865     DrawAllPlayers();
5866
5867     // scroll in two steps of half tile size to make things smoother
5868     BlitScreenToBitmapExt_RND(window, fx, fy);
5869
5870     // scroll second step to align at full tile size
5871     BlitScreenToBitmap(window);
5872   }
5873
5874   DrawAllPlayers();
5875   BackToFront();
5876
5877   SetVideoFrameDelay(frame_delay_value_old);
5878 }
5879
5880 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5881 {
5882   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5883   int player_nr = GET_PLAYER_NR(el_player);
5884   struct PlayerInfo *player = &stored_player[player_nr];
5885   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5886   boolean no_delay = (tape.warp_forward);
5887   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5888   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5889   int old_jx = player->jx;
5890   int old_jy = player->jy;
5891   int old_element = Tile[old_jx][old_jy];
5892   int element = Tile[jx][jy];
5893   boolean player_relocated = (old_jx != jx || old_jy != jy);
5894
5895   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5896   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5897   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5898   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5899   int leave_side_horiz = move_dir_horiz;
5900   int leave_side_vert  = move_dir_vert;
5901   int enter_side = enter_side_horiz | enter_side_vert;
5902   int leave_side = leave_side_horiz | leave_side_vert;
5903
5904   if (player->buried)           // do not reanimate dead player
5905     return;
5906
5907   if (!player_relocated)        // no need to relocate the player
5908     return;
5909
5910   if (IS_PLAYER(jx, jy))        // player already placed at new position
5911   {
5912     RemoveField(jx, jy);        // temporarily remove newly placed player
5913     DrawLevelField(jx, jy);
5914   }
5915
5916   if (player->present)
5917   {
5918     while (player->MovPos)
5919     {
5920       ScrollPlayer(player, SCROLL_GO_ON);
5921       ScrollScreen(NULL, SCROLL_GO_ON);
5922
5923       AdvanceFrameAndPlayerCounters(player->index_nr);
5924
5925       DrawPlayer(player);
5926
5927       BackToFront_WithFrameDelay(wait_delay_value);
5928     }
5929
5930     DrawPlayer(player);         // needed here only to cleanup last field
5931     DrawLevelField(player->jx, player->jy);     // remove player graphic
5932
5933     player->is_moving = FALSE;
5934   }
5935
5936   if (IS_CUSTOM_ELEMENT(old_element))
5937     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5938                                CE_LEFT_BY_PLAYER,
5939                                player->index_bit, leave_side);
5940
5941   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5942                                       CE_PLAYER_LEAVES_X,
5943                                       player->index_bit, leave_side);
5944
5945   Tile[jx][jy] = el_player;
5946   InitPlayerField(jx, jy, el_player, TRUE);
5947
5948   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5949      possible that the relocation target field did not contain a player element,
5950      but a walkable element, to which the new player was relocated -- in this
5951      case, restore that (already initialized!) element on the player field */
5952   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5953   {
5954     Tile[jx][jy] = element;     // restore previously existing element
5955   }
5956
5957   // only visually relocate centered player
5958   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5959                      FALSE, level.instant_relocation);
5960
5961   TestIfPlayerTouchesBadThing(jx, jy);
5962   TestIfPlayerTouchesCustomElement(jx, jy);
5963
5964   if (IS_CUSTOM_ELEMENT(element))
5965     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5966                                player->index_bit, enter_side);
5967
5968   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5969                                       player->index_bit, enter_side);
5970
5971   if (player->is_switching)
5972   {
5973     /* ensure that relocation while still switching an element does not cause
5974        a new element to be treated as also switched directly after relocation
5975        (this is important for teleporter switches that teleport the player to
5976        a place where another teleporter switch is in the same direction, which
5977        would then incorrectly be treated as immediately switched before the
5978        direction key that caused the switch was released) */
5979
5980     player->switch_x += jx - old_jx;
5981     player->switch_y += jy - old_jy;
5982   }
5983 }
5984
5985 static void Explode(int ex, int ey, int phase, int mode)
5986 {
5987   int x, y;
5988   int last_phase;
5989   int border_element;
5990
5991   if (game.explosions_delayed)
5992   {
5993     ExplodeField[ex][ey] = mode;
5994     return;
5995   }
5996
5997   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5998   {
5999     int center_element = Tile[ex][ey];
6000     int ce_value = CustomValue[ex][ey];
6001     int ce_score = element_info[center_element].collect_score;
6002     int artwork_element, explosion_element;     // set these values later
6003
6004     // remove things displayed in background while burning dynamite
6005     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
6006       Back[ex][ey] = 0;
6007
6008     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6009     {
6010       // put moving element to center field (and let it explode there)
6011       center_element = MovingOrBlocked2Element(ex, ey);
6012       RemoveMovingField(ex, ey);
6013       Tile[ex][ey] = center_element;
6014     }
6015
6016     // now "center_element" is finally determined -- set related values now
6017     artwork_element = center_element;           // for custom player artwork
6018     explosion_element = center_element;         // for custom player artwork
6019
6020     if (IS_PLAYER(ex, ey))
6021     {
6022       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6023
6024       artwork_element = stored_player[player_nr].artwork_element;
6025
6026       if (level.use_explosion_element[player_nr])
6027       {
6028         explosion_element = level.explosion_element[player_nr];
6029         artwork_element = explosion_element;
6030       }
6031     }
6032
6033     if (mode == EX_TYPE_NORMAL ||
6034         mode == EX_TYPE_CENTER ||
6035         mode == EX_TYPE_CROSS)
6036       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6037
6038     last_phase = element_info[explosion_element].explosion_delay + 1;
6039
6040     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6041     {
6042       int xx = x - ex + 1;
6043       int yy = y - ey + 1;
6044       int element;
6045
6046       if (!IN_LEV_FIELD(x, y) ||
6047           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6048           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6049         continue;
6050
6051       element = Tile[x][y];
6052
6053       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6054       {
6055         element = MovingOrBlocked2Element(x, y);
6056
6057         if (!IS_EXPLOSION_PROOF(element))
6058           RemoveMovingField(x, y);
6059       }
6060
6061       // indestructible elements can only explode in center (but not flames)
6062       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6063                                            mode == EX_TYPE_BORDER)) ||
6064           element == EL_FLAMES)
6065         continue;
6066
6067       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6068          behaviour, for example when touching a yamyam that explodes to rocks
6069          with active deadly shield, a rock is created under the player !!! */
6070       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6071 #if 0
6072       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6073           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6074            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6075 #else
6076       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6077 #endif
6078       {
6079         if (IS_ACTIVE_BOMB(element))
6080         {
6081           // re-activate things under the bomb like gate or penguin
6082           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6083           Back[x][y] = 0;
6084         }
6085
6086         continue;
6087       }
6088
6089       // save walkable background elements while explosion on same tile
6090       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6091           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6092         Back[x][y] = element;
6093
6094       // ignite explodable elements reached by other explosion
6095       if (element == EL_EXPLOSION)
6096         element = Store2[x][y];
6097
6098       if (AmoebaNr[x][y] &&
6099           (element == EL_AMOEBA_FULL ||
6100            element == EL_BD_AMOEBA ||
6101            element == EL_AMOEBA_GROWING))
6102       {
6103         AmoebaCnt[AmoebaNr[x][y]]--;
6104         AmoebaCnt2[AmoebaNr[x][y]]--;
6105       }
6106
6107       RemoveField(x, y);
6108
6109       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6110       {
6111         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6112
6113         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6114
6115         if (PLAYERINFO(ex, ey)->use_murphy)
6116           Store[x][y] = EL_EMPTY;
6117       }
6118
6119       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6120       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6121       else if (IS_PLAYER_ELEMENT(center_element))
6122         Store[x][y] = EL_EMPTY;
6123       else if (center_element == EL_YAMYAM)
6124         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6125       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6126         Store[x][y] = element_info[center_element].content.e[xx][yy];
6127 #if 1
6128       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6129       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6130       // otherwise) -- FIX THIS !!!
6131       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6132         Store[x][y] = element_info[element].content.e[1][1];
6133 #else
6134       else if (!CAN_EXPLODE(element))
6135         Store[x][y] = element_info[element].content.e[1][1];
6136 #endif
6137       else
6138         Store[x][y] = EL_EMPTY;
6139
6140       if (IS_CUSTOM_ELEMENT(center_element))
6141         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6142                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6143                        Store[x][y] >= EL_PREV_CE_8 &&
6144                        Store[x][y] <= EL_NEXT_CE_8 ?
6145                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6146                        Store[x][y]);
6147
6148       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6149           center_element == EL_AMOEBA_TO_DIAMOND)
6150         Store2[x][y] = element;
6151
6152       Tile[x][y] = EL_EXPLOSION;
6153       GfxElement[x][y] = artwork_element;
6154
6155       ExplodePhase[x][y] = 1;
6156       ExplodeDelay[x][y] = last_phase;
6157
6158       Stop[x][y] = TRUE;
6159     }
6160
6161     if (center_element == EL_YAMYAM)
6162       game.yamyam_content_nr =
6163         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6164
6165     return;
6166   }
6167
6168   if (Stop[ex][ey])
6169     return;
6170
6171   x = ex;
6172   y = ey;
6173
6174   if (phase == 1)
6175     GfxFrame[x][y] = 0;         // restart explosion animation
6176
6177   last_phase = ExplodeDelay[x][y];
6178
6179   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6180
6181   // this can happen if the player leaves an explosion just in time
6182   if (GfxElement[x][y] == EL_UNDEFINED)
6183     GfxElement[x][y] = EL_EMPTY;
6184
6185   border_element = Store2[x][y];
6186   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6187     border_element = StorePlayer[x][y];
6188
6189   if (phase == element_info[border_element].ignition_delay ||
6190       phase == last_phase)
6191   {
6192     boolean border_explosion = FALSE;
6193
6194     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6195         !PLAYER_EXPLOSION_PROTECTED(x, y))
6196     {
6197       KillPlayerUnlessExplosionProtected(x, y);
6198       border_explosion = TRUE;
6199     }
6200     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6201     {
6202       Tile[x][y] = Store2[x][y];
6203       Store2[x][y] = 0;
6204       Bang(x, y);
6205       border_explosion = TRUE;
6206     }
6207     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6208     {
6209       AmoebaToDiamond(x, y);
6210       Store2[x][y] = 0;
6211       border_explosion = TRUE;
6212     }
6213
6214     // if an element just explodes due to another explosion (chain-reaction),
6215     // do not immediately end the new explosion when it was the last frame of
6216     // the explosion (as it would be done in the following "if"-statement!)
6217     if (border_explosion && phase == last_phase)
6218       return;
6219   }
6220
6221   // this can happen if the player was just killed by an explosion
6222   if (GfxElement[x][y] == EL_UNDEFINED)
6223     GfxElement[x][y] = EL_EMPTY;
6224
6225   if (phase == last_phase)
6226   {
6227     int element;
6228
6229     element = Tile[x][y] = Store[x][y];
6230     Store[x][y] = Store2[x][y] = 0;
6231     GfxElement[x][y] = EL_UNDEFINED;
6232
6233     // player can escape from explosions and might therefore be still alive
6234     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6235         element <= EL_PLAYER_IS_EXPLODING_4)
6236     {
6237       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6238       int explosion_element = EL_PLAYER_1 + player_nr;
6239       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6240       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6241
6242       if (level.use_explosion_element[player_nr])
6243         explosion_element = level.explosion_element[player_nr];
6244
6245       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6246                     element_info[explosion_element].content.e[xx][yy]);
6247     }
6248
6249     // restore probably existing indestructible background element
6250     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6251       element = Tile[x][y] = Back[x][y];
6252     Back[x][y] = 0;
6253
6254     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6255     GfxDir[x][y] = MV_NONE;
6256     ChangeDelay[x][y] = 0;
6257     ChangePage[x][y] = -1;
6258
6259     CustomValue[x][y] = 0;
6260
6261     InitField_WithBug2(x, y, FALSE);
6262
6263     TEST_DrawLevelField(x, y);
6264
6265     TestIfElementTouchesCustomElement(x, y);
6266
6267     if (GFX_CRUMBLED(element))
6268       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6269
6270     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6271       StorePlayer[x][y] = 0;
6272
6273     if (IS_PLAYER_ELEMENT(element))
6274       RelocatePlayer(x, y, element);
6275   }
6276   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6277   {
6278     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6279     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6280
6281     if (phase == 1)
6282       TEST_DrawLevelFieldCrumbled(x, y);
6283
6284     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6285     {
6286       DrawLevelElement(x, y, Back[x][y]);
6287       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6288     }
6289     else if (IS_WALKABLE_UNDER(Back[x][y]))
6290     {
6291       DrawLevelGraphic(x, y, graphic, frame);
6292       DrawLevelElementThruMask(x, y, Back[x][y]);
6293     }
6294     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6295       DrawLevelGraphic(x, y, graphic, frame);
6296   }
6297 }
6298
6299 static void DynaExplode(int ex, int ey)
6300 {
6301   int i, j;
6302   int dynabomb_element = Tile[ex][ey];
6303   int dynabomb_size = 1;
6304   boolean dynabomb_xl = FALSE;
6305   struct PlayerInfo *player;
6306   struct XY *xy = xy_topdown;
6307
6308   if (IS_ACTIVE_BOMB(dynabomb_element))
6309   {
6310     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6311     dynabomb_size = player->dynabomb_size;
6312     dynabomb_xl = player->dynabomb_xl;
6313     player->dynabombs_left++;
6314   }
6315
6316   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6317
6318   for (i = 0; i < NUM_DIRECTIONS; i++)
6319   {
6320     for (j = 1; j <= dynabomb_size; j++)
6321     {
6322       int x = ex + j * xy[i].x;
6323       int y = ey + j * xy[i].y;
6324       int element;
6325
6326       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6327         break;
6328
6329       element = Tile[x][y];
6330
6331       // do not restart explosions of fields with active bombs
6332       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6333         continue;
6334
6335       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6336
6337       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6338           !IS_DIGGABLE(element) && !dynabomb_xl)
6339         break;
6340     }
6341   }
6342 }
6343
6344 void Bang(int x, int y)
6345 {
6346   int element = MovingOrBlocked2Element(x, y);
6347   int explosion_type = EX_TYPE_NORMAL;
6348
6349   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6350   {
6351     struct PlayerInfo *player = PLAYERINFO(x, y);
6352
6353     element = Tile[x][y] = player->initial_element;
6354
6355     if (level.use_explosion_element[player->index_nr])
6356     {
6357       int explosion_element = level.explosion_element[player->index_nr];
6358
6359       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6360         explosion_type = EX_TYPE_CROSS;
6361       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6362         explosion_type = EX_TYPE_CENTER;
6363     }
6364   }
6365
6366   switch (element)
6367   {
6368     case EL_BUG:
6369     case EL_SPACESHIP:
6370     case EL_BD_BUTTERFLY:
6371     case EL_BD_FIREFLY:
6372     case EL_YAMYAM:
6373     case EL_DARK_YAMYAM:
6374     case EL_ROBOT:
6375     case EL_PACMAN:
6376     case EL_MOLE:
6377       RaiseScoreElement(element);
6378       break;
6379
6380     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6381     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6382     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6383     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6384     case EL_DYNABOMB_INCREASE_NUMBER:
6385     case EL_DYNABOMB_INCREASE_SIZE:
6386     case EL_DYNABOMB_INCREASE_POWER:
6387       explosion_type = EX_TYPE_DYNA;
6388       break;
6389
6390     case EL_DC_LANDMINE:
6391       explosion_type = EX_TYPE_CENTER;
6392       break;
6393
6394     case EL_PENGUIN:
6395     case EL_LAMP:
6396     case EL_LAMP_ACTIVE:
6397     case EL_AMOEBA_TO_DIAMOND:
6398       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6399         explosion_type = EX_TYPE_CENTER;
6400       break;
6401
6402     default:
6403       if (element_info[element].explosion_type == EXPLODES_CROSS)
6404         explosion_type = EX_TYPE_CROSS;
6405       else if (element_info[element].explosion_type == EXPLODES_1X1)
6406         explosion_type = EX_TYPE_CENTER;
6407       break;
6408   }
6409
6410   if (explosion_type == EX_TYPE_DYNA)
6411     DynaExplode(x, y);
6412   else
6413     Explode(x, y, EX_PHASE_START, explosion_type);
6414
6415   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6416 }
6417
6418 static void SplashAcid(int x, int y)
6419 {
6420   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421       (!IN_LEV_FIELD(x - 1, y - 2) ||
6422        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6424
6425   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426       (!IN_LEV_FIELD(x + 1, y - 2) ||
6427        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6429
6430   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6431 }
6432
6433 static void InitBeltMovement(void)
6434 {
6435   static int belt_base_element[4] =
6436   {
6437     EL_CONVEYOR_BELT_1_LEFT,
6438     EL_CONVEYOR_BELT_2_LEFT,
6439     EL_CONVEYOR_BELT_3_LEFT,
6440     EL_CONVEYOR_BELT_4_LEFT
6441   };
6442   static int belt_base_active_element[4] =
6443   {
6444     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6448   };
6449
6450   int x, y, i, j;
6451
6452   // set frame order for belt animation graphic according to belt direction
6453   for (i = 0; i < NUM_BELTS; i++)
6454   {
6455     int belt_nr = i;
6456
6457     for (j = 0; j < NUM_BELT_PARTS; j++)
6458     {
6459       int element = belt_base_active_element[belt_nr] + j;
6460       int graphic_1 = el2img(element);
6461       int graphic_2 = el2panelimg(element);
6462
6463       if (game.belt_dir[i] == MV_LEFT)
6464       {
6465         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6467       }
6468       else
6469       {
6470         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6471         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6472       }
6473     }
6474   }
6475
6476   SCAN_PLAYFIELD(x, y)
6477   {
6478     int element = Tile[x][y];
6479
6480     for (i = 0; i < NUM_BELTS; i++)
6481     {
6482       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6483       {
6484         int e_belt_nr = getBeltNrFromBeltElement(element);
6485         int belt_nr = i;
6486
6487         if (e_belt_nr == belt_nr)
6488         {
6489           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6490
6491           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6492         }
6493       }
6494     }
6495   }
6496 }
6497
6498 static void ToggleBeltSwitch(int x, int y)
6499 {
6500   static int belt_base_element[4] =
6501   {
6502     EL_CONVEYOR_BELT_1_LEFT,
6503     EL_CONVEYOR_BELT_2_LEFT,
6504     EL_CONVEYOR_BELT_3_LEFT,
6505     EL_CONVEYOR_BELT_4_LEFT
6506   };
6507   static int belt_base_active_element[4] =
6508   {
6509     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6513   };
6514   static int belt_base_switch_element[4] =
6515   {
6516     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6520   };
6521   static int belt_move_dir[4] =
6522   {
6523     MV_LEFT,
6524     MV_NONE,
6525     MV_RIGHT,
6526     MV_NONE,
6527   };
6528
6529   int element = Tile[x][y];
6530   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532   int belt_dir = belt_move_dir[belt_dir_nr];
6533   int xx, yy, i;
6534
6535   if (!IS_BELT_SWITCH(element))
6536     return;
6537
6538   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539   game.belt_dir[belt_nr] = belt_dir;
6540
6541   if (belt_dir_nr == 3)
6542     belt_dir_nr = 1;
6543
6544   // set frame order for belt animation graphic according to belt direction
6545   for (i = 0; i < NUM_BELT_PARTS; i++)
6546   {
6547     int element = belt_base_active_element[belt_nr] + i;
6548     int graphic_1 = el2img(element);
6549     int graphic_2 = el2panelimg(element);
6550
6551     if (belt_dir == MV_LEFT)
6552     {
6553       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6555     }
6556     else
6557     {
6558       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6559       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6560     }
6561   }
6562
6563   SCAN_PLAYFIELD(xx, yy)
6564   {
6565     int element = Tile[xx][yy];
6566
6567     if (IS_BELT_SWITCH(element))
6568     {
6569       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6570
6571       if (e_belt_nr == belt_nr)
6572       {
6573         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574         TEST_DrawLevelField(xx, yy);
6575       }
6576     }
6577     else if (IS_BELT(element) && belt_dir != MV_NONE)
6578     {
6579       int e_belt_nr = getBeltNrFromBeltElement(element);
6580
6581       if (e_belt_nr == belt_nr)
6582       {
6583         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6584
6585         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586         TEST_DrawLevelField(xx, yy);
6587       }
6588     }
6589     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6590     {
6591       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6592
6593       if (e_belt_nr == belt_nr)
6594       {
6595         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6596
6597         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598         TEST_DrawLevelField(xx, yy);
6599       }
6600     }
6601   }
6602 }
6603
6604 static void ToggleSwitchgateSwitch(void)
6605 {
6606   int xx, yy;
6607
6608   game.switchgate_pos = !game.switchgate_pos;
6609
6610   SCAN_PLAYFIELD(xx, yy)
6611   {
6612     int element = Tile[xx][yy];
6613
6614     if (element == EL_SWITCHGATE_SWITCH_UP)
6615     {
6616       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6617       TEST_DrawLevelField(xx, yy);
6618     }
6619     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6620     {
6621       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6622       TEST_DrawLevelField(xx, yy);
6623     }
6624     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6625     {
6626       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6627       TEST_DrawLevelField(xx, yy);
6628     }
6629     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6630     {
6631       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6632       TEST_DrawLevelField(xx, yy);
6633     }
6634     else if (element == EL_SWITCHGATE_OPEN ||
6635              element == EL_SWITCHGATE_OPENING)
6636     {
6637       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6638
6639       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6640     }
6641     else if (element == EL_SWITCHGATE_CLOSED ||
6642              element == EL_SWITCHGATE_CLOSING)
6643     {
6644       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6645
6646       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6647     }
6648   }
6649 }
6650
6651 static int getInvisibleActiveFromInvisibleElement(int element)
6652 {
6653   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6654           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6655           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6656           element);
6657 }
6658
6659 static int getInvisibleFromInvisibleActiveElement(int element)
6660 {
6661   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6662           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6663           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6664           element);
6665 }
6666
6667 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6668 {
6669   int x, y;
6670
6671   SCAN_PLAYFIELD(x, y)
6672   {
6673     int element = Tile[x][y];
6674
6675     if (element == EL_LIGHT_SWITCH &&
6676         game.light_time_left > 0)
6677     {
6678       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6679       TEST_DrawLevelField(x, y);
6680     }
6681     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6682              game.light_time_left == 0)
6683     {
6684       Tile[x][y] = EL_LIGHT_SWITCH;
6685       TEST_DrawLevelField(x, y);
6686     }
6687     else if (element == EL_EMC_DRIPPER &&
6688              game.light_time_left > 0)
6689     {
6690       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6691       TEST_DrawLevelField(x, y);
6692     }
6693     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6694              game.light_time_left == 0)
6695     {
6696       Tile[x][y] = EL_EMC_DRIPPER;
6697       TEST_DrawLevelField(x, y);
6698     }
6699     else if (element == EL_INVISIBLE_STEELWALL ||
6700              element == EL_INVISIBLE_WALL ||
6701              element == EL_INVISIBLE_SAND)
6702     {
6703       if (game.light_time_left > 0)
6704         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6705
6706       TEST_DrawLevelField(x, y);
6707
6708       // uncrumble neighbour fields, if needed
6709       if (element == EL_INVISIBLE_SAND)
6710         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6711     }
6712     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6713              element == EL_INVISIBLE_WALL_ACTIVE ||
6714              element == EL_INVISIBLE_SAND_ACTIVE)
6715     {
6716       if (game.light_time_left == 0)
6717         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6718
6719       TEST_DrawLevelField(x, y);
6720
6721       // re-crumble neighbour fields, if needed
6722       if (element == EL_INVISIBLE_SAND)
6723         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6724     }
6725   }
6726 }
6727
6728 static void RedrawAllInvisibleElementsForLenses(void)
6729 {
6730   int x, y;
6731
6732   SCAN_PLAYFIELD(x, y)
6733   {
6734     int element = Tile[x][y];
6735
6736     if (element == EL_EMC_DRIPPER &&
6737         game.lenses_time_left > 0)
6738     {
6739       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6740       TEST_DrawLevelField(x, y);
6741     }
6742     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6743              game.lenses_time_left == 0)
6744     {
6745       Tile[x][y] = EL_EMC_DRIPPER;
6746       TEST_DrawLevelField(x, y);
6747     }
6748     else if (element == EL_INVISIBLE_STEELWALL ||
6749              element == EL_INVISIBLE_WALL ||
6750              element == EL_INVISIBLE_SAND)
6751     {
6752       if (game.lenses_time_left > 0)
6753         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6754
6755       TEST_DrawLevelField(x, y);
6756
6757       // uncrumble neighbour fields, if needed
6758       if (element == EL_INVISIBLE_SAND)
6759         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6760     }
6761     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6762              element == EL_INVISIBLE_WALL_ACTIVE ||
6763              element == EL_INVISIBLE_SAND_ACTIVE)
6764     {
6765       if (game.lenses_time_left == 0)
6766         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6767
6768       TEST_DrawLevelField(x, y);
6769
6770       // re-crumble neighbour fields, if needed
6771       if (element == EL_INVISIBLE_SAND)
6772         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6773     }
6774   }
6775 }
6776
6777 static void RedrawAllInvisibleElementsForMagnifier(void)
6778 {
6779   int x, y;
6780
6781   SCAN_PLAYFIELD(x, y)
6782   {
6783     int element = Tile[x][y];
6784
6785     if (element == EL_EMC_FAKE_GRASS &&
6786         game.magnify_time_left > 0)
6787     {
6788       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6789       TEST_DrawLevelField(x, y);
6790     }
6791     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6792              game.magnify_time_left == 0)
6793     {
6794       Tile[x][y] = EL_EMC_FAKE_GRASS;
6795       TEST_DrawLevelField(x, y);
6796     }
6797     else if (IS_GATE_GRAY(element) &&
6798              game.magnify_time_left > 0)
6799     {
6800       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6801                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6802                     IS_EM_GATE_GRAY(element) ?
6803                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6804                     IS_EMC_GATE_GRAY(element) ?
6805                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6806                     IS_DC_GATE_GRAY(element) ?
6807                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6808                     element);
6809       TEST_DrawLevelField(x, y);
6810     }
6811     else if (IS_GATE_GRAY_ACTIVE(element) &&
6812              game.magnify_time_left == 0)
6813     {
6814       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6815                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6816                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6817                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6818                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6819                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6820                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6821                     EL_DC_GATE_WHITE_GRAY :
6822                     element);
6823       TEST_DrawLevelField(x, y);
6824     }
6825   }
6826 }
6827
6828 static void ToggleLightSwitch(int x, int y)
6829 {
6830   int element = Tile[x][y];
6831
6832   game.light_time_left =
6833     (element == EL_LIGHT_SWITCH ?
6834      level.time_light * FRAMES_PER_SECOND : 0);
6835
6836   RedrawAllLightSwitchesAndInvisibleElements();
6837 }
6838
6839 static void ActivateTimegateSwitch(int x, int y)
6840 {
6841   int xx, yy;
6842
6843   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6844
6845   SCAN_PLAYFIELD(xx, yy)
6846   {
6847     int element = Tile[xx][yy];
6848
6849     if (element == EL_TIMEGATE_CLOSED ||
6850         element == EL_TIMEGATE_CLOSING)
6851     {
6852       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6853       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6854     }
6855
6856     /*
6857     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6858     {
6859       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6860       TEST_DrawLevelField(xx, yy);
6861     }
6862     */
6863
6864   }
6865
6866   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6867                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6868 }
6869
6870 static void Impact(int x, int y)
6871 {
6872   boolean last_line = (y == lev_fieldy - 1);
6873   boolean object_hit = FALSE;
6874   boolean impact = (last_line || object_hit);
6875   int element = Tile[x][y];
6876   int smashed = EL_STEELWALL;
6877
6878   if (!last_line)       // check if element below was hit
6879   {
6880     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6881       return;
6882
6883     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6884                                          MovDir[x][y + 1] != MV_DOWN ||
6885                                          MovPos[x][y + 1] <= TILEY / 2));
6886
6887     // do not smash moving elements that left the smashed field in time
6888     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6889         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6890       object_hit = FALSE;
6891
6892 #if USE_QUICKSAND_IMPACT_BUGFIX
6893     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6894     {
6895       RemoveMovingField(x, y + 1);
6896       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6897       Tile[x][y + 2] = EL_ROCK;
6898       TEST_DrawLevelField(x, y + 2);
6899
6900       object_hit = TRUE;
6901     }
6902
6903     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6904     {
6905       RemoveMovingField(x, y + 1);
6906       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6907       Tile[x][y + 2] = EL_ROCK;
6908       TEST_DrawLevelField(x, y + 2);
6909
6910       object_hit = TRUE;
6911     }
6912 #endif
6913
6914     if (object_hit)
6915       smashed = MovingOrBlocked2Element(x, y + 1);
6916
6917     impact = (last_line || object_hit);
6918   }
6919
6920   if (!last_line && smashed == EL_ACID) // element falls into acid
6921   {
6922     SplashAcid(x, y + 1);
6923     return;
6924   }
6925
6926   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6927   // only reset graphic animation if graphic really changes after impact
6928   if (impact &&
6929       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6930   {
6931     ResetGfxAnimation(x, y);
6932     TEST_DrawLevelField(x, y);
6933   }
6934
6935   if (impact && CAN_EXPLODE_IMPACT(element))
6936   {
6937     Bang(x, y);
6938     return;
6939   }
6940   else if (impact && element == EL_PEARL &&
6941            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6942   {
6943     ResetGfxAnimation(x, y);
6944
6945     Tile[x][y] = EL_PEARL_BREAKING;
6946     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6947     return;
6948   }
6949   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6950   {
6951     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6952
6953     return;
6954   }
6955
6956   if (impact && element == EL_AMOEBA_DROP)
6957   {
6958     if (object_hit && IS_PLAYER(x, y + 1))
6959       KillPlayerUnlessEnemyProtected(x, y + 1);
6960     else if (object_hit && smashed == EL_PENGUIN)
6961       Bang(x, y + 1);
6962     else
6963     {
6964       Tile[x][y] = EL_AMOEBA_GROWING;
6965       Store[x][y] = EL_AMOEBA_WET;
6966
6967       ResetRandomAnimationValue(x, y);
6968     }
6969     return;
6970   }
6971
6972   if (object_hit)               // check which object was hit
6973   {
6974     if ((CAN_PASS_MAGIC_WALL(element) && 
6975          (smashed == EL_MAGIC_WALL ||
6976           smashed == EL_BD_MAGIC_WALL)) ||
6977         (CAN_PASS_DC_MAGIC_WALL(element) &&
6978          smashed == EL_DC_MAGIC_WALL))
6979     {
6980       int xx, yy;
6981       int activated_magic_wall =
6982         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6983          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6984          EL_DC_MAGIC_WALL_ACTIVE);
6985
6986       // activate magic wall / mill
6987       SCAN_PLAYFIELD(xx, yy)
6988       {
6989         if (Tile[xx][yy] == smashed)
6990           Tile[xx][yy] = activated_magic_wall;
6991       }
6992
6993       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6994       game.magic_wall_active = TRUE;
6995
6996       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6997                             SND_MAGIC_WALL_ACTIVATING :
6998                             smashed == EL_BD_MAGIC_WALL ?
6999                             SND_BD_MAGIC_WALL_ACTIVATING :
7000                             SND_DC_MAGIC_WALL_ACTIVATING));
7001     }
7002
7003     if (IS_PLAYER(x, y + 1))
7004     {
7005       if (CAN_SMASH_PLAYER(element))
7006       {
7007         KillPlayerUnlessEnemyProtected(x, y + 1);
7008         return;
7009       }
7010     }
7011     else if (smashed == EL_PENGUIN)
7012     {
7013       if (CAN_SMASH_PLAYER(element))
7014       {
7015         Bang(x, y + 1);
7016         return;
7017       }
7018     }
7019     else if (element == EL_BD_DIAMOND)
7020     {
7021       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7022       {
7023         Bang(x, y + 1);
7024         return;
7025       }
7026     }
7027     else if (((element == EL_SP_INFOTRON ||
7028                element == EL_SP_ZONK) &&
7029               (smashed == EL_SP_SNIKSNAK ||
7030                smashed == EL_SP_ELECTRON ||
7031                smashed == EL_SP_DISK_ORANGE)) ||
7032              (element == EL_SP_INFOTRON &&
7033               smashed == EL_SP_DISK_YELLOW))
7034     {
7035       Bang(x, y + 1);
7036       return;
7037     }
7038     else if (CAN_SMASH_EVERYTHING(element))
7039     {
7040       if (IS_CLASSIC_ENEMY(smashed) ||
7041           CAN_EXPLODE_SMASHED(smashed))
7042       {
7043         Bang(x, y + 1);
7044         return;
7045       }
7046       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7047       {
7048         if (smashed == EL_LAMP ||
7049             smashed == EL_LAMP_ACTIVE)
7050         {
7051           Bang(x, y + 1);
7052           return;
7053         }
7054         else if (smashed == EL_NUT)
7055         {
7056           Tile[x][y + 1] = EL_NUT_BREAKING;
7057           PlayLevelSound(x, y, SND_NUT_BREAKING);
7058           RaiseScoreElement(EL_NUT);
7059           return;
7060         }
7061         else if (smashed == EL_PEARL)
7062         {
7063           ResetGfxAnimation(x, y);
7064
7065           Tile[x][y + 1] = EL_PEARL_BREAKING;
7066           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7067           return;
7068         }
7069         else if (smashed == EL_DIAMOND)
7070         {
7071           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7072           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7073           return;
7074         }
7075         else if (IS_BELT_SWITCH(smashed))
7076         {
7077           ToggleBeltSwitch(x, y + 1);
7078         }
7079         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7080                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7081                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7082                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7083         {
7084           ToggleSwitchgateSwitch();
7085         }
7086         else if (smashed == EL_LIGHT_SWITCH ||
7087                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7088         {
7089           ToggleLightSwitch(x, y + 1);
7090         }
7091         else
7092         {
7093           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7094
7095           CheckElementChangeBySide(x, y + 1, smashed, element,
7096                                    CE_SWITCHED, CH_SIDE_TOP);
7097           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7098                                             CH_SIDE_TOP);
7099         }
7100       }
7101       else
7102       {
7103         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7104       }
7105     }
7106   }
7107
7108   // play sound of magic wall / mill
7109   if (!last_line &&
7110       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7111        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7112        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7113   {
7114     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7115       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7116     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7117       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7118     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7119       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7120
7121     return;
7122   }
7123
7124   // play sound of object that hits the ground
7125   if (last_line || object_hit)
7126     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7127 }
7128
7129 static void TurnRoundExt(int x, int y)
7130 {
7131   static struct
7132   {
7133     int dx, dy;
7134   } move_xy[] =
7135   {
7136     {  0,  0 },
7137     { -1,  0 },
7138     { +1,  0 },
7139     {  0,  0 },
7140     {  0, -1 },
7141     {  0,  0 }, { 0, 0 }, { 0, 0 },
7142     {  0, +1 }
7143   };
7144   static struct
7145   {
7146     int left, right, back;
7147   } turn[] =
7148   {
7149     { 0,        0,              0        },
7150     { MV_DOWN,  MV_UP,          MV_RIGHT },
7151     { MV_UP,    MV_DOWN,        MV_LEFT  },
7152     { 0,        0,              0        },
7153     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7154     { 0,        0,              0        },
7155     { 0,        0,              0        },
7156     { 0,        0,              0        },
7157     { MV_RIGHT, MV_LEFT,        MV_UP    }
7158   };
7159
7160   int element = Tile[x][y];
7161   int move_pattern = element_info[element].move_pattern;
7162
7163   int old_move_dir = MovDir[x][y];
7164   int left_dir  = turn[old_move_dir].left;
7165   int right_dir = turn[old_move_dir].right;
7166   int back_dir  = turn[old_move_dir].back;
7167
7168   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7169   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7170   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7171   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7172
7173   int left_x  = x + left_dx,  left_y  = y + left_dy;
7174   int right_x = x + right_dx, right_y = y + right_dy;
7175   int move_x  = x + move_dx,  move_y  = y + move_dy;
7176
7177   int xx, yy;
7178
7179   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7180   {
7181     TestIfBadThingTouchesOtherBadThing(x, y);
7182
7183     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7184       MovDir[x][y] = right_dir;
7185     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7186       MovDir[x][y] = left_dir;
7187
7188     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7189       MovDelay[x][y] = 9;
7190     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7191       MovDelay[x][y] = 1;
7192   }
7193   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7194   {
7195     TestIfBadThingTouchesOtherBadThing(x, y);
7196
7197     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7198       MovDir[x][y] = left_dir;
7199     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7200       MovDir[x][y] = right_dir;
7201
7202     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7203       MovDelay[x][y] = 9;
7204     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7205       MovDelay[x][y] = 1;
7206   }
7207   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7208   {
7209     TestIfBadThingTouchesOtherBadThing(x, y);
7210
7211     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7212       MovDir[x][y] = left_dir;
7213     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7214       MovDir[x][y] = right_dir;
7215
7216     if (MovDir[x][y] != old_move_dir)
7217       MovDelay[x][y] = 9;
7218   }
7219   else if (element == EL_YAMYAM)
7220   {
7221     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7222     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7223
7224     if (can_turn_left && can_turn_right)
7225       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7226     else if (can_turn_left)
7227       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7228     else if (can_turn_right)
7229       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7230     else
7231       MovDir[x][y] = back_dir;
7232
7233     MovDelay[x][y] = 16 + 16 * RND(3);
7234   }
7235   else if (element == EL_DARK_YAMYAM)
7236   {
7237     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7238                                                          left_x, left_y);
7239     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7240                                                          right_x, right_y);
7241
7242     if (can_turn_left && can_turn_right)
7243       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7244     else if (can_turn_left)
7245       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7246     else if (can_turn_right)
7247       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7248     else
7249       MovDir[x][y] = back_dir;
7250
7251     MovDelay[x][y] = 16 + 16 * RND(3);
7252   }
7253   else if (element == EL_PACMAN)
7254   {
7255     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7256     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7257
7258     if (can_turn_left && can_turn_right)
7259       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7260     else if (can_turn_left)
7261       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7262     else if (can_turn_right)
7263       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7264     else
7265       MovDir[x][y] = back_dir;
7266
7267     MovDelay[x][y] = 6 + RND(40);
7268   }
7269   else if (element == EL_PIG)
7270   {
7271     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7272     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7273     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7274     boolean should_turn_left, should_turn_right, should_move_on;
7275     int rnd_value = 24;
7276     int rnd = RND(rnd_value);
7277
7278     should_turn_left = (can_turn_left &&
7279                         (!can_move_on ||
7280                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7281                                                    y + back_dy + left_dy)));
7282     should_turn_right = (can_turn_right &&
7283                          (!can_move_on ||
7284                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7285                                                     y + back_dy + right_dy)));
7286     should_move_on = (can_move_on &&
7287                       (!can_turn_left ||
7288                        !can_turn_right ||
7289                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7290                                                  y + move_dy + left_dy) ||
7291                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7292                                                  y + move_dy + right_dy)));
7293
7294     if (should_turn_left || should_turn_right || should_move_on)
7295     {
7296       if (should_turn_left && should_turn_right && should_move_on)
7297         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7298                         rnd < 2 * rnd_value / 3 ? right_dir :
7299                         old_move_dir);
7300       else if (should_turn_left && should_turn_right)
7301         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7302       else if (should_turn_left && should_move_on)
7303         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7304       else if (should_turn_right && should_move_on)
7305         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7306       else if (should_turn_left)
7307         MovDir[x][y] = left_dir;
7308       else if (should_turn_right)
7309         MovDir[x][y] = right_dir;
7310       else if (should_move_on)
7311         MovDir[x][y] = old_move_dir;
7312     }
7313     else if (can_move_on && rnd > rnd_value / 8)
7314       MovDir[x][y] = old_move_dir;
7315     else if (can_turn_left && can_turn_right)
7316       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7317     else if (can_turn_left && rnd > rnd_value / 8)
7318       MovDir[x][y] = left_dir;
7319     else if (can_turn_right && rnd > rnd_value/8)
7320       MovDir[x][y] = right_dir;
7321     else
7322       MovDir[x][y] = back_dir;
7323
7324     xx = x + move_xy[MovDir[x][y]].dx;
7325     yy = y + move_xy[MovDir[x][y]].dy;
7326
7327     if (!IN_LEV_FIELD(xx, yy) ||
7328         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7329       MovDir[x][y] = old_move_dir;
7330
7331     MovDelay[x][y] = 0;
7332   }
7333   else if (element == EL_DRAGON)
7334   {
7335     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7336     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7337     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7338     int rnd_value = 24;
7339     int rnd = RND(rnd_value);
7340
7341     if (can_move_on && rnd > rnd_value / 8)
7342       MovDir[x][y] = old_move_dir;
7343     else if (can_turn_left && can_turn_right)
7344       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7345     else if (can_turn_left && rnd > rnd_value / 8)
7346       MovDir[x][y] = left_dir;
7347     else if (can_turn_right && rnd > rnd_value / 8)
7348       MovDir[x][y] = right_dir;
7349     else
7350       MovDir[x][y] = back_dir;
7351
7352     xx = x + move_xy[MovDir[x][y]].dx;
7353     yy = y + move_xy[MovDir[x][y]].dy;
7354
7355     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7356       MovDir[x][y] = old_move_dir;
7357
7358     MovDelay[x][y] = 0;
7359   }
7360   else if (element == EL_MOLE)
7361   {
7362     boolean can_move_on =
7363       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7364                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7365                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7366     if (!can_move_on)
7367     {
7368       boolean can_turn_left =
7369         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7370                               IS_AMOEBOID(Tile[left_x][left_y])));
7371
7372       boolean can_turn_right =
7373         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7374                               IS_AMOEBOID(Tile[right_x][right_y])));
7375
7376       if (can_turn_left && can_turn_right)
7377         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7378       else if (can_turn_left)
7379         MovDir[x][y] = left_dir;
7380       else
7381         MovDir[x][y] = right_dir;
7382     }
7383
7384     if (MovDir[x][y] != old_move_dir)
7385       MovDelay[x][y] = 9;
7386   }
7387   else if (element == EL_BALLOON)
7388   {
7389     MovDir[x][y] = game.wind_direction;
7390     MovDelay[x][y] = 0;
7391   }
7392   else if (element == EL_SPRING)
7393   {
7394     if (MovDir[x][y] & MV_HORIZONTAL)
7395     {
7396       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7397           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7398       {
7399         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7400         ResetGfxAnimation(move_x, move_y);
7401         TEST_DrawLevelField(move_x, move_y);
7402
7403         MovDir[x][y] = back_dir;
7404       }
7405       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7406                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7407         MovDir[x][y] = MV_NONE;
7408     }
7409
7410     MovDelay[x][y] = 0;
7411   }
7412   else if (element == EL_ROBOT ||
7413            element == EL_SATELLITE ||
7414            element == EL_PENGUIN ||
7415            element == EL_EMC_ANDROID)
7416   {
7417     int attr_x = -1, attr_y = -1;
7418
7419     if (game.all_players_gone)
7420     {
7421       attr_x = game.exit_x;
7422       attr_y = game.exit_y;
7423     }
7424     else
7425     {
7426       int i;
7427
7428       for (i = 0; i < MAX_PLAYERS; i++)
7429       {
7430         struct PlayerInfo *player = &stored_player[i];
7431         int jx = player->jx, jy = player->jy;
7432
7433         if (!player->active)
7434           continue;
7435
7436         if (attr_x == -1 ||
7437             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7438         {
7439           attr_x = jx;
7440           attr_y = jy;
7441         }
7442       }
7443     }
7444
7445     if (element == EL_ROBOT &&
7446         game.robot_wheel_x >= 0 &&
7447         game.robot_wheel_y >= 0 &&
7448         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7449          game.engine_version < VERSION_IDENT(3,1,0,0)))
7450     {
7451       attr_x = game.robot_wheel_x;
7452       attr_y = game.robot_wheel_y;
7453     }
7454
7455     if (element == EL_PENGUIN)
7456     {
7457       int i;
7458       struct XY *xy = xy_topdown;
7459
7460       for (i = 0; i < NUM_DIRECTIONS; i++)
7461       {
7462         int ex = x + xy[i].x;
7463         int ey = y + xy[i].y;
7464
7465         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7466                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7467                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7468                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7469         {
7470           attr_x = ex;
7471           attr_y = ey;
7472           break;
7473         }
7474       }
7475     }
7476
7477     MovDir[x][y] = MV_NONE;
7478     if (attr_x < x)
7479       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7480     else if (attr_x > x)
7481       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7482     if (attr_y < y)
7483       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7484     else if (attr_y > y)
7485       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7486
7487     if (element == EL_ROBOT)
7488     {
7489       int newx, newy;
7490
7491       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7492         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7493       Moving2Blocked(x, y, &newx, &newy);
7494
7495       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7496         MovDelay[x][y] = 8 + 8 * !RND(3);
7497       else
7498         MovDelay[x][y] = 16;
7499     }
7500     else if (element == EL_PENGUIN)
7501     {
7502       int newx, newy;
7503
7504       MovDelay[x][y] = 1;
7505
7506       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7507       {
7508         boolean first_horiz = RND(2);
7509         int new_move_dir = MovDir[x][y];
7510
7511         MovDir[x][y] =
7512           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7513         Moving2Blocked(x, y, &newx, &newy);
7514
7515         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7516           return;
7517
7518         MovDir[x][y] =
7519           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7520         Moving2Blocked(x, y, &newx, &newy);
7521
7522         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7523           return;
7524
7525         MovDir[x][y] = old_move_dir;
7526         return;
7527       }
7528     }
7529     else if (element == EL_SATELLITE)
7530     {
7531       int newx, newy;
7532
7533       MovDelay[x][y] = 1;
7534
7535       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7536       {
7537         boolean first_horiz = RND(2);
7538         int new_move_dir = MovDir[x][y];
7539
7540         MovDir[x][y] =
7541           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7542         Moving2Blocked(x, y, &newx, &newy);
7543
7544         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7545           return;
7546
7547         MovDir[x][y] =
7548           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7549         Moving2Blocked(x, y, &newx, &newy);
7550
7551         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7552           return;
7553
7554         MovDir[x][y] = old_move_dir;
7555         return;
7556       }
7557     }
7558     else if (element == EL_EMC_ANDROID)
7559     {
7560       static int check_pos[16] =
7561       {
7562         -1,             //  0 => (invalid)
7563         7,              //  1 => MV_LEFT
7564         3,              //  2 => MV_RIGHT
7565         -1,             //  3 => (invalid)
7566         1,              //  4 =>            MV_UP
7567         0,              //  5 => MV_LEFT  | MV_UP
7568         2,              //  6 => MV_RIGHT | MV_UP
7569         -1,             //  7 => (invalid)
7570         5,              //  8 =>            MV_DOWN
7571         6,              //  9 => MV_LEFT  | MV_DOWN
7572         4,              // 10 => MV_RIGHT | MV_DOWN
7573         -1,             // 11 => (invalid)
7574         -1,             // 12 => (invalid)
7575         -1,             // 13 => (invalid)
7576         -1,             // 14 => (invalid)
7577         -1,             // 15 => (invalid)
7578       };
7579       static struct
7580       {
7581         int dx, dy;
7582         int dir;
7583       } check_xy[8] =
7584       {
7585         { -1, -1,       MV_LEFT  | MV_UP   },
7586         {  0, -1,                  MV_UP   },
7587         { +1, -1,       MV_RIGHT | MV_UP   },
7588         { +1,  0,       MV_RIGHT           },
7589         { +1, +1,       MV_RIGHT | MV_DOWN },
7590         {  0, +1,                  MV_DOWN },
7591         { -1, +1,       MV_LEFT  | MV_DOWN },
7592         { -1,  0,       MV_LEFT            },
7593       };
7594       int start_pos, check_order;
7595       boolean can_clone = FALSE;
7596       int i;
7597
7598       // check if there is any free field around current position
7599       for (i = 0; i < 8; i++)
7600       {
7601         int newx = x + check_xy[i].dx;
7602         int newy = y + check_xy[i].dy;
7603
7604         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7605         {
7606           can_clone = TRUE;
7607
7608           break;
7609         }
7610       }
7611
7612       if (can_clone)            // randomly find an element to clone
7613       {
7614         can_clone = FALSE;
7615
7616         start_pos = check_pos[RND(8)];
7617         check_order = (RND(2) ? -1 : +1);
7618
7619         for (i = 0; i < 8; i++)
7620         {
7621           int pos_raw = start_pos + i * check_order;
7622           int pos = (pos_raw + 8) % 8;
7623           int newx = x + check_xy[pos].dx;
7624           int newy = y + check_xy[pos].dy;
7625
7626           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7627           {
7628             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7629             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7630
7631             Store[x][y] = Tile[newx][newy];
7632
7633             can_clone = TRUE;
7634
7635             break;
7636           }
7637         }
7638       }
7639
7640       if (can_clone)            // randomly find a direction to move
7641       {
7642         can_clone = FALSE;
7643
7644         start_pos = check_pos[RND(8)];
7645         check_order = (RND(2) ? -1 : +1);
7646
7647         for (i = 0; i < 8; i++)
7648         {
7649           int pos_raw = start_pos + i * check_order;
7650           int pos = (pos_raw + 8) % 8;
7651           int newx = x + check_xy[pos].dx;
7652           int newy = y + check_xy[pos].dy;
7653           int new_move_dir = check_xy[pos].dir;
7654
7655           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7656           {
7657             MovDir[x][y] = new_move_dir;
7658             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7659
7660             can_clone = TRUE;
7661
7662             break;
7663           }
7664         }
7665       }
7666
7667       if (can_clone)            // cloning and moving successful
7668         return;
7669
7670       // cannot clone -- try to move towards player
7671
7672       start_pos = check_pos[MovDir[x][y] & 0x0f];
7673       check_order = (RND(2) ? -1 : +1);
7674
7675       for (i = 0; i < 3; i++)
7676       {
7677         // first check start_pos, then previous/next or (next/previous) pos
7678         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7679         int pos = (pos_raw + 8) % 8;
7680         int newx = x + check_xy[pos].dx;
7681         int newy = y + check_xy[pos].dy;
7682         int new_move_dir = check_xy[pos].dir;
7683
7684         if (IS_PLAYER(newx, newy))
7685           break;
7686
7687         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7688         {
7689           MovDir[x][y] = new_move_dir;
7690           MovDelay[x][y] = level.android_move_time * 8 + 1;
7691
7692           break;
7693         }
7694       }
7695     }
7696   }
7697   else if (move_pattern == MV_TURNING_LEFT ||
7698            move_pattern == MV_TURNING_RIGHT ||
7699            move_pattern == MV_TURNING_LEFT_RIGHT ||
7700            move_pattern == MV_TURNING_RIGHT_LEFT ||
7701            move_pattern == MV_TURNING_RANDOM ||
7702            move_pattern == MV_ALL_DIRECTIONS)
7703   {
7704     boolean can_turn_left =
7705       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7706     boolean can_turn_right =
7707       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7708
7709     if (element_info[element].move_stepsize == 0)       // "not moving"
7710       return;
7711
7712     if (move_pattern == MV_TURNING_LEFT)
7713       MovDir[x][y] = left_dir;
7714     else if (move_pattern == MV_TURNING_RIGHT)
7715       MovDir[x][y] = right_dir;
7716     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7717       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7718     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7719       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7720     else if (move_pattern == MV_TURNING_RANDOM)
7721       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7722                       can_turn_right && !can_turn_left ? right_dir :
7723                       RND(2) ? left_dir : right_dir);
7724     else if (can_turn_left && can_turn_right)
7725       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7726     else if (can_turn_left)
7727       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7728     else if (can_turn_right)
7729       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7730     else
7731       MovDir[x][y] = back_dir;
7732
7733     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7734   }
7735   else if (move_pattern == MV_HORIZONTAL ||
7736            move_pattern == MV_VERTICAL)
7737   {
7738     if (move_pattern & old_move_dir)
7739       MovDir[x][y] = back_dir;
7740     else if (move_pattern == MV_HORIZONTAL)
7741       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7742     else if (move_pattern == MV_VERTICAL)
7743       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7744
7745     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7746   }
7747   else if (move_pattern & MV_ANY_DIRECTION)
7748   {
7749     MovDir[x][y] = move_pattern;
7750     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7751   }
7752   else if (move_pattern & MV_WIND_DIRECTION)
7753   {
7754     MovDir[x][y] = game.wind_direction;
7755     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7756   }
7757   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7758   {
7759     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7760       MovDir[x][y] = left_dir;
7761     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7762       MovDir[x][y] = right_dir;
7763
7764     if (MovDir[x][y] != old_move_dir)
7765       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7766   }
7767   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7768   {
7769     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7770       MovDir[x][y] = right_dir;
7771     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7772       MovDir[x][y] = left_dir;
7773
7774     if (MovDir[x][y] != old_move_dir)
7775       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7776   }
7777   else if (move_pattern == MV_TOWARDS_PLAYER ||
7778            move_pattern == MV_AWAY_FROM_PLAYER)
7779   {
7780     int attr_x = -1, attr_y = -1;
7781     int newx, newy;
7782     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7783
7784     if (game.all_players_gone)
7785     {
7786       attr_x = game.exit_x;
7787       attr_y = game.exit_y;
7788     }
7789     else
7790     {
7791       int i;
7792
7793       for (i = 0; i < MAX_PLAYERS; i++)
7794       {
7795         struct PlayerInfo *player = &stored_player[i];
7796         int jx = player->jx, jy = player->jy;
7797
7798         if (!player->active)
7799           continue;
7800
7801         if (attr_x == -1 ||
7802             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7803         {
7804           attr_x = jx;
7805           attr_y = jy;
7806         }
7807       }
7808     }
7809
7810     MovDir[x][y] = MV_NONE;
7811     if (attr_x < x)
7812       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7813     else if (attr_x > x)
7814       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7815     if (attr_y < y)
7816       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7817     else if (attr_y > y)
7818       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7819
7820     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7821
7822     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7823     {
7824       boolean first_horiz = RND(2);
7825       int new_move_dir = MovDir[x][y];
7826
7827       if (element_info[element].move_stepsize == 0)     // "not moving"
7828       {
7829         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7830         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7831
7832         return;
7833       }
7834
7835       MovDir[x][y] =
7836         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7837       Moving2Blocked(x, y, &newx, &newy);
7838
7839       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7840         return;
7841
7842       MovDir[x][y] =
7843         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7844       Moving2Blocked(x, y, &newx, &newy);
7845
7846       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7847         return;
7848
7849       MovDir[x][y] = old_move_dir;
7850     }
7851   }
7852   else if (move_pattern == MV_WHEN_PUSHED ||
7853            move_pattern == MV_WHEN_DROPPED)
7854   {
7855     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7856       MovDir[x][y] = MV_NONE;
7857
7858     MovDelay[x][y] = 0;
7859   }
7860   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7861   {
7862     struct XY *test_xy = xy_topdown;
7863     static int test_dir[4] =
7864     {
7865       MV_UP,
7866       MV_LEFT,
7867       MV_RIGHT,
7868       MV_DOWN
7869     };
7870     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7871     int move_preference = -1000000;     // start with very low preference
7872     int new_move_dir = MV_NONE;
7873     int start_test = RND(4);
7874     int i;
7875
7876     for (i = 0; i < NUM_DIRECTIONS; i++)
7877     {
7878       int j = (start_test + i) % 4;
7879       int move_dir = test_dir[j];
7880       int move_dir_preference;
7881
7882       xx = x + test_xy[j].x;
7883       yy = y + test_xy[j].y;
7884
7885       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7886           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7887       {
7888         new_move_dir = move_dir;
7889
7890         break;
7891       }
7892
7893       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7894         continue;
7895
7896       move_dir_preference = -1 * RunnerVisit[xx][yy];
7897       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7898         move_dir_preference = PlayerVisit[xx][yy];
7899
7900       if (move_dir_preference > move_preference)
7901       {
7902         // prefer field that has not been visited for the longest time
7903         move_preference = move_dir_preference;
7904         new_move_dir = move_dir;
7905       }
7906       else if (move_dir_preference == move_preference &&
7907                move_dir == old_move_dir)
7908       {
7909         // prefer last direction when all directions are preferred equally
7910         move_preference = move_dir_preference;
7911         new_move_dir = move_dir;
7912       }
7913     }
7914
7915     MovDir[x][y] = new_move_dir;
7916     if (old_move_dir != new_move_dir)
7917       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7918   }
7919 }
7920
7921 static void TurnRound(int x, int y)
7922 {
7923   int direction = MovDir[x][y];
7924
7925   TurnRoundExt(x, y);
7926
7927   GfxDir[x][y] = MovDir[x][y];
7928
7929   if (direction != MovDir[x][y])
7930     GfxFrame[x][y] = 0;
7931
7932   if (MovDelay[x][y])
7933     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7934
7935   ResetGfxFrame(x, y);
7936 }
7937
7938 static boolean JustBeingPushed(int x, int y)
7939 {
7940   int i;
7941
7942   for (i = 0; i < MAX_PLAYERS; i++)
7943   {
7944     struct PlayerInfo *player = &stored_player[i];
7945
7946     if (player->active && player->is_pushing && player->MovPos)
7947     {
7948       int next_jx = player->jx + (player->jx - player->last_jx);
7949       int next_jy = player->jy + (player->jy - player->last_jy);
7950
7951       if (x == next_jx && y == next_jy)
7952         return TRUE;
7953     }
7954   }
7955
7956   return FALSE;
7957 }
7958
7959 static void StartMoving(int x, int y)
7960 {
7961   boolean started_moving = FALSE;       // some elements can fall _and_ move
7962   int element = Tile[x][y];
7963
7964   if (Stop[x][y])
7965     return;
7966
7967   if (MovDelay[x][y] == 0)
7968     GfxAction[x][y] = ACTION_DEFAULT;
7969
7970   if (CAN_FALL(element) && y < lev_fieldy - 1)
7971   {
7972     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7973         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7974       if (JustBeingPushed(x, y))
7975         return;
7976
7977     if (element == EL_QUICKSAND_FULL)
7978     {
7979       if (IS_FREE(x, y + 1))
7980       {
7981         InitMovingField(x, y, MV_DOWN);
7982         started_moving = TRUE;
7983
7984         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7985 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7986         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7987           Store[x][y] = EL_ROCK;
7988 #else
7989         Store[x][y] = EL_ROCK;
7990 #endif
7991
7992         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7993       }
7994       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7995       {
7996         if (!MovDelay[x][y])
7997         {
7998           MovDelay[x][y] = TILEY + 1;
7999
8000           ResetGfxAnimation(x, y);
8001           ResetGfxAnimation(x, y + 1);
8002         }
8003
8004         if (MovDelay[x][y])
8005         {
8006           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8007           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8008
8009           MovDelay[x][y]--;
8010           if (MovDelay[x][y])
8011             return;
8012         }
8013
8014         Tile[x][y] = EL_QUICKSAND_EMPTY;
8015         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8016         Store[x][y + 1] = Store[x][y];
8017         Store[x][y] = 0;
8018
8019         PlayLevelSoundAction(x, y, ACTION_FILLING);
8020       }
8021       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8022       {
8023         if (!MovDelay[x][y])
8024         {
8025           MovDelay[x][y] = TILEY + 1;
8026
8027           ResetGfxAnimation(x, y);
8028           ResetGfxAnimation(x, y + 1);
8029         }
8030
8031         if (MovDelay[x][y])
8032         {
8033           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8034           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8035
8036           MovDelay[x][y]--;
8037           if (MovDelay[x][y])
8038             return;
8039         }
8040
8041         Tile[x][y] = EL_QUICKSAND_EMPTY;
8042         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8043         Store[x][y + 1] = Store[x][y];
8044         Store[x][y] = 0;
8045
8046         PlayLevelSoundAction(x, y, ACTION_FILLING);
8047       }
8048     }
8049     else if (element == EL_QUICKSAND_FAST_FULL)
8050     {
8051       if (IS_FREE(x, y + 1))
8052       {
8053         InitMovingField(x, y, MV_DOWN);
8054         started_moving = TRUE;
8055
8056         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8057 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8058         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8059           Store[x][y] = EL_ROCK;
8060 #else
8061         Store[x][y] = EL_ROCK;
8062 #endif
8063
8064         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8065       }
8066       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8067       {
8068         if (!MovDelay[x][y])
8069         {
8070           MovDelay[x][y] = TILEY + 1;
8071
8072           ResetGfxAnimation(x, y);
8073           ResetGfxAnimation(x, y + 1);
8074         }
8075
8076         if (MovDelay[x][y])
8077         {
8078           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8079           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8080
8081           MovDelay[x][y]--;
8082           if (MovDelay[x][y])
8083             return;
8084         }
8085
8086         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8087         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088         Store[x][y + 1] = Store[x][y];
8089         Store[x][y] = 0;
8090
8091         PlayLevelSoundAction(x, y, ACTION_FILLING);
8092       }
8093       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8094       {
8095         if (!MovDelay[x][y])
8096         {
8097           MovDelay[x][y] = TILEY + 1;
8098
8099           ResetGfxAnimation(x, y);
8100           ResetGfxAnimation(x, y + 1);
8101         }
8102
8103         if (MovDelay[x][y])
8104         {
8105           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8106           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8107
8108           MovDelay[x][y]--;
8109           if (MovDelay[x][y])
8110             return;
8111         }
8112
8113         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8114         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8115         Store[x][y + 1] = Store[x][y];
8116         Store[x][y] = 0;
8117
8118         PlayLevelSoundAction(x, y, ACTION_FILLING);
8119       }
8120     }
8121     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8122              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8123     {
8124       InitMovingField(x, y, MV_DOWN);
8125       started_moving = TRUE;
8126
8127       Tile[x][y] = EL_QUICKSAND_FILLING;
8128       Store[x][y] = element;
8129
8130       PlayLevelSoundAction(x, y, ACTION_FILLING);
8131     }
8132     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8133              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8134     {
8135       InitMovingField(x, y, MV_DOWN);
8136       started_moving = TRUE;
8137
8138       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8139       Store[x][y] = element;
8140
8141       PlayLevelSoundAction(x, y, ACTION_FILLING);
8142     }
8143     else if (element == EL_MAGIC_WALL_FULL)
8144     {
8145       if (IS_FREE(x, y + 1))
8146       {
8147         InitMovingField(x, y, MV_DOWN);
8148         started_moving = TRUE;
8149
8150         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8151         Store[x][y] = EL_CHANGED(Store[x][y]);
8152       }
8153       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8154       {
8155         if (!MovDelay[x][y])
8156           MovDelay[x][y] = TILEY / 4 + 1;
8157
8158         if (MovDelay[x][y])
8159         {
8160           MovDelay[x][y]--;
8161           if (MovDelay[x][y])
8162             return;
8163         }
8164
8165         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8166         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8167         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8168         Store[x][y] = 0;
8169       }
8170     }
8171     else if (element == EL_BD_MAGIC_WALL_FULL)
8172     {
8173       if (IS_FREE(x, y + 1))
8174       {
8175         InitMovingField(x, y, MV_DOWN);
8176         started_moving = TRUE;
8177
8178         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8179         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8180       }
8181       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8182       {
8183         if (!MovDelay[x][y])
8184           MovDelay[x][y] = TILEY / 4 + 1;
8185
8186         if (MovDelay[x][y])
8187         {
8188           MovDelay[x][y]--;
8189           if (MovDelay[x][y])
8190             return;
8191         }
8192
8193         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8194         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8195         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8196         Store[x][y] = 0;
8197       }
8198     }
8199     else if (element == EL_DC_MAGIC_WALL_FULL)
8200     {
8201       if (IS_FREE(x, y + 1))
8202       {
8203         InitMovingField(x, y, MV_DOWN);
8204         started_moving = TRUE;
8205
8206         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8207         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8208       }
8209       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8210       {
8211         if (!MovDelay[x][y])
8212           MovDelay[x][y] = TILEY / 4 + 1;
8213
8214         if (MovDelay[x][y])
8215         {
8216           MovDelay[x][y]--;
8217           if (MovDelay[x][y])
8218             return;
8219         }
8220
8221         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8222         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8223         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8224         Store[x][y] = 0;
8225       }
8226     }
8227     else if ((CAN_PASS_MAGIC_WALL(element) &&
8228               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8229                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8230              (CAN_PASS_DC_MAGIC_WALL(element) &&
8231               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8232
8233     {
8234       InitMovingField(x, y, MV_DOWN);
8235       started_moving = TRUE;
8236
8237       Tile[x][y] =
8238         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8239          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8240          EL_DC_MAGIC_WALL_FILLING);
8241       Store[x][y] = element;
8242     }
8243     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8244     {
8245       SplashAcid(x, y + 1);
8246
8247       InitMovingField(x, y, MV_DOWN);
8248       started_moving = TRUE;
8249
8250       Store[x][y] = EL_ACID;
8251     }
8252     else if (
8253              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8254               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8255              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8256               CAN_FALL(element) && WasJustFalling[x][y] &&
8257               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8258
8259              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8260               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8261               (Tile[x][y + 1] == EL_BLOCKED)))
8262     {
8263       /* this is needed for a special case not covered by calling "Impact()"
8264          from "ContinueMoving()": if an element moves to a tile directly below
8265          another element which was just falling on that tile (which was empty
8266          in the previous frame), the falling element above would just stop
8267          instead of smashing the element below (in previous version, the above
8268          element was just checked for "moving" instead of "falling", resulting
8269          in incorrect smashes caused by horizontal movement of the above
8270          element; also, the case of the player being the element to smash was
8271          simply not covered here... :-/ ) */
8272
8273       CheckCollision[x][y] = 0;
8274       CheckImpact[x][y] = 0;
8275
8276       Impact(x, y);
8277     }
8278     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8279     {
8280       if (MovDir[x][y] == MV_NONE)
8281       {
8282         InitMovingField(x, y, MV_DOWN);
8283         started_moving = TRUE;
8284       }
8285     }
8286     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8287     {
8288       if (WasJustFalling[x][y]) // prevent animation from being restarted
8289         MovDir[x][y] = MV_DOWN;
8290
8291       InitMovingField(x, y, MV_DOWN);
8292       started_moving = TRUE;
8293     }
8294     else if (element == EL_AMOEBA_DROP)
8295     {
8296       Tile[x][y] = EL_AMOEBA_GROWING;
8297       Store[x][y] = EL_AMOEBA_WET;
8298     }
8299     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8300               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8301              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8302              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8303     {
8304       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8305                                 (IS_FREE(x - 1, y + 1) ||
8306                                  Tile[x - 1][y + 1] == EL_ACID));
8307       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8308                                 (IS_FREE(x + 1, y + 1) ||
8309                                  Tile[x + 1][y + 1] == EL_ACID));
8310       boolean can_fall_any  = (can_fall_left || can_fall_right);
8311       boolean can_fall_both = (can_fall_left && can_fall_right);
8312       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8313
8314       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8315       {
8316         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8317           can_fall_right = FALSE;
8318         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8319           can_fall_left = FALSE;
8320         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8321           can_fall_right = FALSE;
8322         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8323           can_fall_left = FALSE;
8324
8325         can_fall_any  = (can_fall_left || can_fall_right);
8326         can_fall_both = FALSE;
8327       }
8328
8329       if (can_fall_both)
8330       {
8331         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8332           can_fall_right = FALSE;       // slip down on left side
8333         else
8334           can_fall_left = !(can_fall_right = RND(2));
8335
8336         can_fall_both = FALSE;
8337       }
8338
8339       if (can_fall_any)
8340       {
8341         // if not determined otherwise, prefer left side for slipping down
8342         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8343         started_moving = TRUE;
8344       }
8345     }
8346     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8347     {
8348       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8349       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8350       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8351       int belt_dir = game.belt_dir[belt_nr];
8352
8353       if ((belt_dir == MV_LEFT  && left_is_free) ||
8354           (belt_dir == MV_RIGHT && right_is_free))
8355       {
8356         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8357
8358         InitMovingField(x, y, belt_dir);
8359         started_moving = TRUE;
8360
8361         Pushed[x][y] = TRUE;
8362         Pushed[nextx][y] = TRUE;
8363
8364         GfxAction[x][y] = ACTION_DEFAULT;
8365       }
8366       else
8367       {
8368         MovDir[x][y] = 0;       // if element was moving, stop it
8369       }
8370     }
8371   }
8372
8373   // not "else if" because of elements that can fall and move (EL_SPRING)
8374   if (CAN_MOVE(element) && !started_moving)
8375   {
8376     int move_pattern = element_info[element].move_pattern;
8377     int newx, newy;
8378
8379     Moving2Blocked(x, y, &newx, &newy);
8380
8381     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8382       return;
8383
8384     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8385         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8386     {
8387       WasJustMoving[x][y] = 0;
8388       CheckCollision[x][y] = 0;
8389
8390       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8391
8392       if (Tile[x][y] != element)        // element has changed
8393         return;
8394     }
8395
8396     if (!MovDelay[x][y])        // start new movement phase
8397     {
8398       // all objects that can change their move direction after each step
8399       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8400
8401       if (element != EL_YAMYAM &&
8402           element != EL_DARK_YAMYAM &&
8403           element != EL_PACMAN &&
8404           !(move_pattern & MV_ANY_DIRECTION) &&
8405           move_pattern != MV_TURNING_LEFT &&
8406           move_pattern != MV_TURNING_RIGHT &&
8407           move_pattern != MV_TURNING_LEFT_RIGHT &&
8408           move_pattern != MV_TURNING_RIGHT_LEFT &&
8409           move_pattern != MV_TURNING_RANDOM)
8410       {
8411         TurnRound(x, y);
8412
8413         if (MovDelay[x][y] && (element == EL_BUG ||
8414                                element == EL_SPACESHIP ||
8415                                element == EL_SP_SNIKSNAK ||
8416                                element == EL_SP_ELECTRON ||
8417                                element == EL_MOLE))
8418           TEST_DrawLevelField(x, y);
8419       }
8420     }
8421
8422     if (MovDelay[x][y])         // wait some time before next movement
8423     {
8424       MovDelay[x][y]--;
8425
8426       if (element == EL_ROBOT ||
8427           element == EL_YAMYAM ||
8428           element == EL_DARK_YAMYAM)
8429       {
8430         DrawLevelElementAnimationIfNeeded(x, y, element);
8431         PlayLevelSoundAction(x, y, ACTION_WAITING);
8432       }
8433       else if (element == EL_SP_ELECTRON)
8434         DrawLevelElementAnimationIfNeeded(x, y, element);
8435       else if (element == EL_DRAGON)
8436       {
8437         int i;
8438         int dir = MovDir[x][y];
8439         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8440         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8441         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8442                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8443                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8444                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8445         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8446
8447         GfxAction[x][y] = ACTION_ATTACKING;
8448
8449         if (IS_PLAYER(x, y))
8450           DrawPlayerField(x, y);
8451         else
8452           TEST_DrawLevelField(x, y);
8453
8454         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8455
8456         for (i = 1; i <= 3; i++)
8457         {
8458           int xx = x + i * dx;
8459           int yy = y + i * dy;
8460           int sx = SCREENX(xx);
8461           int sy = SCREENY(yy);
8462           int flame_graphic = graphic + (i - 1);
8463
8464           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8465             break;
8466
8467           if (MovDelay[x][y])
8468           {
8469             int flamed = MovingOrBlocked2Element(xx, yy);
8470
8471             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8472               Bang(xx, yy);
8473             else
8474               RemoveMovingField(xx, yy);
8475
8476             ChangeDelay[xx][yy] = 0;
8477
8478             Tile[xx][yy] = EL_FLAMES;
8479
8480             if (IN_SCR_FIELD(sx, sy))
8481             {
8482               TEST_DrawLevelFieldCrumbled(xx, yy);
8483               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8484             }
8485           }
8486           else
8487           {
8488             if (Tile[xx][yy] == EL_FLAMES)
8489               Tile[xx][yy] = EL_EMPTY;
8490             TEST_DrawLevelField(xx, yy);
8491           }
8492         }
8493       }
8494
8495       if (MovDelay[x][y])       // element still has to wait some time
8496       {
8497         PlayLevelSoundAction(x, y, ACTION_WAITING);
8498
8499         return;
8500       }
8501     }
8502
8503     // now make next step
8504
8505     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8506
8507     if (DONT_COLLIDE_WITH(element) &&
8508         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8509         !PLAYER_ENEMY_PROTECTED(newx, newy))
8510     {
8511       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8512
8513       return;
8514     }
8515
8516     else if (CAN_MOVE_INTO_ACID(element) &&
8517              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8518              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8519              (MovDir[x][y] == MV_DOWN ||
8520               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8521     {
8522       SplashAcid(newx, newy);
8523       Store[x][y] = EL_ACID;
8524     }
8525     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8526     {
8527       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8528           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8529           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8530           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8531       {
8532         RemoveField(x, y);
8533         TEST_DrawLevelField(x, y);
8534
8535         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8536         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8537           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8538
8539         game.friends_still_needed--;
8540         if (!game.friends_still_needed &&
8541             !game.GameOver &&
8542             game.all_players_gone)
8543           LevelSolved();
8544
8545         return;
8546       }
8547       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8548       {
8549         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8550           TEST_DrawLevelField(newx, newy);
8551         else
8552           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8553       }
8554       else if (!IS_FREE(newx, newy))
8555       {
8556         GfxAction[x][y] = ACTION_WAITING;
8557
8558         if (IS_PLAYER(x, y))
8559           DrawPlayerField(x, y);
8560         else
8561           TEST_DrawLevelField(x, y);
8562
8563         return;
8564       }
8565     }
8566     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8567     {
8568       if (IS_FOOD_PIG(Tile[newx][newy]))
8569       {
8570         if (IS_MOVING(newx, newy))
8571           RemoveMovingField(newx, newy);
8572         else
8573         {
8574           Tile[newx][newy] = EL_EMPTY;
8575           TEST_DrawLevelField(newx, newy);
8576         }
8577
8578         PlayLevelSound(x, y, SND_PIG_DIGGING);
8579       }
8580       else if (!IS_FREE(newx, newy))
8581       {
8582         if (IS_PLAYER(x, y))
8583           DrawPlayerField(x, y);
8584         else
8585           TEST_DrawLevelField(x, y);
8586
8587         return;
8588       }
8589     }
8590     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8591     {
8592       if (Store[x][y] != EL_EMPTY)
8593       {
8594         boolean can_clone = FALSE;
8595         int xx, yy;
8596
8597         // check if element to clone is still there
8598         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8599         {
8600           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8601           {
8602             can_clone = TRUE;
8603
8604             break;
8605           }
8606         }
8607
8608         // cannot clone or target field not free anymore -- do not clone
8609         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8610           Store[x][y] = EL_EMPTY;
8611       }
8612
8613       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8614       {
8615         if (IS_MV_DIAGONAL(MovDir[x][y]))
8616         {
8617           int diagonal_move_dir = MovDir[x][y];
8618           int stored = Store[x][y];
8619           int change_delay = 8;
8620           int graphic;
8621
8622           // android is moving diagonally
8623
8624           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8625
8626           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8627           GfxElement[x][y] = EL_EMC_ANDROID;
8628           GfxAction[x][y] = ACTION_SHRINKING;
8629           GfxDir[x][y] = diagonal_move_dir;
8630           ChangeDelay[x][y] = change_delay;
8631
8632           if (Store[x][y] == EL_EMPTY)
8633             Store[x][y] = GfxElementEmpty[x][y];
8634
8635           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8636                                    GfxDir[x][y]);
8637
8638           DrawLevelGraphicAnimation(x, y, graphic);
8639           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8640
8641           if (Tile[newx][newy] == EL_ACID)
8642           {
8643             SplashAcid(newx, newy);
8644
8645             return;
8646           }
8647
8648           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8649
8650           Store[newx][newy] = EL_EMC_ANDROID;
8651           GfxElement[newx][newy] = EL_EMC_ANDROID;
8652           GfxAction[newx][newy] = ACTION_GROWING;
8653           GfxDir[newx][newy] = diagonal_move_dir;
8654           ChangeDelay[newx][newy] = change_delay;
8655
8656           graphic = el_act_dir2img(GfxElement[newx][newy],
8657                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8658
8659           DrawLevelGraphicAnimation(newx, newy, graphic);
8660           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8661
8662           return;
8663         }
8664         else
8665         {
8666           Tile[newx][newy] = EL_EMPTY;
8667           TEST_DrawLevelField(newx, newy);
8668
8669           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8670         }
8671       }
8672       else if (!IS_FREE(newx, newy))
8673       {
8674         return;
8675       }
8676     }
8677     else if (IS_CUSTOM_ELEMENT(element) &&
8678              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8679     {
8680       if (!DigFieldByCE(newx, newy, element))
8681         return;
8682
8683       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8684       {
8685         RunnerVisit[x][y] = FrameCounter;
8686         PlayerVisit[x][y] /= 8;         // expire player visit path
8687       }
8688     }
8689     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8690     {
8691       if (!IS_FREE(newx, newy))
8692       {
8693         if (IS_PLAYER(x, y))
8694           DrawPlayerField(x, y);
8695         else
8696           TEST_DrawLevelField(x, y);
8697
8698         return;
8699       }
8700       else
8701       {
8702         boolean wanna_flame = !RND(10);
8703         int dx = newx - x, dy = newy - y;
8704         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8705         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8706         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8707                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8708         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8709                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8710
8711         if ((wanna_flame ||
8712              IS_CLASSIC_ENEMY(element1) ||
8713              IS_CLASSIC_ENEMY(element2)) &&
8714             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8715             element1 != EL_FLAMES && element2 != EL_FLAMES)
8716         {
8717           ResetGfxAnimation(x, y);
8718           GfxAction[x][y] = ACTION_ATTACKING;
8719
8720           if (IS_PLAYER(x, y))
8721             DrawPlayerField(x, y);
8722           else
8723             TEST_DrawLevelField(x, y);
8724
8725           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8726
8727           MovDelay[x][y] = 50;
8728
8729           Tile[newx][newy] = EL_FLAMES;
8730           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8731             Tile[newx1][newy1] = EL_FLAMES;
8732           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8733             Tile[newx2][newy2] = EL_FLAMES;
8734
8735           return;
8736         }
8737       }
8738     }
8739     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8740              Tile[newx][newy] == EL_DIAMOND)
8741     {
8742       if (IS_MOVING(newx, newy))
8743         RemoveMovingField(newx, newy);
8744       else
8745       {
8746         Tile[newx][newy] = EL_EMPTY;
8747         TEST_DrawLevelField(newx, newy);
8748       }
8749
8750       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8751     }
8752     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8753              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8754     {
8755       if (AmoebaNr[newx][newy])
8756       {
8757         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8758         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8759             Tile[newx][newy] == EL_BD_AMOEBA)
8760           AmoebaCnt[AmoebaNr[newx][newy]]--;
8761       }
8762
8763       if (IS_MOVING(newx, newy))
8764       {
8765         RemoveMovingField(newx, newy);
8766       }
8767       else
8768       {
8769         Tile[newx][newy] = EL_EMPTY;
8770         TEST_DrawLevelField(newx, newy);
8771       }
8772
8773       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8774     }
8775     else if ((element == EL_PACMAN || element == EL_MOLE)
8776              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8777     {
8778       if (AmoebaNr[newx][newy])
8779       {
8780         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8781         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8782             Tile[newx][newy] == EL_BD_AMOEBA)
8783           AmoebaCnt[AmoebaNr[newx][newy]]--;
8784       }
8785
8786       if (element == EL_MOLE)
8787       {
8788         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8789         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8790
8791         ResetGfxAnimation(x, y);
8792         GfxAction[x][y] = ACTION_DIGGING;
8793         TEST_DrawLevelField(x, y);
8794
8795         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8796
8797         return;                         // wait for shrinking amoeba
8798       }
8799       else      // element == EL_PACMAN
8800       {
8801         Tile[newx][newy] = EL_EMPTY;
8802         TEST_DrawLevelField(newx, newy);
8803         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8804       }
8805     }
8806     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8807              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8808               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8809     {
8810       // wait for shrinking amoeba to completely disappear
8811       return;
8812     }
8813     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8814     {
8815       // object was running against a wall
8816
8817       TurnRound(x, y);
8818
8819       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8820         DrawLevelElementAnimation(x, y, element);
8821
8822       if (DONT_TOUCH(element))
8823         TestIfBadThingTouchesPlayer(x, y);
8824
8825       return;
8826     }
8827
8828     InitMovingField(x, y, MovDir[x][y]);
8829
8830     PlayLevelSoundAction(x, y, ACTION_MOVING);
8831   }
8832
8833   if (MovDir[x][y])
8834     ContinueMoving(x, y);
8835 }
8836
8837 void ContinueMoving(int x, int y)
8838 {
8839   int element = Tile[x][y];
8840   struct ElementInfo *ei = &element_info[element];
8841   int direction = MovDir[x][y];
8842   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8843   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8844   int newx = x + dx, newy = y + dy;
8845   int stored = Store[x][y];
8846   int stored_new = Store[newx][newy];
8847   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8848   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8849   boolean last_line = (newy == lev_fieldy - 1);
8850   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8851
8852   if (pushed_by_player)         // special case: moving object pushed by player
8853   {
8854     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8855   }
8856   else if (use_step_delay)      // special case: moving object has step delay
8857   {
8858     if (!MovDelay[x][y])
8859       MovPos[x][y] += getElementMoveStepsize(x, y);
8860
8861     if (MovDelay[x][y])
8862       MovDelay[x][y]--;
8863     else
8864       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8865
8866     if (MovDelay[x][y])
8867     {
8868       TEST_DrawLevelField(x, y);
8869
8870       return;   // element is still waiting
8871     }
8872   }
8873   else                          // normal case: generically moving object
8874   {
8875     MovPos[x][y] += getElementMoveStepsize(x, y);
8876   }
8877
8878   if (ABS(MovPos[x][y]) < TILEX)
8879   {
8880     TEST_DrawLevelField(x, y);
8881
8882     return;     // element is still moving
8883   }
8884
8885   // element reached destination field
8886
8887   Tile[x][y] = EL_EMPTY;
8888   Tile[newx][newy] = element;
8889   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8890
8891   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8892   {
8893     element = Tile[newx][newy] = EL_ACID;
8894   }
8895   else if (element == EL_MOLE)
8896   {
8897     Tile[x][y] = EL_SAND;
8898
8899     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8900   }
8901   else if (element == EL_QUICKSAND_FILLING)
8902   {
8903     element = Tile[newx][newy] = get_next_element(element);
8904     Store[newx][newy] = Store[x][y];
8905   }
8906   else if (element == EL_QUICKSAND_EMPTYING)
8907   {
8908     Tile[x][y] = get_next_element(element);
8909     element = Tile[newx][newy] = Store[x][y];
8910   }
8911   else if (element == EL_QUICKSAND_FAST_FILLING)
8912   {
8913     element = Tile[newx][newy] = get_next_element(element);
8914     Store[newx][newy] = Store[x][y];
8915   }
8916   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8917   {
8918     Tile[x][y] = get_next_element(element);
8919     element = Tile[newx][newy] = Store[x][y];
8920   }
8921   else if (element == EL_MAGIC_WALL_FILLING)
8922   {
8923     element = Tile[newx][newy] = get_next_element(element);
8924     if (!game.magic_wall_active)
8925       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8926     Store[newx][newy] = Store[x][y];
8927   }
8928   else if (element == EL_MAGIC_WALL_EMPTYING)
8929   {
8930     Tile[x][y] = get_next_element(element);
8931     if (!game.magic_wall_active)
8932       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8933     element = Tile[newx][newy] = Store[x][y];
8934
8935     InitField(newx, newy, FALSE);
8936   }
8937   else if (element == EL_BD_MAGIC_WALL_FILLING)
8938   {
8939     element = Tile[newx][newy] = get_next_element(element);
8940     if (!game.magic_wall_active)
8941       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8942     Store[newx][newy] = Store[x][y];
8943   }
8944   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8945   {
8946     Tile[x][y] = get_next_element(element);
8947     if (!game.magic_wall_active)
8948       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8949     element = Tile[newx][newy] = Store[x][y];
8950
8951     InitField(newx, newy, FALSE);
8952   }
8953   else if (element == EL_DC_MAGIC_WALL_FILLING)
8954   {
8955     element = Tile[newx][newy] = get_next_element(element);
8956     if (!game.magic_wall_active)
8957       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8958     Store[newx][newy] = Store[x][y];
8959   }
8960   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8961   {
8962     Tile[x][y] = get_next_element(element);
8963     if (!game.magic_wall_active)
8964       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8965     element = Tile[newx][newy] = Store[x][y];
8966
8967     InitField(newx, newy, FALSE);
8968   }
8969   else if (element == EL_AMOEBA_DROPPING)
8970   {
8971     Tile[x][y] = get_next_element(element);
8972     element = Tile[newx][newy] = Store[x][y];
8973   }
8974   else if (element == EL_SOKOBAN_OBJECT)
8975   {
8976     if (Back[x][y])
8977       Tile[x][y] = Back[x][y];
8978
8979     if (Back[newx][newy])
8980       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8981
8982     Back[x][y] = Back[newx][newy] = 0;
8983   }
8984
8985   Store[x][y] = EL_EMPTY;
8986   MovPos[x][y] = 0;
8987   MovDir[x][y] = 0;
8988   MovDelay[x][y] = 0;
8989
8990   MovDelay[newx][newy] = 0;
8991
8992   if (CAN_CHANGE_OR_HAS_ACTION(element))
8993   {
8994     // copy element change control values to new field
8995     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8996     ChangePage[newx][newy]  = ChangePage[x][y];
8997     ChangeCount[newx][newy] = ChangeCount[x][y];
8998     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8999   }
9000
9001   CustomValue[newx][newy] = CustomValue[x][y];
9002
9003   ChangeDelay[x][y] = 0;
9004   ChangePage[x][y] = -1;
9005   ChangeCount[x][y] = 0;
9006   ChangeEvent[x][y] = -1;
9007
9008   CustomValue[x][y] = 0;
9009
9010   // copy animation control values to new field
9011   GfxFrame[newx][newy]  = GfxFrame[x][y];
9012   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
9013   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
9014   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
9015
9016   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9017
9018   // some elements can leave other elements behind after moving
9019   if (ei->move_leave_element != EL_EMPTY &&
9020       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9021       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9022   {
9023     int move_leave_element = ei->move_leave_element;
9024
9025     // this makes it possible to leave the removed element again
9026     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9027       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9028
9029     Tile[x][y] = move_leave_element;
9030
9031     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9032       MovDir[x][y] = direction;
9033
9034     InitField(x, y, FALSE);
9035
9036     if (GFX_CRUMBLED(Tile[x][y]))
9037       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9038
9039     if (IS_PLAYER_ELEMENT(move_leave_element))
9040       RelocatePlayer(x, y, move_leave_element);
9041   }
9042
9043   // do this after checking for left-behind element
9044   ResetGfxAnimation(x, y);      // reset animation values for old field
9045
9046   if (!CAN_MOVE(element) ||
9047       (CAN_FALL(element) && direction == MV_DOWN &&
9048        (element == EL_SPRING ||
9049         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9050         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9051     GfxDir[x][y] = MovDir[newx][newy] = 0;
9052
9053   TEST_DrawLevelField(x, y);
9054   TEST_DrawLevelField(newx, newy);
9055
9056   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9057
9058   // prevent pushed element from moving on in pushed direction
9059   if (pushed_by_player && CAN_MOVE(element) &&
9060       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9061       !(element_info[element].move_pattern & direction))
9062     TurnRound(newx, newy);
9063
9064   // prevent elements on conveyor belt from moving on in last direction
9065   if (pushed_by_conveyor && CAN_FALL(element) &&
9066       direction & MV_HORIZONTAL)
9067     MovDir[newx][newy] = 0;
9068
9069   if (!pushed_by_player)
9070   {
9071     int nextx = newx + dx, nexty = newy + dy;
9072     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9073
9074     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9075
9076     if (CAN_FALL(element) && direction == MV_DOWN)
9077       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9078
9079     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9080       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9081
9082     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9083       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9084   }
9085
9086   if (DONT_TOUCH(element))      // object may be nasty to player or others
9087   {
9088     TestIfBadThingTouchesPlayer(newx, newy);
9089     TestIfBadThingTouchesFriend(newx, newy);
9090
9091     if (!IS_CUSTOM_ELEMENT(element))
9092       TestIfBadThingTouchesOtherBadThing(newx, newy);
9093   }
9094   else if (element == EL_PENGUIN)
9095     TestIfFriendTouchesBadThing(newx, newy);
9096
9097   if (DONT_GET_HIT_BY(element))
9098   {
9099     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9100   }
9101
9102   // give the player one last chance (one more frame) to move away
9103   if (CAN_FALL(element) && direction == MV_DOWN &&
9104       (last_line || (!IS_FREE(x, newy + 1) &&
9105                      (!IS_PLAYER(x, newy + 1) ||
9106                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9107     Impact(x, newy);
9108
9109   if (pushed_by_player && !game.use_change_when_pushing_bug)
9110   {
9111     int push_side = MV_DIR_OPPOSITE(direction);
9112     struct PlayerInfo *player = PLAYERINFO(x, y);
9113
9114     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9115                                player->index_bit, push_side);
9116     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9117                                         player->index_bit, push_side);
9118   }
9119
9120   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9121     MovDelay[newx][newy] = 1;
9122
9123   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9124
9125   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9126   TestIfElementHitsCustomElement(newx, newy, direction);
9127   TestIfPlayerTouchesCustomElement(newx, newy);
9128   TestIfElementTouchesCustomElement(newx, newy);
9129
9130   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9131       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9132     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9133                              MV_DIR_OPPOSITE(direction));
9134 }
9135
9136 int AmoebaNeighbourNr(int ax, int ay)
9137 {
9138   int i;
9139   int element = Tile[ax][ay];
9140   int group_nr = 0;
9141   struct XY *xy = xy_topdown;
9142
9143   for (i = 0; i < NUM_DIRECTIONS; i++)
9144   {
9145     int x = ax + xy[i].x;
9146     int y = ay + xy[i].y;
9147
9148     if (!IN_LEV_FIELD(x, y))
9149       continue;
9150
9151     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9152       group_nr = AmoebaNr[x][y];
9153   }
9154
9155   return group_nr;
9156 }
9157
9158 static void AmoebaMerge(int ax, int ay)
9159 {
9160   int i, x, y, xx, yy;
9161   int new_group_nr = AmoebaNr[ax][ay];
9162   struct XY *xy = xy_topdown;
9163
9164   if (new_group_nr == 0)
9165     return;
9166
9167   for (i = 0; i < NUM_DIRECTIONS; i++)
9168   {
9169     x = ax + xy[i].x;
9170     y = ay + xy[i].y;
9171
9172     if (!IN_LEV_FIELD(x, y))
9173       continue;
9174
9175     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9176          Tile[x][y] == EL_BD_AMOEBA ||
9177          Tile[x][y] == EL_AMOEBA_DEAD) &&
9178         AmoebaNr[x][y] != new_group_nr)
9179     {
9180       int old_group_nr = AmoebaNr[x][y];
9181
9182       if (old_group_nr == 0)
9183         return;
9184
9185       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9186       AmoebaCnt[old_group_nr] = 0;
9187       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9188       AmoebaCnt2[old_group_nr] = 0;
9189
9190       SCAN_PLAYFIELD(xx, yy)
9191       {
9192         if (AmoebaNr[xx][yy] == old_group_nr)
9193           AmoebaNr[xx][yy] = new_group_nr;
9194       }
9195     }
9196   }
9197 }
9198
9199 void AmoebaToDiamond(int ax, int ay)
9200 {
9201   int i, x, y;
9202
9203   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9204   {
9205     int group_nr = AmoebaNr[ax][ay];
9206
9207 #ifdef DEBUG
9208     if (group_nr == 0)
9209     {
9210       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9211       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9212
9213       return;
9214     }
9215 #endif
9216
9217     SCAN_PLAYFIELD(x, y)
9218     {
9219       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9220       {
9221         AmoebaNr[x][y] = 0;
9222         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9223       }
9224     }
9225
9226     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9227                             SND_AMOEBA_TURNING_TO_GEM :
9228                             SND_AMOEBA_TURNING_TO_ROCK));
9229     Bang(ax, ay);
9230   }
9231   else
9232   {
9233     struct XY *xy = xy_topdown;
9234
9235     for (i = 0; i < NUM_DIRECTIONS; i++)
9236     {
9237       x = ax + xy[i].x;
9238       y = ay + xy[i].y;
9239
9240       if (!IN_LEV_FIELD(x, y))
9241         continue;
9242
9243       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9244       {
9245         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9246                               SND_AMOEBA_TURNING_TO_GEM :
9247                               SND_AMOEBA_TURNING_TO_ROCK));
9248         Bang(x, y);
9249       }
9250     }
9251   }
9252 }
9253
9254 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9255 {
9256   int x, y;
9257   int group_nr = AmoebaNr[ax][ay];
9258   boolean done = FALSE;
9259
9260 #ifdef DEBUG
9261   if (group_nr == 0)
9262   {
9263     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9264     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9265
9266     return;
9267   }
9268 #endif
9269
9270   SCAN_PLAYFIELD(x, y)
9271   {
9272     if (AmoebaNr[x][y] == group_nr &&
9273         (Tile[x][y] == EL_AMOEBA_DEAD ||
9274          Tile[x][y] == EL_BD_AMOEBA ||
9275          Tile[x][y] == EL_AMOEBA_GROWING))
9276     {
9277       AmoebaNr[x][y] = 0;
9278       Tile[x][y] = new_element;
9279       InitField(x, y, FALSE);
9280       TEST_DrawLevelField(x, y);
9281       done = TRUE;
9282     }
9283   }
9284
9285   if (done)
9286     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9287                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9288                             SND_BD_AMOEBA_TURNING_TO_GEM));
9289 }
9290
9291 static void AmoebaGrowing(int x, int y)
9292 {
9293   static DelayCounter sound_delay = { 0 };
9294
9295   if (!MovDelay[x][y])          // start new growing cycle
9296   {
9297     MovDelay[x][y] = 7;
9298
9299     if (DelayReached(&sound_delay))
9300     {
9301       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9302       sound_delay.value = 30;
9303     }
9304   }
9305
9306   if (MovDelay[x][y])           // wait some time before growing bigger
9307   {
9308     MovDelay[x][y]--;
9309     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9310     {
9311       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9312                                            6 - MovDelay[x][y]);
9313
9314       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9315     }
9316
9317     if (!MovDelay[x][y])
9318     {
9319       Tile[x][y] = Store[x][y];
9320       Store[x][y] = 0;
9321       TEST_DrawLevelField(x, y);
9322     }
9323   }
9324 }
9325
9326 static void AmoebaShrinking(int x, int y)
9327 {
9328   static DelayCounter sound_delay = { 0 };
9329
9330   if (!MovDelay[x][y])          // start new shrinking cycle
9331   {
9332     MovDelay[x][y] = 7;
9333
9334     if (DelayReached(&sound_delay))
9335       sound_delay.value = 30;
9336   }
9337
9338   if (MovDelay[x][y])           // wait some time before shrinking
9339   {
9340     MovDelay[x][y]--;
9341     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9342     {
9343       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9344                                            6 - MovDelay[x][y]);
9345
9346       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9347     }
9348
9349     if (!MovDelay[x][y])
9350     {
9351       Tile[x][y] = EL_EMPTY;
9352       TEST_DrawLevelField(x, y);
9353
9354       // don't let mole enter this field in this cycle;
9355       // (give priority to objects falling to this field from above)
9356       Stop[x][y] = TRUE;
9357     }
9358   }
9359 }
9360
9361 static void AmoebaReproduce(int ax, int ay)
9362 {
9363   int i;
9364   int element = Tile[ax][ay];
9365   int graphic = el2img(element);
9366   int newax = ax, neway = ay;
9367   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9368   struct XY *xy = xy_topdown;
9369
9370   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9371   {
9372     Tile[ax][ay] = EL_AMOEBA_DEAD;
9373     TEST_DrawLevelField(ax, ay);
9374     return;
9375   }
9376
9377   if (IS_ANIMATED(graphic))
9378     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9379
9380   if (!MovDelay[ax][ay])        // start making new amoeba field
9381     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9382
9383   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9384   {
9385     MovDelay[ax][ay]--;
9386     if (MovDelay[ax][ay])
9387       return;
9388   }
9389
9390   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9391   {
9392     int start = RND(4);
9393     int x = ax + xy[start].x;
9394     int y = ay + xy[start].y;
9395
9396     if (!IN_LEV_FIELD(x, y))
9397       return;
9398
9399     if (IS_FREE(x, y) ||
9400         CAN_GROW_INTO(Tile[x][y]) ||
9401         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9402         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9403     {
9404       newax = x;
9405       neway = y;
9406     }
9407
9408     if (newax == ax && neway == ay)
9409       return;
9410   }
9411   else                          // normal or "filled" (BD style) amoeba
9412   {
9413     int start = RND(4);
9414     boolean waiting_for_player = FALSE;
9415
9416     for (i = 0; i < NUM_DIRECTIONS; i++)
9417     {
9418       int j = (start + i) % 4;
9419       int x = ax + xy[j].x;
9420       int y = ay + xy[j].y;
9421
9422       if (!IN_LEV_FIELD(x, y))
9423         continue;
9424
9425       if (IS_FREE(x, y) ||
9426           CAN_GROW_INTO(Tile[x][y]) ||
9427           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9428           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9429       {
9430         newax = x;
9431         neway = y;
9432         break;
9433       }
9434       else if (IS_PLAYER(x, y))
9435         waiting_for_player = TRUE;
9436     }
9437
9438     if (newax == ax && neway == ay)             // amoeba cannot grow
9439     {
9440       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9441       {
9442         Tile[ax][ay] = EL_AMOEBA_DEAD;
9443         TEST_DrawLevelField(ax, ay);
9444         AmoebaCnt[AmoebaNr[ax][ay]]--;
9445
9446         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9447         {
9448           if (element == EL_AMOEBA_FULL)
9449             AmoebaToDiamond(ax, ay);
9450           else if (element == EL_BD_AMOEBA)
9451             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9452         }
9453       }
9454       return;
9455     }
9456     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9457     {
9458       // amoeba gets larger by growing in some direction
9459
9460       int new_group_nr = AmoebaNr[ax][ay];
9461
9462 #ifdef DEBUG
9463   if (new_group_nr == 0)
9464   {
9465     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9466           newax, neway);
9467     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9468
9469     return;
9470   }
9471 #endif
9472
9473       AmoebaNr[newax][neway] = new_group_nr;
9474       AmoebaCnt[new_group_nr]++;
9475       AmoebaCnt2[new_group_nr]++;
9476
9477       // if amoeba touches other amoeba(s) after growing, unify them
9478       AmoebaMerge(newax, neway);
9479
9480       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9481       {
9482         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9483         return;
9484       }
9485     }
9486   }
9487
9488   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9489       (neway == lev_fieldy - 1 && newax != ax))
9490   {
9491     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9492     Store[newax][neway] = element;
9493   }
9494   else if (neway == ay || element == EL_EMC_DRIPPER)
9495   {
9496     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9497
9498     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9499   }
9500   else
9501   {
9502     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9503     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9504     Store[ax][ay] = EL_AMOEBA_DROP;
9505     ContinueMoving(ax, ay);
9506     return;
9507   }
9508
9509   TEST_DrawLevelField(newax, neway);
9510 }
9511
9512 static void Life(int ax, int ay)
9513 {
9514   int x1, y1, x2, y2;
9515   int life_time = 40;
9516   int element = Tile[ax][ay];
9517   int graphic = el2img(element);
9518   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9519                          level.biomaze);
9520   boolean changed = FALSE;
9521
9522   if (IS_ANIMATED(graphic))
9523     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9524
9525   if (Stop[ax][ay])
9526     return;
9527
9528   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9529     MovDelay[ax][ay] = life_time;
9530
9531   if (MovDelay[ax][ay])         // wait some time before next cycle
9532   {
9533     MovDelay[ax][ay]--;
9534     if (MovDelay[ax][ay])
9535       return;
9536   }
9537
9538   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9539   {
9540     int xx = ax + x1, yy = ay + y1;
9541     int old_element = Tile[xx][yy];
9542     int num_neighbours = 0;
9543
9544     if (!IN_LEV_FIELD(xx, yy))
9545       continue;
9546
9547     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9548     {
9549       int x = xx + x2, y = yy + y2;
9550
9551       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9552         continue;
9553
9554       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9555       boolean is_neighbour = FALSE;
9556
9557       if (level.use_life_bugs)
9558         is_neighbour =
9559           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9560            (IS_FREE(x, y)                             &&  Stop[x][y]));
9561       else
9562         is_neighbour =
9563           (Last[x][y] == element || is_player_cell);
9564
9565       if (is_neighbour)
9566         num_neighbours++;
9567     }
9568
9569     boolean is_free = FALSE;
9570
9571     if (level.use_life_bugs)
9572       is_free = (IS_FREE(xx, yy));
9573     else
9574       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9575
9576     if (xx == ax && yy == ay)           // field in the middle
9577     {
9578       if (num_neighbours < life_parameter[0] ||
9579           num_neighbours > life_parameter[1])
9580       {
9581         Tile[xx][yy] = EL_EMPTY;
9582         if (Tile[xx][yy] != old_element)
9583           TEST_DrawLevelField(xx, yy);
9584         Stop[xx][yy] = TRUE;
9585         changed = TRUE;
9586       }
9587     }
9588     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9589     {                                   // free border field
9590       if (num_neighbours >= life_parameter[2] &&
9591           num_neighbours <= life_parameter[3])
9592       {
9593         Tile[xx][yy] = element;
9594         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9595         if (Tile[xx][yy] != old_element)
9596           TEST_DrawLevelField(xx, yy);
9597         Stop[xx][yy] = TRUE;
9598         changed = TRUE;
9599       }
9600     }
9601   }
9602
9603   if (changed)
9604     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9605                    SND_GAME_OF_LIFE_GROWING);
9606 }
9607
9608 static void InitRobotWheel(int x, int y)
9609 {
9610   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9611 }
9612
9613 static void RunRobotWheel(int x, int y)
9614 {
9615   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9616 }
9617
9618 static void StopRobotWheel(int x, int y)
9619 {
9620   if (game.robot_wheel_x == x &&
9621       game.robot_wheel_y == y)
9622   {
9623     game.robot_wheel_x = -1;
9624     game.robot_wheel_y = -1;
9625     game.robot_wheel_active = FALSE;
9626   }
9627 }
9628
9629 static void InitTimegateWheel(int x, int y)
9630 {
9631   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9632 }
9633
9634 static void RunTimegateWheel(int x, int y)
9635 {
9636   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9637 }
9638
9639 static void InitMagicBallDelay(int x, int y)
9640 {
9641   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9642 }
9643
9644 static void ActivateMagicBall(int bx, int by)
9645 {
9646   int x, y;
9647
9648   if (level.ball_random)
9649   {
9650     int pos_border = RND(8);    // select one of the eight border elements
9651     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9652     int xx = pos_content % 3;
9653     int yy = pos_content / 3;
9654
9655     x = bx - 1 + xx;
9656     y = by - 1 + yy;
9657
9658     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9659       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9660   }
9661   else
9662   {
9663     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9664     {
9665       int xx = x - bx + 1;
9666       int yy = y - by + 1;
9667
9668       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9669         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9670     }
9671   }
9672
9673   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9674 }
9675
9676 static void CheckExit(int x, int y)
9677 {
9678   if (game.gems_still_needed > 0 ||
9679       game.sokoban_fields_still_needed > 0 ||
9680       game.sokoban_objects_still_needed > 0 ||
9681       game.lights_still_needed > 0)
9682   {
9683     int element = Tile[x][y];
9684     int graphic = el2img(element);
9685
9686     if (IS_ANIMATED(graphic))
9687       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9688
9689     return;
9690   }
9691
9692   // do not re-open exit door closed after last player
9693   if (game.all_players_gone)
9694     return;
9695
9696   Tile[x][y] = EL_EXIT_OPENING;
9697
9698   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9699 }
9700
9701 static void CheckExitEM(int x, int y)
9702 {
9703   if (game.gems_still_needed > 0 ||
9704       game.sokoban_fields_still_needed > 0 ||
9705       game.sokoban_objects_still_needed > 0 ||
9706       game.lights_still_needed > 0)
9707   {
9708     int element = Tile[x][y];
9709     int graphic = el2img(element);
9710
9711     if (IS_ANIMATED(graphic))
9712       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9713
9714     return;
9715   }
9716
9717   // do not re-open exit door closed after last player
9718   if (game.all_players_gone)
9719     return;
9720
9721   Tile[x][y] = EL_EM_EXIT_OPENING;
9722
9723   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9724 }
9725
9726 static void CheckExitSteel(int x, int y)
9727 {
9728   if (game.gems_still_needed > 0 ||
9729       game.sokoban_fields_still_needed > 0 ||
9730       game.sokoban_objects_still_needed > 0 ||
9731       game.lights_still_needed > 0)
9732   {
9733     int element = Tile[x][y];
9734     int graphic = el2img(element);
9735
9736     if (IS_ANIMATED(graphic))
9737       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9738
9739     return;
9740   }
9741
9742   // do not re-open exit door closed after last player
9743   if (game.all_players_gone)
9744     return;
9745
9746   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9747
9748   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9749 }
9750
9751 static void CheckExitSteelEM(int x, int y)
9752 {
9753   if (game.gems_still_needed > 0 ||
9754       game.sokoban_fields_still_needed > 0 ||
9755       game.sokoban_objects_still_needed > 0 ||
9756       game.lights_still_needed > 0)
9757   {
9758     int element = Tile[x][y];
9759     int graphic = el2img(element);
9760
9761     if (IS_ANIMATED(graphic))
9762       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9763
9764     return;
9765   }
9766
9767   // do not re-open exit door closed after last player
9768   if (game.all_players_gone)
9769     return;
9770
9771   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9772
9773   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9774 }
9775
9776 static void CheckExitSP(int x, int y)
9777 {
9778   if (game.gems_still_needed > 0)
9779   {
9780     int element = Tile[x][y];
9781     int graphic = el2img(element);
9782
9783     if (IS_ANIMATED(graphic))
9784       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9785
9786     return;
9787   }
9788
9789   // do not re-open exit door closed after last player
9790   if (game.all_players_gone)
9791     return;
9792
9793   Tile[x][y] = EL_SP_EXIT_OPENING;
9794
9795   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9796 }
9797
9798 static void CloseAllOpenTimegates(void)
9799 {
9800   int x, y;
9801
9802   SCAN_PLAYFIELD(x, y)
9803   {
9804     int element = Tile[x][y];
9805
9806     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9807     {
9808       Tile[x][y] = EL_TIMEGATE_CLOSING;
9809
9810       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9811     }
9812   }
9813 }
9814
9815 static void DrawTwinkleOnField(int x, int y)
9816 {
9817   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9818     return;
9819
9820   if (Tile[x][y] == EL_BD_DIAMOND)
9821     return;
9822
9823   if (MovDelay[x][y] == 0)      // next animation frame
9824     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9825
9826   if (MovDelay[x][y] != 0)      // wait some time before next frame
9827   {
9828     MovDelay[x][y]--;
9829
9830     DrawLevelElementAnimation(x, y, Tile[x][y]);
9831
9832     if (MovDelay[x][y] != 0)
9833     {
9834       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9835                                            10 - MovDelay[x][y]);
9836
9837       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9838     }
9839   }
9840 }
9841
9842 static void WallGrowing(int x, int y)
9843 {
9844   int delay = 6;
9845
9846   if (!MovDelay[x][y])          // next animation frame
9847     MovDelay[x][y] = 3 * delay;
9848
9849   if (MovDelay[x][y])           // wait some time before next frame
9850   {
9851     MovDelay[x][y]--;
9852
9853     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9854     {
9855       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9856       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9857
9858       DrawLevelGraphic(x, y, graphic, frame);
9859     }
9860
9861     if (!MovDelay[x][y])
9862     {
9863       if (MovDir[x][y] == MV_LEFT)
9864       {
9865         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9866           TEST_DrawLevelField(x - 1, y);
9867       }
9868       else if (MovDir[x][y] == MV_RIGHT)
9869       {
9870         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9871           TEST_DrawLevelField(x + 1, y);
9872       }
9873       else if (MovDir[x][y] == MV_UP)
9874       {
9875         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9876           TEST_DrawLevelField(x, y - 1);
9877       }
9878       else
9879       {
9880         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9881           TEST_DrawLevelField(x, y + 1);
9882       }
9883
9884       Tile[x][y] = Store[x][y];
9885       Store[x][y] = 0;
9886       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9887       TEST_DrawLevelField(x, y);
9888     }
9889   }
9890 }
9891
9892 static void CheckWallGrowing(int ax, int ay)
9893 {
9894   int element = Tile[ax][ay];
9895   int graphic = el2img(element);
9896   boolean free_top    = FALSE;
9897   boolean free_bottom = FALSE;
9898   boolean free_left   = FALSE;
9899   boolean free_right  = FALSE;
9900   boolean stop_top    = FALSE;
9901   boolean stop_bottom = FALSE;
9902   boolean stop_left   = FALSE;
9903   boolean stop_right  = FALSE;
9904   boolean new_wall    = FALSE;
9905
9906   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9907                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9908                            element == EL_EXPANDABLE_STEELWALL_ANY);
9909
9910   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9911                              element == EL_EXPANDABLE_WALL_ANY ||
9912                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9913                              element == EL_EXPANDABLE_STEELWALL_ANY);
9914
9915   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9916                              element == EL_EXPANDABLE_WALL_ANY ||
9917                              element == EL_EXPANDABLE_WALL ||
9918                              element == EL_BD_EXPANDABLE_WALL ||
9919                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9920                              element == EL_EXPANDABLE_STEELWALL_ANY);
9921
9922   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9923                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9924
9925   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9926                              element == EL_EXPANDABLE_WALL ||
9927                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9928
9929   int wall_growing = (is_steelwall ?
9930                       EL_EXPANDABLE_STEELWALL_GROWING :
9931                       EL_EXPANDABLE_WALL_GROWING);
9932
9933   int gfx_wall_growing_up    = (is_steelwall ?
9934                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9935                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9936   int gfx_wall_growing_down  = (is_steelwall ?
9937                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9938                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9939   int gfx_wall_growing_left  = (is_steelwall ?
9940                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9941                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9942   int gfx_wall_growing_right = (is_steelwall ?
9943                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9944                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9945
9946   if (IS_ANIMATED(graphic))
9947     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9948
9949   if (!MovDelay[ax][ay])        // start building new wall
9950     MovDelay[ax][ay] = 6;
9951
9952   if (MovDelay[ax][ay])         // wait some time before building new wall
9953   {
9954     MovDelay[ax][ay]--;
9955     if (MovDelay[ax][ay])
9956       return;
9957   }
9958
9959   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9960     free_top = TRUE;
9961   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9962     free_bottom = TRUE;
9963   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9964     free_left = TRUE;
9965   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9966     free_right = TRUE;
9967
9968   if (grow_vertical)
9969   {
9970     if (free_top)
9971     {
9972       Tile[ax][ay - 1] = wall_growing;
9973       Store[ax][ay - 1] = element;
9974       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9975
9976       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9977         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9978
9979       new_wall = TRUE;
9980     }
9981
9982     if (free_bottom)
9983     {
9984       Tile[ax][ay + 1] = wall_growing;
9985       Store[ax][ay + 1] = element;
9986       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9987
9988       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9989         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9990
9991       new_wall = TRUE;
9992     }
9993   }
9994
9995   if (grow_horizontal)
9996   {
9997     if (free_left)
9998     {
9999       Tile[ax - 1][ay] = wall_growing;
10000       Store[ax - 1][ay] = element;
10001       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
10002
10003       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
10004         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
10005
10006       new_wall = TRUE;
10007     }
10008
10009     if (free_right)
10010     {
10011       Tile[ax + 1][ay] = wall_growing;
10012       Store[ax + 1][ay] = element;
10013       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
10014
10015       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10016         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10017
10018       new_wall = TRUE;
10019     }
10020   }
10021
10022   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10023     TEST_DrawLevelField(ax, ay);
10024
10025   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10026     stop_top = TRUE;
10027   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10028     stop_bottom = TRUE;
10029   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10030     stop_left = TRUE;
10031   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10032     stop_right = TRUE;
10033
10034   if (((stop_top && stop_bottom) || stop_horizontal) &&
10035       ((stop_left && stop_right) || stop_vertical))
10036     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10037
10038   if (new_wall)
10039     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10040 }
10041
10042 static void CheckForDragon(int x, int y)
10043 {
10044   int i, j;
10045   boolean dragon_found = FALSE;
10046   struct XY *xy = xy_topdown;
10047
10048   for (i = 0; i < NUM_DIRECTIONS; i++)
10049   {
10050     for (j = 0; j < 4; j++)
10051     {
10052       int xx = x + j * xy[i].x;
10053       int yy = y + j * xy[i].y;
10054
10055       if (IN_LEV_FIELD(xx, yy) &&
10056           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10057       {
10058         if (Tile[xx][yy] == EL_DRAGON)
10059           dragon_found = TRUE;
10060       }
10061       else
10062         break;
10063     }
10064   }
10065
10066   if (!dragon_found)
10067   {
10068     for (i = 0; i < NUM_DIRECTIONS; i++)
10069     {
10070       for (j = 0; j < 3; j++)
10071       {
10072         int xx = x + j * xy[i].x;
10073         int yy = y + j * xy[i].y;
10074
10075         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10076         {
10077           Tile[xx][yy] = EL_EMPTY;
10078           TEST_DrawLevelField(xx, yy);
10079         }
10080         else
10081           break;
10082       }
10083     }
10084   }
10085 }
10086
10087 static void InitBuggyBase(int x, int y)
10088 {
10089   int element = Tile[x][y];
10090   int activating_delay = FRAMES_PER_SECOND / 4;
10091
10092   ChangeDelay[x][y] =
10093     (element == EL_SP_BUGGY_BASE ?
10094      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10095      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10096      activating_delay :
10097      element == EL_SP_BUGGY_BASE_ACTIVE ?
10098      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10099 }
10100
10101 static void WarnBuggyBase(int x, int y)
10102 {
10103   int i;
10104   struct XY *xy = xy_topdown;
10105
10106   for (i = 0; i < NUM_DIRECTIONS; i++)
10107   {
10108     int xx = x + xy[i].x;
10109     int yy = y + xy[i].y;
10110
10111     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10112     {
10113       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10114
10115       break;
10116     }
10117   }
10118 }
10119
10120 static void InitTrap(int x, int y)
10121 {
10122   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10123 }
10124
10125 static void ActivateTrap(int x, int y)
10126 {
10127   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10128 }
10129
10130 static void ChangeActiveTrap(int x, int y)
10131 {
10132   int graphic = IMG_TRAP_ACTIVE;
10133
10134   // if new animation frame was drawn, correct crumbled sand border
10135   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10136     TEST_DrawLevelFieldCrumbled(x, y);
10137 }
10138
10139 static int getSpecialActionElement(int element, int number, int base_element)
10140 {
10141   return (element != EL_EMPTY ? element :
10142           number != -1 ? base_element + number - 1 :
10143           EL_EMPTY);
10144 }
10145
10146 static int getModifiedActionNumber(int value_old, int operator, int operand,
10147                                    int value_min, int value_max)
10148 {
10149   int value_new = (operator == CA_MODE_SET      ? operand :
10150                    operator == CA_MODE_ADD      ? value_old + operand :
10151                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10152                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10153                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10154                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10155                    value_old);
10156
10157   return (value_new < value_min ? value_min :
10158           value_new > value_max ? value_max :
10159           value_new);
10160 }
10161
10162 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10163 {
10164   struct ElementInfo *ei = &element_info[element];
10165   struct ElementChangeInfo *change = &ei->change_page[page];
10166   int target_element = change->target_element;
10167   int action_type = change->action_type;
10168   int action_mode = change->action_mode;
10169   int action_arg = change->action_arg;
10170   int action_element = change->action_element;
10171   int i;
10172
10173   if (!change->has_action)
10174     return;
10175
10176   // ---------- determine action paramater values -----------------------------
10177
10178   int level_time_value =
10179     (level.time > 0 ? TimeLeft :
10180      TimePlayed);
10181
10182   int action_arg_element_raw =
10183     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10184      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10185      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10186      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10187      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10188      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10189      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10190      EL_EMPTY);
10191   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10192
10193   int action_arg_direction =
10194     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10195      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10196      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10197      change->actual_trigger_side :
10198      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10199      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10200      MV_NONE);
10201
10202   int action_arg_number_min =
10203     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10204      CA_ARG_MIN);
10205
10206   int action_arg_number_max =
10207     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10208      action_type == CA_SET_LEVEL_GEMS ? 999 :
10209      action_type == CA_SET_LEVEL_TIME ? 9999 :
10210      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10211      action_type == CA_SET_CE_VALUE ? 9999 :
10212      action_type == CA_SET_CE_SCORE ? 9999 :
10213      CA_ARG_MAX);
10214
10215   int action_arg_number_reset =
10216     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10217      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10218      action_type == CA_SET_LEVEL_TIME ? level.time :
10219      action_type == CA_SET_LEVEL_SCORE ? 0 :
10220      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10221      action_type == CA_SET_CE_SCORE ? 0 :
10222      0);
10223
10224   int action_arg_number =
10225     (action_arg <= CA_ARG_MAX ? action_arg :
10226      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10227      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10228      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10229      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10230      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10231      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10232      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10233      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10234      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10235      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10236      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10237      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10238      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10239      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10240      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10241      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10242      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10243      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10244      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10245      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10246      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10247      -1);
10248
10249   int action_arg_number_old =
10250     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10251      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10252      action_type == CA_SET_LEVEL_SCORE ? game.score :
10253      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10254      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10255      0);
10256
10257   int action_arg_number_new =
10258     getModifiedActionNumber(action_arg_number_old,
10259                             action_mode, action_arg_number,
10260                             action_arg_number_min, action_arg_number_max);
10261
10262   int trigger_player_bits =
10263     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10264      change->actual_trigger_player_bits : change->trigger_player);
10265
10266   int action_arg_player_bits =
10267     (action_arg >= CA_ARG_PLAYER_1 &&
10268      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10269      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10270      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10271      PLAYER_BITS_ANY);
10272
10273   // ---------- execute action  -----------------------------------------------
10274
10275   switch (action_type)
10276   {
10277     case CA_NO_ACTION:
10278     {
10279       return;
10280     }
10281
10282     // ---------- level actions  ----------------------------------------------
10283
10284     case CA_RESTART_LEVEL:
10285     {
10286       game.restart_level = TRUE;
10287
10288       break;
10289     }
10290
10291     case CA_SHOW_ENVELOPE:
10292     {
10293       int element = getSpecialActionElement(action_arg_element,
10294                                             action_arg_number, EL_ENVELOPE_1);
10295
10296       if (IS_ENVELOPE(element))
10297         local_player->show_envelope = element;
10298
10299       break;
10300     }
10301
10302     case CA_SET_LEVEL_TIME:
10303     {
10304       if (level.time > 0)       // only modify limited time value
10305       {
10306         TimeLeft = action_arg_number_new;
10307
10308         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10309
10310         DisplayGameControlValues();
10311
10312         if (!TimeLeft && game.time_limit)
10313           for (i = 0; i < MAX_PLAYERS; i++)
10314             KillPlayer(&stored_player[i]);
10315       }
10316
10317       break;
10318     }
10319
10320     case CA_SET_LEVEL_SCORE:
10321     {
10322       game.score = action_arg_number_new;
10323
10324       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10325
10326       DisplayGameControlValues();
10327
10328       break;
10329     }
10330
10331     case CA_SET_LEVEL_GEMS:
10332     {
10333       game.gems_still_needed = action_arg_number_new;
10334
10335       game.snapshot.collected_item = TRUE;
10336
10337       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10338
10339       DisplayGameControlValues();
10340
10341       break;
10342     }
10343
10344     case CA_SET_LEVEL_WIND:
10345     {
10346       game.wind_direction = action_arg_direction;
10347
10348       break;
10349     }
10350
10351     case CA_SET_LEVEL_RANDOM_SEED:
10352     {
10353       // ensure that setting a new random seed while playing is predictable
10354       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10355
10356       break;
10357     }
10358
10359     // ---------- player actions  ---------------------------------------------
10360
10361     case CA_MOVE_PLAYER:
10362     case CA_MOVE_PLAYER_NEW:
10363     {
10364       // automatically move to the next field in specified direction
10365       for (i = 0; i < MAX_PLAYERS; i++)
10366         if (trigger_player_bits & (1 << i))
10367           if (action_type == CA_MOVE_PLAYER ||
10368               stored_player[i].MovPos == 0)
10369             stored_player[i].programmed_action = action_arg_direction;
10370
10371       break;
10372     }
10373
10374     case CA_EXIT_PLAYER:
10375     {
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377         if (action_arg_player_bits & (1 << i))
10378           ExitPlayer(&stored_player[i]);
10379
10380       if (game.players_still_needed == 0)
10381         LevelSolved();
10382
10383       break;
10384     }
10385
10386     case CA_KILL_PLAYER:
10387     {
10388       for (i = 0; i < MAX_PLAYERS; i++)
10389         if (action_arg_player_bits & (1 << i))
10390           KillPlayer(&stored_player[i]);
10391
10392       break;
10393     }
10394
10395     case CA_SET_PLAYER_KEYS:
10396     {
10397       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10398       int element = getSpecialActionElement(action_arg_element,
10399                                             action_arg_number, EL_KEY_1);
10400
10401       if (IS_KEY(element))
10402       {
10403         for (i = 0; i < MAX_PLAYERS; i++)
10404         {
10405           if (trigger_player_bits & (1 << i))
10406           {
10407             stored_player[i].key[KEY_NR(element)] = key_state;
10408
10409             DrawGameDoorValues();
10410           }
10411         }
10412       }
10413
10414       break;
10415     }
10416
10417     case CA_SET_PLAYER_SPEED:
10418     {
10419       for (i = 0; i < MAX_PLAYERS; i++)
10420       {
10421         if (trigger_player_bits & (1 << i))
10422         {
10423           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10424
10425           if (action_arg == CA_ARG_SPEED_FASTER &&
10426               stored_player[i].cannot_move)
10427           {
10428             action_arg_number = STEPSIZE_VERY_SLOW;
10429           }
10430           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10431                    action_arg == CA_ARG_SPEED_FASTER)
10432           {
10433             action_arg_number = 2;
10434             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10435                            CA_MODE_MULTIPLY);
10436           }
10437           else if (action_arg == CA_ARG_NUMBER_RESET)
10438           {
10439             action_arg_number = level.initial_player_stepsize[i];
10440           }
10441
10442           move_stepsize =
10443             getModifiedActionNumber(move_stepsize,
10444                                     action_mode,
10445                                     action_arg_number,
10446                                     action_arg_number_min,
10447                                     action_arg_number_max);
10448
10449           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10450         }
10451       }
10452
10453       break;
10454     }
10455
10456     case CA_SET_PLAYER_SHIELD:
10457     {
10458       for (i = 0; i < MAX_PLAYERS; i++)
10459       {
10460         if (trigger_player_bits & (1 << i))
10461         {
10462           if (action_arg == CA_ARG_SHIELD_OFF)
10463           {
10464             stored_player[i].shield_normal_time_left = 0;
10465             stored_player[i].shield_deadly_time_left = 0;
10466           }
10467           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10468           {
10469             stored_player[i].shield_normal_time_left = 999999;
10470           }
10471           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10472           {
10473             stored_player[i].shield_normal_time_left = 999999;
10474             stored_player[i].shield_deadly_time_left = 999999;
10475           }
10476         }
10477       }
10478
10479       break;
10480     }
10481
10482     case CA_SET_PLAYER_GRAVITY:
10483     {
10484       for (i = 0; i < MAX_PLAYERS; i++)
10485       {
10486         if (trigger_player_bits & (1 << i))
10487         {
10488           stored_player[i].gravity =
10489             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10490              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10491              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10492              stored_player[i].gravity);
10493         }
10494       }
10495
10496       break;
10497     }
10498
10499     case CA_SET_PLAYER_ARTWORK:
10500     {
10501       for (i = 0; i < MAX_PLAYERS; i++)
10502       {
10503         if (trigger_player_bits & (1 << i))
10504         {
10505           int artwork_element = action_arg_element;
10506
10507           if (action_arg == CA_ARG_ELEMENT_RESET)
10508             artwork_element =
10509               (level.use_artwork_element[i] ? level.artwork_element[i] :
10510                stored_player[i].element_nr);
10511
10512           if (stored_player[i].artwork_element != artwork_element)
10513             stored_player[i].Frame = 0;
10514
10515           stored_player[i].artwork_element = artwork_element;
10516
10517           SetPlayerWaiting(&stored_player[i], FALSE);
10518
10519           // set number of special actions for bored and sleeping animation
10520           stored_player[i].num_special_action_bored =
10521             get_num_special_action(artwork_element,
10522                                    ACTION_BORING_1, ACTION_BORING_LAST);
10523           stored_player[i].num_special_action_sleeping =
10524             get_num_special_action(artwork_element,
10525                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10526         }
10527       }
10528
10529       break;
10530     }
10531
10532     case CA_SET_PLAYER_INVENTORY:
10533     {
10534       for (i = 0; i < MAX_PLAYERS; i++)
10535       {
10536         struct PlayerInfo *player = &stored_player[i];
10537         int j, k;
10538
10539         if (trigger_player_bits & (1 << i))
10540         {
10541           int inventory_element = action_arg_element;
10542
10543           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10544               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10545               action_arg == CA_ARG_ELEMENT_ACTION)
10546           {
10547             int element = inventory_element;
10548             int collect_count = element_info[element].collect_count_initial;
10549
10550             if (!IS_CUSTOM_ELEMENT(element))
10551               collect_count = 1;
10552
10553             if (collect_count == 0)
10554               player->inventory_infinite_element = element;
10555             else
10556               for (k = 0; k < collect_count; k++)
10557                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10558                   player->inventory_element[player->inventory_size++] =
10559                     element;
10560           }
10561           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10562                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10563                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10564           {
10565             if (player->inventory_infinite_element != EL_UNDEFINED &&
10566                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10567                                      action_arg_element_raw))
10568               player->inventory_infinite_element = EL_UNDEFINED;
10569
10570             for (k = 0, j = 0; j < player->inventory_size; j++)
10571             {
10572               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10573                                         action_arg_element_raw))
10574                 player->inventory_element[k++] = player->inventory_element[j];
10575             }
10576
10577             player->inventory_size = k;
10578           }
10579           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10580           {
10581             if (player->inventory_size > 0)
10582             {
10583               for (j = 0; j < player->inventory_size - 1; j++)
10584                 player->inventory_element[j] = player->inventory_element[j + 1];
10585
10586               player->inventory_size--;
10587             }
10588           }
10589           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10590           {
10591             if (player->inventory_size > 0)
10592               player->inventory_size--;
10593           }
10594           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10595           {
10596             player->inventory_infinite_element = EL_UNDEFINED;
10597             player->inventory_size = 0;
10598           }
10599           else if (action_arg == CA_ARG_INVENTORY_RESET)
10600           {
10601             player->inventory_infinite_element = EL_UNDEFINED;
10602             player->inventory_size = 0;
10603
10604             if (level.use_initial_inventory[i])
10605             {
10606               for (j = 0; j < level.initial_inventory_size[i]; j++)
10607               {
10608                 int element = level.initial_inventory_content[i][j];
10609                 int collect_count = element_info[element].collect_count_initial;
10610
10611                 if (!IS_CUSTOM_ELEMENT(element))
10612                   collect_count = 1;
10613
10614                 if (collect_count == 0)
10615                   player->inventory_infinite_element = element;
10616                 else
10617                   for (k = 0; k < collect_count; k++)
10618                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10619                       player->inventory_element[player->inventory_size++] =
10620                         element;
10621               }
10622             }
10623           }
10624         }
10625       }
10626
10627       break;
10628     }
10629
10630     // ---------- CE actions  -------------------------------------------------
10631
10632     case CA_SET_CE_VALUE:
10633     {
10634       int last_ce_value = CustomValue[x][y];
10635
10636       CustomValue[x][y] = action_arg_number_new;
10637
10638       if (CustomValue[x][y] != last_ce_value)
10639       {
10640         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10641         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10642
10643         if (CustomValue[x][y] == 0)
10644         {
10645           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10646           ChangeCount[x][y] = 0;        // allow at least one more change
10647
10648           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10649           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10650         }
10651       }
10652
10653       break;
10654     }
10655
10656     case CA_SET_CE_SCORE:
10657     {
10658       int last_ce_score = ei->collect_score;
10659
10660       ei->collect_score = action_arg_number_new;
10661
10662       if (ei->collect_score != last_ce_score)
10663       {
10664         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10665         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10666
10667         if (ei->collect_score == 0)
10668         {
10669           int xx, yy;
10670
10671           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10672           ChangeCount[x][y] = 0;        // allow at least one more change
10673
10674           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10675           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10676
10677           /*
10678             This is a very special case that seems to be a mixture between
10679             CheckElementChange() and CheckTriggeredElementChange(): while
10680             the first one only affects single elements that are triggered
10681             directly, the second one affects multiple elements in the playfield
10682             that are triggered indirectly by another element. This is a third
10683             case: Changing the CE score always affects multiple identical CEs,
10684             so every affected CE must be checked, not only the single CE for
10685             which the CE score was changed in the first place (as every instance
10686             of that CE shares the same CE score, and therefore also can change)!
10687           */
10688           SCAN_PLAYFIELD(xx, yy)
10689           {
10690             if (Tile[xx][yy] == element)
10691               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10692                                  CE_SCORE_GETS_ZERO);
10693           }
10694         }
10695       }
10696
10697       break;
10698     }
10699
10700     case CA_SET_CE_ARTWORK:
10701     {
10702       int artwork_element = action_arg_element;
10703       boolean reset_frame = FALSE;
10704       int xx, yy;
10705
10706       if (action_arg == CA_ARG_ELEMENT_RESET)
10707         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10708                            element);
10709
10710       if (ei->gfx_element != artwork_element)
10711         reset_frame = TRUE;
10712
10713       ei->gfx_element = artwork_element;
10714
10715       SCAN_PLAYFIELD(xx, yy)
10716       {
10717         if (Tile[xx][yy] == element)
10718         {
10719           if (reset_frame)
10720           {
10721             ResetGfxAnimation(xx, yy);
10722             ResetRandomAnimationValue(xx, yy);
10723           }
10724
10725           TEST_DrawLevelField(xx, yy);
10726         }
10727       }
10728
10729       break;
10730     }
10731
10732     // ---------- engine actions  ---------------------------------------------
10733
10734     case CA_SET_ENGINE_SCAN_MODE:
10735     {
10736       InitPlayfieldScanMode(action_arg);
10737
10738       break;
10739     }
10740
10741     default:
10742       break;
10743   }
10744 }
10745
10746 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10747 {
10748   int old_element = Tile[x][y];
10749   int new_element = GetElementFromGroupElement(element);
10750   int previous_move_direction = MovDir[x][y];
10751   int last_ce_value = CustomValue[x][y];
10752   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10753   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10754   boolean add_player_onto_element = (new_element_is_player &&
10755                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10756                                      IS_WALKABLE(old_element));
10757
10758   if (!add_player_onto_element)
10759   {
10760     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10761       RemoveMovingField(x, y);
10762     else
10763       RemoveField(x, y);
10764
10765     Tile[x][y] = new_element;
10766
10767     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10768       MovDir[x][y] = previous_move_direction;
10769
10770     if (element_info[new_element].use_last_ce_value)
10771       CustomValue[x][y] = last_ce_value;
10772
10773     InitField_WithBug1(x, y, FALSE);
10774
10775     new_element = Tile[x][y];   // element may have changed
10776
10777     ResetGfxAnimation(x, y);
10778     ResetRandomAnimationValue(x, y);
10779
10780     TEST_DrawLevelField(x, y);
10781
10782     if (GFX_CRUMBLED(new_element))
10783       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10784
10785     if (old_element == EL_EXPLOSION)
10786     {
10787       Store[x][y] = Store2[x][y] = 0;
10788
10789       // check if new element replaces an exploding player, requiring cleanup
10790       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10791         StorePlayer[x][y] = 0;
10792     }
10793
10794     // check if element under the player changes from accessible to unaccessible
10795     // (needed for special case of dropping element which then changes)
10796     // (must be checked after creating new element for walkable group elements)
10797     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10798         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10799     {
10800       KillPlayer(PLAYERINFO(x, y));
10801
10802       return;
10803     }
10804   }
10805
10806   // "ChangeCount" not set yet to allow "entered by player" change one time
10807   if (new_element_is_player)
10808     RelocatePlayer(x, y, new_element);
10809
10810   if (is_change)
10811     ChangeCount[x][y]++;        // count number of changes in the same frame
10812
10813   TestIfBadThingTouchesPlayer(x, y);
10814   TestIfPlayerTouchesCustomElement(x, y);
10815   TestIfElementTouchesCustomElement(x, y);
10816 }
10817
10818 static void CreateField(int x, int y, int element)
10819 {
10820   CreateFieldExt(x, y, element, FALSE);
10821 }
10822
10823 static void CreateElementFromChange(int x, int y, int element)
10824 {
10825   element = GET_VALID_RUNTIME_ELEMENT(element);
10826
10827   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10828   {
10829     int old_element = Tile[x][y];
10830
10831     // prevent changed element from moving in same engine frame
10832     // unless both old and new element can either fall or move
10833     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10834         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10835       Stop[x][y] = TRUE;
10836   }
10837
10838   CreateFieldExt(x, y, element, TRUE);
10839 }
10840
10841 static boolean ChangeElement(int x, int y, int element, int page)
10842 {
10843   struct ElementInfo *ei = &element_info[element];
10844   struct ElementChangeInfo *change = &ei->change_page[page];
10845   int ce_value = CustomValue[x][y];
10846   int ce_score = ei->collect_score;
10847   int target_element;
10848   int old_element = Tile[x][y];
10849
10850   // always use default change event to prevent running into a loop
10851   if (ChangeEvent[x][y] == -1)
10852     ChangeEvent[x][y] = CE_DELAY;
10853
10854   if (ChangeEvent[x][y] == CE_DELAY)
10855   {
10856     // reset actual trigger element, trigger player and action element
10857     change->actual_trigger_element = EL_EMPTY;
10858     change->actual_trigger_player = EL_EMPTY;
10859     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10860     change->actual_trigger_side = CH_SIDE_NONE;
10861     change->actual_trigger_ce_value = 0;
10862     change->actual_trigger_ce_score = 0;
10863     change->actual_trigger_x = -1;
10864     change->actual_trigger_y = -1;
10865   }
10866
10867   // do not change elements more than a specified maximum number of changes
10868   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10869     return FALSE;
10870
10871   ChangeCount[x][y]++;          // count number of changes in the same frame
10872
10873   if (ei->has_anim_event)
10874     HandleGlobalAnimEventByElementChange(element, page, x, y,
10875                                          change->actual_trigger_x,
10876                                          change->actual_trigger_y);
10877
10878   if (change->explode)
10879   {
10880     Bang(x, y);
10881
10882     return TRUE;
10883   }
10884
10885   if (change->use_target_content)
10886   {
10887     boolean complete_replace = TRUE;
10888     boolean can_replace[3][3];
10889     int xx, yy;
10890
10891     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10892     {
10893       boolean is_empty;
10894       boolean is_walkable;
10895       boolean is_diggable;
10896       boolean is_collectible;
10897       boolean is_removable;
10898       boolean is_destructible;
10899       int ex = x + xx - 1;
10900       int ey = y + yy - 1;
10901       int content_element = change->target_content.e[xx][yy];
10902       int e;
10903
10904       can_replace[xx][yy] = TRUE;
10905
10906       if (ex == x && ey == y)   // do not check changing element itself
10907         continue;
10908
10909       if (content_element == EL_EMPTY_SPACE)
10910       {
10911         can_replace[xx][yy] = FALSE;    // do not replace border with space
10912
10913         continue;
10914       }
10915
10916       if (!IN_LEV_FIELD(ex, ey))
10917       {
10918         can_replace[xx][yy] = FALSE;
10919         complete_replace = FALSE;
10920
10921         continue;
10922       }
10923
10924       e = Tile[ex][ey];
10925
10926       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10927         e = MovingOrBlocked2Element(ex, ey);
10928
10929       is_empty = (IS_FREE(ex, ey) ||
10930                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10931
10932       is_walkable     = (is_empty || IS_WALKABLE(e));
10933       is_diggable     = (is_empty || IS_DIGGABLE(e));
10934       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10935       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10936       is_removable    = (is_diggable || is_collectible);
10937
10938       can_replace[xx][yy] =
10939         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10940           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10941           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10942           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10943           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10944           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10945          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10946
10947       if (!can_replace[xx][yy])
10948         complete_replace = FALSE;
10949     }
10950
10951     if (!change->only_if_complete || complete_replace)
10952     {
10953       boolean something_has_changed = FALSE;
10954
10955       if (change->only_if_complete && change->use_random_replace &&
10956           RND(100) < change->random_percentage)
10957         return FALSE;
10958
10959       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10960       {
10961         int ex = x + xx - 1;
10962         int ey = y + yy - 1;
10963         int content_element;
10964
10965         if (can_replace[xx][yy] && (!change->use_random_replace ||
10966                                     RND(100) < change->random_percentage))
10967         {
10968           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10969             RemoveMovingField(ex, ey);
10970
10971           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10972
10973           content_element = change->target_content.e[xx][yy];
10974           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10975                                               ce_value, ce_score);
10976
10977           CreateElementFromChange(ex, ey, target_element);
10978
10979           something_has_changed = TRUE;
10980
10981           // for symmetry reasons, freeze newly created border elements
10982           if (ex != x || ey != y)
10983             Stop[ex][ey] = TRUE;        // no more moving in this frame
10984         }
10985       }
10986
10987       if (something_has_changed)
10988       {
10989         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10990         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10991       }
10992     }
10993   }
10994   else
10995   {
10996     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10997                                         ce_value, ce_score);
10998
10999     if (element == EL_DIAGONAL_GROWING ||
11000         element == EL_DIAGONAL_SHRINKING)
11001     {
11002       target_element = Store[x][y];
11003
11004       Store[x][y] = EL_EMPTY;
11005     }
11006
11007     // special case: element changes to player (and may be kept if walkable)
11008     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
11009       CreateElementFromChange(x, y, EL_EMPTY);
11010
11011     CreateElementFromChange(x, y, target_element);
11012
11013     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11014     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11015   }
11016
11017   // this uses direct change before indirect change
11018   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11019
11020   return TRUE;
11021 }
11022
11023 static void HandleElementChange(int x, int y, int page)
11024 {
11025   int element = MovingOrBlocked2Element(x, y);
11026   struct ElementInfo *ei = &element_info[element];
11027   struct ElementChangeInfo *change = &ei->change_page[page];
11028   boolean handle_action_before_change = FALSE;
11029
11030 #ifdef DEBUG
11031   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11032       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11033   {
11034     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11035           x, y, element, element_info[element].token_name);
11036     Debug("game:playing:HandleElementChange", "This should never happen!");
11037   }
11038 #endif
11039
11040   // this can happen with classic bombs on walkable, changing elements
11041   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11042   {
11043     return;
11044   }
11045
11046   if (ChangeDelay[x][y] == 0)           // initialize element change
11047   {
11048     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11049
11050     if (change->can_change)
11051     {
11052       // !!! not clear why graphic animation should be reset at all here !!!
11053       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11054       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11055
11056       /*
11057         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11058
11059         When using an animation frame delay of 1 (this only happens with
11060         "sp_zonk.moving.left/right" in the classic graphics), the default
11061         (non-moving) animation shows wrong animation frames (while the
11062         moving animation, like "sp_zonk.moving.left/right", is correct,
11063         so this graphical bug never shows up with the classic graphics).
11064         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11065         be drawn instead of the correct frames 0,1,2,3. This is caused by
11066         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11067         an element change: First when the change delay ("ChangeDelay[][]")
11068         counter has reached zero after decrementing, then a second time in
11069         the next frame (after "GfxFrame[][]" was already incremented) when
11070         "ChangeDelay[][]" is reset to the initial delay value again.
11071
11072         This causes frame 0 to be drawn twice, while the last frame won't
11073         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11074
11075         As some animations may already be cleverly designed around this bug
11076         (at least the "Snake Bite" snake tail animation does this), it cannot
11077         simply be fixed here without breaking such existing animations.
11078         Unfortunately, it cannot easily be detected if a graphics set was
11079         designed "before" or "after" the bug was fixed. As a workaround,
11080         a new graphics set option "game.graphics_engine_version" was added
11081         to be able to specify the game's major release version for which the
11082         graphics set was designed, which can then be used to decide if the
11083         bugfix should be used (version 4 and above) or not (version 3 or
11084         below, or if no version was specified at all, as with old sets).
11085
11086         (The wrong/fixed animation frames can be tested with the test level set
11087         "test_gfxframe" and level "000", which contains a specially prepared
11088         custom element at level position (x/y) == (11/9) which uses the zonk
11089         animation mentioned above. Using "game.graphics_engine_version: 4"
11090         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11091         This can also be seen from the debug output for this test element.)
11092       */
11093
11094       // when a custom element is about to change (for example by change delay),
11095       // do not reset graphic animation when the custom element is moving
11096       if (game.graphics_engine_version < 4 &&
11097           !IS_MOVING(x, y))
11098       {
11099         ResetGfxAnimation(x, y);
11100         ResetRandomAnimationValue(x, y);
11101       }
11102
11103       if (change->pre_change_function)
11104         change->pre_change_function(x, y);
11105     }
11106   }
11107
11108   ChangeDelay[x][y]--;
11109
11110   if (ChangeDelay[x][y] != 0)           // continue element change
11111   {
11112     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11113
11114     // also needed if CE can not change, but has CE delay with CE action
11115     if (IS_ANIMATED(graphic))
11116       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11117
11118     if (change->can_change)
11119     {
11120       if (change->change_function)
11121         change->change_function(x, y);
11122     }
11123   }
11124   else                                  // finish element change
11125   {
11126     if (ChangePage[x][y] != -1)         // remember page from delayed change
11127     {
11128       page = ChangePage[x][y];
11129       ChangePage[x][y] = -1;
11130
11131       change = &ei->change_page[page];
11132     }
11133
11134     if (IS_MOVING(x, y))                // never change a running system ;-)
11135     {
11136       ChangeDelay[x][y] = 1;            // try change after next move step
11137       ChangePage[x][y] = page;          // remember page to use for change
11138
11139       return;
11140     }
11141
11142     // special case: set new level random seed before changing element
11143     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11144       handle_action_before_change = TRUE;
11145
11146     if (change->has_action && handle_action_before_change)
11147       ExecuteCustomElementAction(x, y, element, page);
11148
11149     if (change->can_change)
11150     {
11151       if (ChangeElement(x, y, element, page))
11152       {
11153         if (change->post_change_function)
11154           change->post_change_function(x, y);
11155       }
11156     }
11157
11158     if (change->has_action && !handle_action_before_change)
11159       ExecuteCustomElementAction(x, y, element, page);
11160   }
11161 }
11162
11163 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11164                                               int trigger_element,
11165                                               int trigger_event,
11166                                               int trigger_player,
11167                                               int trigger_side,
11168                                               int trigger_page)
11169 {
11170   boolean change_done_any = FALSE;
11171   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11172   int i;
11173
11174   if (!(trigger_events[trigger_element][trigger_event]))
11175     return FALSE;
11176
11177   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11178
11179   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11180   {
11181     int element = EL_CUSTOM_START + i;
11182     boolean change_done = FALSE;
11183     int p;
11184
11185     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11186         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11187       continue;
11188
11189     for (p = 0; p < element_info[element].num_change_pages; p++)
11190     {
11191       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11192
11193       if (change->can_change_or_has_action &&
11194           change->has_event[trigger_event] &&
11195           change->trigger_side & trigger_side &&
11196           change->trigger_player & trigger_player &&
11197           change->trigger_page & trigger_page_bits &&
11198           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11199       {
11200         change->actual_trigger_element = trigger_element;
11201         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11202         change->actual_trigger_player_bits = trigger_player;
11203         change->actual_trigger_side = trigger_side;
11204         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11205         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11206         change->actual_trigger_x = trigger_x;
11207         change->actual_trigger_y = trigger_y;
11208
11209         if ((change->can_change && !change_done) || change->has_action)
11210         {
11211           int x, y;
11212
11213           SCAN_PLAYFIELD(x, y)
11214           {
11215             if (Tile[x][y] == element)
11216             {
11217               if (change->can_change && !change_done)
11218               {
11219                 // if element already changed in this frame, not only prevent
11220                 // another element change (checked in ChangeElement()), but
11221                 // also prevent additional element actions for this element
11222
11223                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11224                     !level.use_action_after_change_bug)
11225                   continue;
11226
11227                 ChangeDelay[x][y] = 1;
11228                 ChangeEvent[x][y] = trigger_event;
11229
11230                 HandleElementChange(x, y, p);
11231               }
11232               else if (change->has_action)
11233               {
11234                 // if element already changed in this frame, not only prevent
11235                 // another element change (checked in ChangeElement()), but
11236                 // also prevent additional element actions for this element
11237
11238                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11239                     !level.use_action_after_change_bug)
11240                   continue;
11241
11242                 ExecuteCustomElementAction(x, y, element, p);
11243                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11244               }
11245             }
11246           }
11247
11248           if (change->can_change)
11249           {
11250             change_done = TRUE;
11251             change_done_any = TRUE;
11252           }
11253         }
11254       }
11255     }
11256   }
11257
11258   RECURSION_LOOP_DETECTION_END();
11259
11260   return change_done_any;
11261 }
11262
11263 static boolean CheckElementChangeExt(int x, int y,
11264                                      int element,
11265                                      int trigger_element,
11266                                      int trigger_event,
11267                                      int trigger_player,
11268                                      int trigger_side)
11269 {
11270   boolean change_done = FALSE;
11271   int p;
11272
11273   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11274       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11275     return FALSE;
11276
11277   if (Tile[x][y] == EL_BLOCKED)
11278   {
11279     Blocked2Moving(x, y, &x, &y);
11280     element = Tile[x][y];
11281   }
11282
11283   // check if element has already changed or is about to change after moving
11284   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11285        Tile[x][y] != element) ||
11286
11287       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11288        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11289         ChangePage[x][y] != -1)))
11290     return FALSE;
11291
11292   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11293
11294   for (p = 0; p < element_info[element].num_change_pages; p++)
11295   {
11296     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11297
11298     /* check trigger element for all events where the element that is checked
11299        for changing interacts with a directly adjacent element -- this is
11300        different to element changes that affect other elements to change on the
11301        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11302     boolean check_trigger_element =
11303       (trigger_event == CE_NEXT_TO_X ||
11304        trigger_event == CE_TOUCHING_X ||
11305        trigger_event == CE_HITTING_X ||
11306        trigger_event == CE_HIT_BY_X ||
11307        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11308
11309     if (change->can_change_or_has_action &&
11310         change->has_event[trigger_event] &&
11311         change->trigger_side & trigger_side &&
11312         change->trigger_player & trigger_player &&
11313         (!check_trigger_element ||
11314          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11315     {
11316       change->actual_trigger_element = trigger_element;
11317       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11318       change->actual_trigger_player_bits = trigger_player;
11319       change->actual_trigger_side = trigger_side;
11320       change->actual_trigger_ce_value = CustomValue[x][y];
11321       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11322       change->actual_trigger_x = x;
11323       change->actual_trigger_y = y;
11324
11325       // special case: trigger element not at (x,y) position for some events
11326       if (check_trigger_element)
11327       {
11328         static struct
11329         {
11330           int dx, dy;
11331         } move_xy[] =
11332           {
11333             {  0,  0 },
11334             { -1,  0 },
11335             { +1,  0 },
11336             {  0,  0 },
11337             {  0, -1 },
11338             {  0,  0 }, { 0, 0 }, { 0, 0 },
11339             {  0, +1 }
11340           };
11341
11342         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11343         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11344
11345         change->actual_trigger_ce_value = CustomValue[xx][yy];
11346         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11347         change->actual_trigger_x = xx;
11348         change->actual_trigger_y = yy;
11349       }
11350
11351       if (change->can_change && !change_done)
11352       {
11353         ChangeDelay[x][y] = 1;
11354         ChangeEvent[x][y] = trigger_event;
11355
11356         HandleElementChange(x, y, p);
11357
11358         change_done = TRUE;
11359       }
11360       else if (change->has_action)
11361       {
11362         ExecuteCustomElementAction(x, y, element, p);
11363         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11364       }
11365     }
11366   }
11367
11368   RECURSION_LOOP_DETECTION_END();
11369
11370   return change_done;
11371 }
11372
11373 static void PlayPlayerSound(struct PlayerInfo *player)
11374 {
11375   int jx = player->jx, jy = player->jy;
11376   int sound_element = player->artwork_element;
11377   int last_action = player->last_action_waiting;
11378   int action = player->action_waiting;
11379
11380   if (player->is_waiting)
11381   {
11382     if (action != last_action)
11383       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11384     else
11385       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11386   }
11387   else
11388   {
11389     if (action != last_action)
11390       StopSound(element_info[sound_element].sound[last_action]);
11391
11392     if (last_action == ACTION_SLEEPING)
11393       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11394   }
11395 }
11396
11397 static void PlayAllPlayersSound(void)
11398 {
11399   int i;
11400
11401   for (i = 0; i < MAX_PLAYERS; i++)
11402     if (stored_player[i].active)
11403       PlayPlayerSound(&stored_player[i]);
11404 }
11405
11406 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11407 {
11408   boolean last_waiting = player->is_waiting;
11409   int move_dir = player->MovDir;
11410
11411   player->dir_waiting = move_dir;
11412   player->last_action_waiting = player->action_waiting;
11413
11414   if (is_waiting)
11415   {
11416     if (!last_waiting)          // not waiting -> waiting
11417     {
11418       player->is_waiting = TRUE;
11419
11420       player->frame_counter_bored =
11421         FrameCounter +
11422         game.player_boring_delay_fixed +
11423         GetSimpleRandom(game.player_boring_delay_random);
11424       player->frame_counter_sleeping =
11425         FrameCounter +
11426         game.player_sleeping_delay_fixed +
11427         GetSimpleRandom(game.player_sleeping_delay_random);
11428
11429       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11430     }
11431
11432     if (game.player_sleeping_delay_fixed +
11433         game.player_sleeping_delay_random > 0 &&
11434         player->anim_delay_counter == 0 &&
11435         player->post_delay_counter == 0 &&
11436         FrameCounter >= player->frame_counter_sleeping)
11437       player->is_sleeping = TRUE;
11438     else if (game.player_boring_delay_fixed +
11439              game.player_boring_delay_random > 0 &&
11440              FrameCounter >= player->frame_counter_bored)
11441       player->is_bored = TRUE;
11442
11443     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11444                               player->is_bored ? ACTION_BORING :
11445                               ACTION_WAITING);
11446
11447     if (player->is_sleeping && player->use_murphy)
11448     {
11449       // special case for sleeping Murphy when leaning against non-free tile
11450
11451       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11452           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11453            !IS_MOVING(player->jx - 1, player->jy)))
11454         move_dir = MV_LEFT;
11455       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11456                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11457                 !IS_MOVING(player->jx + 1, player->jy)))
11458         move_dir = MV_RIGHT;
11459       else
11460         player->is_sleeping = FALSE;
11461
11462       player->dir_waiting = move_dir;
11463     }
11464
11465     if (player->is_sleeping)
11466     {
11467       if (player->num_special_action_sleeping > 0)
11468       {
11469         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11470         {
11471           int last_special_action = player->special_action_sleeping;
11472           int num_special_action = player->num_special_action_sleeping;
11473           int special_action =
11474             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11475              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11476              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11477              last_special_action + 1 : ACTION_SLEEPING);
11478           int special_graphic =
11479             el_act_dir2img(player->artwork_element, special_action, move_dir);
11480
11481           player->anim_delay_counter =
11482             graphic_info[special_graphic].anim_delay_fixed +
11483             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11484           player->post_delay_counter =
11485             graphic_info[special_graphic].post_delay_fixed +
11486             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11487
11488           player->special_action_sleeping = special_action;
11489         }
11490
11491         if (player->anim_delay_counter > 0)
11492         {
11493           player->action_waiting = player->special_action_sleeping;
11494           player->anim_delay_counter--;
11495         }
11496         else if (player->post_delay_counter > 0)
11497         {
11498           player->post_delay_counter--;
11499         }
11500       }
11501     }
11502     else if (player->is_bored)
11503     {
11504       if (player->num_special_action_bored > 0)
11505       {
11506         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11507         {
11508           int special_action =
11509             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11510           int special_graphic =
11511             el_act_dir2img(player->artwork_element, special_action, move_dir);
11512
11513           player->anim_delay_counter =
11514             graphic_info[special_graphic].anim_delay_fixed +
11515             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11516           player->post_delay_counter =
11517             graphic_info[special_graphic].post_delay_fixed +
11518             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11519
11520           player->special_action_bored = special_action;
11521         }
11522
11523         if (player->anim_delay_counter > 0)
11524         {
11525           player->action_waiting = player->special_action_bored;
11526           player->anim_delay_counter--;
11527         }
11528         else if (player->post_delay_counter > 0)
11529         {
11530           player->post_delay_counter--;
11531         }
11532       }
11533     }
11534   }
11535   else if (last_waiting)        // waiting -> not waiting
11536   {
11537     player->is_waiting = FALSE;
11538     player->is_bored = FALSE;
11539     player->is_sleeping = FALSE;
11540
11541     player->frame_counter_bored = -1;
11542     player->frame_counter_sleeping = -1;
11543
11544     player->anim_delay_counter = 0;
11545     player->post_delay_counter = 0;
11546
11547     player->dir_waiting = player->MovDir;
11548     player->action_waiting = ACTION_DEFAULT;
11549
11550     player->special_action_bored = ACTION_DEFAULT;
11551     player->special_action_sleeping = ACTION_DEFAULT;
11552   }
11553 }
11554
11555 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11556 {
11557   if ((!player->is_moving  && player->was_moving) ||
11558       (player->MovPos == 0 && player->was_moving) ||
11559       (player->is_snapping && !player->was_snapping) ||
11560       (player->is_dropping && !player->was_dropping))
11561   {
11562     if (!CheckSaveEngineSnapshotToList())
11563       return;
11564
11565     player->was_moving = FALSE;
11566     player->was_snapping = TRUE;
11567     player->was_dropping = TRUE;
11568   }
11569   else
11570   {
11571     if (player->is_moving)
11572       player->was_moving = TRUE;
11573
11574     if (!player->is_snapping)
11575       player->was_snapping = FALSE;
11576
11577     if (!player->is_dropping)
11578       player->was_dropping = FALSE;
11579   }
11580
11581   static struct MouseActionInfo mouse_action_last = { 0 };
11582   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11583   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11584
11585   if (new_released)
11586     CheckSaveEngineSnapshotToList();
11587
11588   mouse_action_last = mouse_action;
11589 }
11590
11591 static void CheckSingleStepMode(struct PlayerInfo *player)
11592 {
11593   if (tape.single_step && tape.recording && !tape.pausing)
11594   {
11595     // as it is called "single step mode", just return to pause mode when the
11596     // player stopped moving after one tile (or never starts moving at all)
11597     // (reverse logic needed here in case single step mode used in team mode)
11598     if (player->is_moving ||
11599         player->is_pushing ||
11600         player->is_dropping_pressed ||
11601         player->effective_mouse_action.button)
11602       game.enter_single_step_mode = FALSE;
11603   }
11604
11605   CheckSaveEngineSnapshot(player);
11606 }
11607
11608 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11609 {
11610   int left      = player_action & JOY_LEFT;
11611   int right     = player_action & JOY_RIGHT;
11612   int up        = player_action & JOY_UP;
11613   int down      = player_action & JOY_DOWN;
11614   int button1   = player_action & JOY_BUTTON_1;
11615   int button2   = player_action & JOY_BUTTON_2;
11616   int dx        = (left ? -1 : right ? 1 : 0);
11617   int dy        = (up   ? -1 : down  ? 1 : 0);
11618
11619   if (!player->active || tape.pausing)
11620     return 0;
11621
11622   if (player_action)
11623   {
11624     if (button1)
11625       SnapField(player, dx, dy);
11626     else
11627     {
11628       if (button2)
11629         DropElement(player);
11630
11631       MovePlayer(player, dx, dy);
11632     }
11633
11634     CheckSingleStepMode(player);
11635
11636     SetPlayerWaiting(player, FALSE);
11637
11638     return player_action;
11639   }
11640   else
11641   {
11642     // no actions for this player (no input at player's configured device)
11643
11644     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11645     SnapField(player, 0, 0);
11646     CheckGravityMovementWhenNotMoving(player);
11647
11648     if (player->MovPos == 0)
11649       SetPlayerWaiting(player, TRUE);
11650
11651     if (player->MovPos == 0)    // needed for tape.playing
11652       player->is_moving = FALSE;
11653
11654     player->is_dropping = FALSE;
11655     player->is_dropping_pressed = FALSE;
11656     player->drop_pressed_delay = 0;
11657
11658     CheckSingleStepMode(player);
11659
11660     return 0;
11661   }
11662 }
11663
11664 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11665                                          byte *tape_action)
11666 {
11667   if (!tape.use_mouse_actions)
11668     return;
11669
11670   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11671   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11672   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11673 }
11674
11675 static void SetTapeActionFromMouseAction(byte *tape_action,
11676                                          struct MouseActionInfo *mouse_action)
11677 {
11678   if (!tape.use_mouse_actions)
11679     return;
11680
11681   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11682   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11683   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11684 }
11685
11686 static void CheckLevelSolved(void)
11687 {
11688   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11689   {
11690     if (game_bd.level_solved &&
11691         !game_bd.game_over)                             // game won
11692     {
11693       LevelSolved();
11694
11695       game_bd.game_over = TRUE;
11696
11697       game.all_players_gone = TRUE;
11698     }
11699
11700     if (game_bd.game_over)                              // game lost
11701       game.all_players_gone = TRUE;
11702   }
11703   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11704   {
11705     if (game_em.level_solved &&
11706         !game_em.game_over)                             // game won
11707     {
11708       LevelSolved();
11709
11710       game_em.game_over = TRUE;
11711
11712       game.all_players_gone = TRUE;
11713     }
11714
11715     if (game_em.game_over)                              // game lost
11716       game.all_players_gone = TRUE;
11717   }
11718   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11719   {
11720     if (game_sp.level_solved &&
11721         !game_sp.game_over)                             // game won
11722     {
11723       LevelSolved();
11724
11725       game_sp.game_over = TRUE;
11726
11727       game.all_players_gone = TRUE;
11728     }
11729
11730     if (game_sp.game_over)                              // game lost
11731       game.all_players_gone = TRUE;
11732   }
11733   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11734   {
11735     if (game_mm.level_solved &&
11736         !game_mm.game_over)                             // game won
11737     {
11738       LevelSolved();
11739
11740       game_mm.game_over = TRUE;
11741
11742       game.all_players_gone = TRUE;
11743     }
11744
11745     if (game_mm.game_over)                              // game lost
11746       game.all_players_gone = TRUE;
11747   }
11748 }
11749
11750 static void PlayTimeoutSound(int seconds_left)
11751 {
11752   // will be played directly by BD engine (for classic bonus time sounds)
11753   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11754     return;
11755
11756   // try to use individual "running out of time" sound for each second left
11757   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11758
11759   // if special sound per second not defined, use default sound
11760   if (getSoundInfoEntryFilename(sound) == NULL)
11761     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11762
11763   // if out of time, but player still alive, play special "timeout" sound, if defined
11764   if (seconds_left == 0 && !checkGameFailed())
11765     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11766       sound = SND_GAME_TIMEOUT;
11767
11768   PlaySound(sound);
11769 }
11770
11771 static void CheckLevelTime_StepCounter(void)
11772 {
11773   int i;
11774
11775   TimePlayed++;
11776
11777   if (TimeLeft > 0)
11778   {
11779     TimeLeft--;
11780
11781     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11782       PlayTimeoutSound(TimeLeft);
11783
11784     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11785
11786     DisplayGameControlValues();
11787
11788     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11789       for (i = 0; i < MAX_PLAYERS; i++)
11790         KillPlayer(&stored_player[i]);
11791   }
11792   else if (game.no_level_time_limit && !game.all_players_gone)
11793   {
11794     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11795
11796     DisplayGameControlValues();
11797   }
11798 }
11799
11800 static void CheckLevelTime(void)
11801 {
11802   int frames_per_second = FRAMES_PER_SECOND;
11803   int i;
11804
11805   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11806   {
11807     // level time may be running slower in native BD engine
11808     frames_per_second = getFramesPerSecond_BD();
11809
11810     // if native engine time changed, force main engine time change
11811     if (getTimeLeft_BD() < TimeLeft)
11812       TimeFrames = frames_per_second;
11813
11814     // if last second running, wait for native engine time to exactly reach zero
11815     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11816       TimeFrames = frames_per_second - 1;
11817   }
11818
11819   if (TimeFrames >= frames_per_second)
11820   {
11821     TimeFrames = 0;
11822
11823     for (i = 0; i < MAX_PLAYERS; i++)
11824     {
11825       struct PlayerInfo *player = &stored_player[i];
11826
11827       if (SHIELD_ON(player))
11828       {
11829         player->shield_normal_time_left--;
11830
11831         if (player->shield_deadly_time_left > 0)
11832           player->shield_deadly_time_left--;
11833       }
11834     }
11835
11836     if (!game.LevelSolved && !level.use_step_counter)
11837     {
11838       TimePlayed++;
11839
11840       if (TimeLeft > 0)
11841       {
11842         TimeLeft--;
11843
11844         if (TimeLeft <= 10 && game.time_limit)
11845           PlayTimeoutSound(TimeLeft);
11846
11847         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11848            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11849
11850         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11851
11852         if (!TimeLeft && game.time_limit)
11853         {
11854           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11855           {
11856             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11857               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11858           }
11859           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11860           {
11861             game_em.lev->killed_out_of_time = TRUE;
11862           }
11863           else
11864           {
11865             for (i = 0; i < MAX_PLAYERS; i++)
11866               KillPlayer(&stored_player[i]);
11867           }
11868         }
11869       }
11870       else if (game.no_level_time_limit && !game.all_players_gone)
11871       {
11872         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11873       }
11874
11875       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11876     }
11877   }
11878
11879   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11880   {
11881     TapeTimeFrames = 0;
11882     TapeTime++;
11883
11884     if (tape.recording || tape.playing)
11885       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11886   }
11887
11888   if (tape.recording || tape.playing)
11889     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11890
11891   UpdateAndDisplayGameControlValues();
11892 }
11893
11894 void AdvanceFrameAndPlayerCounters(int player_nr)
11895 {
11896   int i;
11897
11898   // handle game and tape time differently for native BD game engine
11899
11900   // tape time is running in native BD engine even if player is not hatched yet
11901   if (!checkGameRunning())
11902     return;
11903
11904   // advance frame counters (global frame counter and tape time frame counter)
11905   FrameCounter++;
11906   TapeTimeFrames++;
11907
11908   // level time is running in native BD engine after player is being hatched
11909   if (!checkGamePlaying())
11910     return;
11911
11912   // advance time frame counter (used to control available time to solve level)
11913   TimeFrames++;
11914
11915   // advance player counters (counters for move delay, move animation etc.)
11916   for (i = 0; i < MAX_PLAYERS; i++)
11917   {
11918     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11919     int move_delay_value = stored_player[i].move_delay_value;
11920     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11921
11922     if (!advance_player_counters)       // not all players may be affected
11923       continue;
11924
11925     if (move_frames == 0)       // less than one move per game frame
11926     {
11927       int stepsize = TILEX / move_delay_value;
11928       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11929       int count = (stored_player[i].is_moving ?
11930                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11931
11932       if (count % delay == 0)
11933         move_frames = 1;
11934     }
11935
11936     stored_player[i].Frame += move_frames;
11937
11938     if (stored_player[i].MovPos != 0)
11939       stored_player[i].StepFrame += move_frames;
11940
11941     if (stored_player[i].move_delay > 0)
11942       stored_player[i].move_delay--;
11943
11944     // due to bugs in previous versions, counter must count up, not down
11945     if (stored_player[i].push_delay != -1)
11946       stored_player[i].push_delay++;
11947
11948     if (stored_player[i].drop_delay > 0)
11949       stored_player[i].drop_delay--;
11950
11951     if (stored_player[i].is_dropping_pressed)
11952       stored_player[i].drop_pressed_delay++;
11953   }
11954 }
11955
11956 void AdvanceFrameCounter(void)
11957 {
11958   FrameCounter++;
11959 }
11960
11961 void AdvanceGfxFrame(void)
11962 {
11963   int x, y;
11964
11965   SCAN_PLAYFIELD(x, y)
11966   {
11967     GfxFrame[x][y]++;
11968   }
11969 }
11970
11971 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11972                               struct MouseActionInfo *mouse_action_last)
11973 {
11974   if (mouse_action->button)
11975   {
11976     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11977     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11978     int x = mouse_action->lx;
11979     int y = mouse_action->ly;
11980     int element = Tile[x][y];
11981
11982     if (new_button)
11983     {
11984       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11985       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11986                                          ch_button);
11987     }
11988
11989     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11990     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11991                                        ch_button);
11992
11993     if (level.use_step_counter)
11994     {
11995       boolean counted_click = FALSE;
11996
11997       // element clicked that can change when clicked/pressed
11998       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11999           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12000            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12001         counted_click = TRUE;
12002
12003       // element clicked that can trigger change when clicked/pressed
12004       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12005           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12006         counted_click = TRUE;
12007
12008       if (new_button && counted_click)
12009         CheckLevelTime_StepCounter();
12010     }
12011   }
12012 }
12013
12014 void StartGameActions(boolean init_network_game, boolean record_tape,
12015                       int random_seed)
12016 {
12017   unsigned int new_random_seed = InitRND(random_seed);
12018
12019   if (record_tape)
12020     TapeStartRecording(new_random_seed);
12021
12022   if (setup.auto_pause_on_start && !tape.pausing)
12023     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12024
12025   if (init_network_game)
12026   {
12027     SendToServer_LevelFile();
12028     SendToServer_StartPlaying();
12029
12030     return;
12031   }
12032
12033   InitGame();
12034 }
12035
12036 static void GameActionsExt(void)
12037 {
12038 #if 0
12039   static unsigned int game_frame_delay = 0;
12040 #endif
12041   unsigned int game_frame_delay_value;
12042   byte *recorded_player_action;
12043   byte summarized_player_action = 0;
12044   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12045   int i;
12046
12047   // detect endless loops, caused by custom element programming
12048   if (recursion_loop_detected && recursion_loop_depth == 0)
12049   {
12050     char *message = getStringCat3("Internal Error! Element ",
12051                                   EL_NAME(recursion_loop_element),
12052                                   " caused endless loop! Quit the game?");
12053
12054     Warn("element '%s' caused endless loop in game engine",
12055          EL_NAME(recursion_loop_element));
12056
12057     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12058
12059     recursion_loop_detected = FALSE;    // if game should be continued
12060
12061     free(message);
12062
12063     return;
12064   }
12065
12066   if (game.restart_level)
12067     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12068
12069   CheckLevelSolved();
12070
12071   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12072     GameWon();
12073
12074   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12075     TapeStop();
12076
12077   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12078     return;
12079
12080   game_frame_delay_value =
12081     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12082
12083   if (tape.playing && tape.warp_forward && !tape.pausing)
12084     game_frame_delay_value = 0;
12085
12086   SetVideoFrameDelay(game_frame_delay_value);
12087
12088   // (de)activate virtual buttons depending on current game status
12089   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12090   {
12091     if (game.all_players_gone)  // if no players there to be controlled anymore
12092       SetOverlayActive(FALSE);
12093     else if (!tape.playing)     // if game continues after tape stopped playing
12094       SetOverlayActive(TRUE);
12095   }
12096
12097 #if 0
12098 #if 0
12099   // ---------- main game synchronization point ----------
12100
12101   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12102
12103   Debug("game:playing:skip", "skip == %d", skip);
12104
12105 #else
12106   // ---------- main game synchronization point ----------
12107
12108   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12109 #endif
12110 #endif
12111
12112   if (network_playing && !network_player_action_received)
12113   {
12114     // try to get network player actions in time
12115
12116     // last chance to get network player actions without main loop delay
12117     HandleNetworking();
12118
12119     // game was quit by network peer
12120     if (game_status != GAME_MODE_PLAYING)
12121       return;
12122
12123     // check if network player actions still missing and game still running
12124     if (!network_player_action_received && !checkGameEnded())
12125       return;           // failed to get network player actions in time
12126
12127     // do not yet reset "network_player_action_received" (for tape.pausing)
12128   }
12129
12130   if (tape.pausing)
12131     return;
12132
12133   // at this point we know that we really continue executing the game
12134
12135   network_player_action_received = FALSE;
12136
12137   // when playing tape, read previously recorded player input from tape data
12138   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12139
12140   local_player->effective_mouse_action = local_player->mouse_action;
12141
12142   if (recorded_player_action != NULL)
12143     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12144                                  recorded_player_action);
12145
12146   // TapePlayAction() may return NULL when toggling to "pause before death"
12147   if (tape.pausing)
12148     return;
12149
12150   if (tape.set_centered_player)
12151   {
12152     game.centered_player_nr_next = tape.centered_player_nr_next;
12153     game.set_centered_player = TRUE;
12154   }
12155
12156   for (i = 0; i < MAX_PLAYERS; i++)
12157   {
12158     summarized_player_action |= stored_player[i].action;
12159
12160     if (!network_playing && (game.team_mode || tape.playing))
12161       stored_player[i].effective_action = stored_player[i].action;
12162   }
12163
12164   if (network_playing && !checkGameEnded())
12165     SendToServer_MovePlayer(summarized_player_action);
12166
12167   // summarize all actions at local players mapped input device position
12168   // (this allows using different input devices in single player mode)
12169   if (!network.enabled && !game.team_mode)
12170     stored_player[map_player_action[local_player->index_nr]].effective_action =
12171       summarized_player_action;
12172
12173   // summarize all actions at centered player in local team mode
12174   if (tape.recording &&
12175       setup.team_mode && !network.enabled &&
12176       setup.input_on_focus &&
12177       game.centered_player_nr != -1)
12178   {
12179     for (i = 0; i < MAX_PLAYERS; i++)
12180       stored_player[map_player_action[i]].effective_action =
12181         (i == game.centered_player_nr ? summarized_player_action : 0);
12182   }
12183
12184   if (recorded_player_action != NULL)
12185     for (i = 0; i < MAX_PLAYERS; i++)
12186       stored_player[i].effective_action = recorded_player_action[i];
12187
12188   for (i = 0; i < MAX_PLAYERS; i++)
12189   {
12190     tape_action[i] = stored_player[i].effective_action;
12191
12192     /* (this may happen in the RND game engine if a player was not present on
12193        the playfield on level start, but appeared later from a custom element */
12194     if (setup.team_mode &&
12195         tape.recording &&
12196         tape_action[i] &&
12197         !tape.player_participates[i])
12198       tape.player_participates[i] = TRUE;
12199   }
12200
12201   SetTapeActionFromMouseAction(tape_action,
12202                                &local_player->effective_mouse_action);
12203
12204   // only record actions from input devices, but not programmed actions
12205   if (tape.recording)
12206     TapeRecordAction(tape_action);
12207
12208   // remember if game was played (especially after tape stopped playing)
12209   if (!tape.playing && summarized_player_action && !checkGameFailed())
12210     game.GamePlayed = TRUE;
12211
12212 #if USE_NEW_PLAYER_ASSIGNMENTS
12213   // !!! also map player actions in single player mode !!!
12214   // if (game.team_mode)
12215   if (1)
12216   {
12217     byte mapped_action[MAX_PLAYERS];
12218
12219 #if DEBUG_PLAYER_ACTIONS
12220     for (i = 0; i < MAX_PLAYERS; i++)
12221       DebugContinued("", "%d, ", stored_player[i].effective_action);
12222 #endif
12223
12224     for (i = 0; i < MAX_PLAYERS; i++)
12225       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12226
12227     for (i = 0; i < MAX_PLAYERS; i++)
12228       stored_player[i].effective_action = mapped_action[i];
12229
12230 #if DEBUG_PLAYER_ACTIONS
12231     DebugContinued("", "=> ");
12232     for (i = 0; i < MAX_PLAYERS; i++)
12233       DebugContinued("", "%d, ", stored_player[i].effective_action);
12234     DebugContinued("game:playing:player", "\n");
12235 #endif
12236   }
12237 #if DEBUG_PLAYER_ACTIONS
12238   else
12239   {
12240     for (i = 0; i < MAX_PLAYERS; i++)
12241       DebugContinued("", "%d, ", stored_player[i].effective_action);
12242     DebugContinued("game:playing:player", "\n");
12243   }
12244 #endif
12245 #endif
12246
12247   for (i = 0; i < MAX_PLAYERS; i++)
12248   {
12249     // allow engine snapshot in case of changed movement attempt
12250     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12251         (stored_player[i].effective_action & KEY_MOTION))
12252       game.snapshot.changed_action = TRUE;
12253
12254     // allow engine snapshot in case of snapping/dropping attempt
12255     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12256         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12257       game.snapshot.changed_action = TRUE;
12258
12259     game.snapshot.last_action[i] = stored_player[i].effective_action;
12260   }
12261
12262   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12263   {
12264     GameActions_BD_Main();
12265   }
12266   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12267   {
12268     GameActions_EM_Main();
12269   }
12270   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12271   {
12272     GameActions_SP_Main();
12273   }
12274   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12275   {
12276     GameActions_MM_Main();
12277   }
12278   else
12279   {
12280     GameActions_RND_Main();
12281   }
12282
12283   BlitScreenToBitmap(backbuffer);
12284
12285   CheckLevelSolved();
12286   CheckLevelTime();
12287
12288   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12289
12290   if (global.show_frames_per_second)
12291   {
12292     static unsigned int fps_counter = 0;
12293     static int fps_frames = 0;
12294     unsigned int fps_delay_ms = Counter() - fps_counter;
12295
12296     fps_frames++;
12297
12298     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12299     {
12300       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12301
12302       fps_frames = 0;
12303       fps_counter = Counter();
12304
12305       // always draw FPS to screen after FPS value was updated
12306       redraw_mask |= REDRAW_FPS;
12307     }
12308
12309     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12310     if (GetDrawDeactivationMask() == REDRAW_NONE)
12311       redraw_mask |= REDRAW_FPS;
12312   }
12313 }
12314
12315 static void GameActions_CheckSaveEngineSnapshot(void)
12316 {
12317   if (!game.snapshot.save_snapshot)
12318     return;
12319
12320   // clear flag for saving snapshot _before_ saving snapshot
12321   game.snapshot.save_snapshot = FALSE;
12322
12323   SaveEngineSnapshotToList();
12324 }
12325
12326 void GameActions(void)
12327 {
12328   GameActionsExt();
12329
12330   GameActions_CheckSaveEngineSnapshot();
12331 }
12332
12333 void GameActions_BD_Main(void)
12334 {
12335   byte effective_action[MAX_PLAYERS];
12336   int i;
12337
12338   for (i = 0; i < MAX_PLAYERS; i++)
12339     effective_action[i] = stored_player[i].effective_action;
12340
12341   GameActions_BD(effective_action);
12342 }
12343
12344 void GameActions_EM_Main(void)
12345 {
12346   byte effective_action[MAX_PLAYERS];
12347   int i;
12348
12349   for (i = 0; i < MAX_PLAYERS; i++)
12350     effective_action[i] = stored_player[i].effective_action;
12351
12352   GameActions_EM(effective_action);
12353 }
12354
12355 void GameActions_SP_Main(void)
12356 {
12357   byte effective_action[MAX_PLAYERS];
12358   int i;
12359
12360   for (i = 0; i < MAX_PLAYERS; i++)
12361     effective_action[i] = stored_player[i].effective_action;
12362
12363   GameActions_SP(effective_action);
12364
12365   for (i = 0; i < MAX_PLAYERS; i++)
12366   {
12367     if (stored_player[i].force_dropping)
12368       stored_player[i].action |= KEY_BUTTON_DROP;
12369
12370     stored_player[i].force_dropping = FALSE;
12371   }
12372 }
12373
12374 void GameActions_MM_Main(void)
12375 {
12376   AdvanceGfxFrame();
12377
12378   GameActions_MM(local_player->effective_mouse_action);
12379 }
12380
12381 void GameActions_RND_Main(void)
12382 {
12383   GameActions_RND();
12384 }
12385
12386 void GameActions_RND(void)
12387 {
12388   static struct MouseActionInfo mouse_action_last = { 0 };
12389   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12390   int magic_wall_x = 0, magic_wall_y = 0;
12391   int i, x, y, element, graphic, last_gfx_frame;
12392
12393   InitPlayfieldScanModeVars();
12394
12395   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12396   {
12397     SCAN_PLAYFIELD(x, y)
12398     {
12399       ChangeCount[x][y] = 0;
12400       ChangeEvent[x][y] = -1;
12401     }
12402   }
12403
12404   if (game.set_centered_player)
12405   {
12406     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12407
12408     // switching to "all players" only possible if all players fit to screen
12409     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12410     {
12411       game.centered_player_nr_next = game.centered_player_nr;
12412       game.set_centered_player = FALSE;
12413     }
12414
12415     // do not switch focus to non-existing (or non-active) player
12416     if (game.centered_player_nr_next >= 0 &&
12417         !stored_player[game.centered_player_nr_next].active)
12418     {
12419       game.centered_player_nr_next = game.centered_player_nr;
12420       game.set_centered_player = FALSE;
12421     }
12422   }
12423
12424   if (game.set_centered_player &&
12425       ScreenMovPos == 0)        // screen currently aligned at tile position
12426   {
12427     int sx, sy;
12428
12429     if (game.centered_player_nr_next == -1)
12430     {
12431       setScreenCenteredToAllPlayers(&sx, &sy);
12432     }
12433     else
12434     {
12435       sx = stored_player[game.centered_player_nr_next].jx;
12436       sy = stored_player[game.centered_player_nr_next].jy;
12437     }
12438
12439     game.centered_player_nr = game.centered_player_nr_next;
12440     game.set_centered_player = FALSE;
12441
12442     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12443     DrawGameDoorValues();
12444   }
12445
12446   // check single step mode (set flag and clear again if any player is active)
12447   game.enter_single_step_mode =
12448     (tape.single_step && tape.recording && !tape.pausing);
12449
12450   for (i = 0; i < MAX_PLAYERS; i++)
12451   {
12452     int actual_player_action = stored_player[i].effective_action;
12453
12454 #if 1
12455     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12456        - rnd_equinox_tetrachloride 048
12457        - rnd_equinox_tetrachloride_ii 096
12458        - rnd_emanuel_schmieg 002
12459        - doctor_sloan_ww 001, 020
12460     */
12461     if (stored_player[i].MovPos == 0)
12462       CheckGravityMovement(&stored_player[i]);
12463 #endif
12464
12465     // overwrite programmed action with tape action
12466     if (stored_player[i].programmed_action)
12467       actual_player_action = stored_player[i].programmed_action;
12468
12469     PlayerActions(&stored_player[i], actual_player_action);
12470
12471     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12472   }
12473
12474   // single step pause mode may already have been toggled by "ScrollPlayer()"
12475   if (game.enter_single_step_mode && !tape.pausing)
12476     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12477
12478   ScrollScreen(NULL, SCROLL_GO_ON);
12479
12480   /* for backwards compatibility, the following code emulates a fixed bug that
12481      occured when pushing elements (causing elements that just made their last
12482      pushing step to already (if possible) make their first falling step in the
12483      same game frame, which is bad); this code is also needed to use the famous
12484      "spring push bug" which is used in older levels and might be wanted to be
12485      used also in newer levels, but in this case the buggy pushing code is only
12486      affecting the "spring" element and no other elements */
12487
12488   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12489   {
12490     for (i = 0; i < MAX_PLAYERS; i++)
12491     {
12492       struct PlayerInfo *player = &stored_player[i];
12493       int x = player->jx;
12494       int y = player->jy;
12495
12496       if (player->active && player->is_pushing && player->is_moving &&
12497           IS_MOVING(x, y) &&
12498           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12499            Tile[x][y] == EL_SPRING))
12500       {
12501         ContinueMoving(x, y);
12502
12503         // continue moving after pushing (this is actually a bug)
12504         if (!IS_MOVING(x, y))
12505           Stop[x][y] = FALSE;
12506       }
12507     }
12508   }
12509
12510   SCAN_PLAYFIELD(x, y)
12511   {
12512     Last[x][y] = Tile[x][y];
12513
12514     ChangeCount[x][y] = 0;
12515     ChangeEvent[x][y] = -1;
12516
12517     // this must be handled before main playfield loop
12518     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12519     {
12520       MovDelay[x][y]--;
12521       if (MovDelay[x][y] <= 0)
12522         RemoveField(x, y);
12523     }
12524
12525     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12526     {
12527       MovDelay[x][y]--;
12528       if (MovDelay[x][y] <= 0)
12529       {
12530         int element = Store[x][y];
12531         int move_direction = MovDir[x][y];
12532         int player_index_bit = Store2[x][y];
12533
12534         Store[x][y] = 0;
12535         Store2[x][y] = 0;
12536
12537         RemoveField(x, y);
12538         TEST_DrawLevelField(x, y);
12539
12540         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12541
12542         if (IS_ENVELOPE(element))
12543           local_player->show_envelope = element;
12544       }
12545     }
12546
12547 #if DEBUG
12548     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12549     {
12550       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12551             x, y);
12552       Debug("game:playing:GameActions_RND", "This should never happen!");
12553
12554       ChangePage[x][y] = -1;
12555     }
12556 #endif
12557
12558     Stop[x][y] = FALSE;
12559     if (WasJustMoving[x][y] > 0)
12560       WasJustMoving[x][y]--;
12561     if (WasJustFalling[x][y] > 0)
12562       WasJustFalling[x][y]--;
12563     if (CheckCollision[x][y] > 0)
12564       CheckCollision[x][y]--;
12565     if (CheckImpact[x][y] > 0)
12566       CheckImpact[x][y]--;
12567
12568     GfxFrame[x][y]++;
12569
12570     /* reset finished pushing action (not done in ContinueMoving() to allow
12571        continuous pushing animation for elements with zero push delay) */
12572     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12573     {
12574       ResetGfxAnimation(x, y);
12575       TEST_DrawLevelField(x, y);
12576     }
12577
12578 #if DEBUG
12579     if (IS_BLOCKED(x, y))
12580     {
12581       int oldx, oldy;
12582
12583       Blocked2Moving(x, y, &oldx, &oldy);
12584       if (!IS_MOVING(oldx, oldy))
12585       {
12586         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12587         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12588         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12589         Debug("game:playing:GameActions_RND", "This should never happen!");
12590       }
12591     }
12592 #endif
12593   }
12594
12595   HandleMouseAction(&mouse_action, &mouse_action_last);
12596
12597   SCAN_PLAYFIELD(x, y)
12598   {
12599     element = Tile[x][y];
12600     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12601     last_gfx_frame = GfxFrame[x][y];
12602
12603     if (element == EL_EMPTY)
12604       graphic = el2img(GfxElementEmpty[x][y]);
12605
12606     ResetGfxFrame(x, y);
12607
12608     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12609       DrawLevelGraphicAnimation(x, y, graphic);
12610
12611     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12612         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12613       ResetRandomAnimationValue(x, y);
12614
12615     SetRandomAnimationValue(x, y);
12616
12617     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12618
12619     if (IS_INACTIVE(element))
12620     {
12621       if (IS_ANIMATED(graphic))
12622         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12623
12624       continue;
12625     }
12626
12627     // this may take place after moving, so 'element' may have changed
12628     if (IS_CHANGING(x, y) &&
12629         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12630     {
12631       int page = element_info[element].event_page_nr[CE_DELAY];
12632
12633       HandleElementChange(x, y, page);
12634
12635       element = Tile[x][y];
12636       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12637     }
12638
12639     CheckNextToConditions(x, y);
12640
12641     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12642     {
12643       StartMoving(x, y);
12644
12645       element = Tile[x][y];
12646       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12647
12648       if (IS_ANIMATED(graphic) &&
12649           !IS_MOVING(x, y) &&
12650           !Stop[x][y])
12651         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12652
12653       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12654         TEST_DrawTwinkleOnField(x, y);
12655     }
12656     else if (element == EL_ACID)
12657     {
12658       if (!Stop[x][y])
12659         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12660     }
12661     else if ((element == EL_EXIT_OPEN ||
12662               element == EL_EM_EXIT_OPEN ||
12663               element == EL_SP_EXIT_OPEN ||
12664               element == EL_STEEL_EXIT_OPEN ||
12665               element == EL_EM_STEEL_EXIT_OPEN ||
12666               element == EL_SP_TERMINAL ||
12667               element == EL_SP_TERMINAL_ACTIVE ||
12668               element == EL_EXTRA_TIME ||
12669               element == EL_SHIELD_NORMAL ||
12670               element == EL_SHIELD_DEADLY) &&
12671              IS_ANIMATED(graphic))
12672       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12673     else if (IS_MOVING(x, y))
12674       ContinueMoving(x, y);
12675     else if (IS_ACTIVE_BOMB(element))
12676       CheckDynamite(x, y);
12677     else if (element == EL_AMOEBA_GROWING)
12678       AmoebaGrowing(x, y);
12679     else if (element == EL_AMOEBA_SHRINKING)
12680       AmoebaShrinking(x, y);
12681
12682 #if !USE_NEW_AMOEBA_CODE
12683     else if (IS_AMOEBALIVE(element))
12684       AmoebaReproduce(x, y);
12685 #endif
12686
12687     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12688       Life(x, y);
12689     else if (element == EL_EXIT_CLOSED)
12690       CheckExit(x, y);
12691     else if (element == EL_EM_EXIT_CLOSED)
12692       CheckExitEM(x, y);
12693     else if (element == EL_STEEL_EXIT_CLOSED)
12694       CheckExitSteel(x, y);
12695     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12696       CheckExitSteelEM(x, y);
12697     else if (element == EL_SP_EXIT_CLOSED)
12698       CheckExitSP(x, y);
12699     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12700              element == EL_EXPANDABLE_STEELWALL_GROWING)
12701       WallGrowing(x, y);
12702     else if (element == EL_EXPANDABLE_WALL ||
12703              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12704              element == EL_EXPANDABLE_WALL_VERTICAL ||
12705              element == EL_EXPANDABLE_WALL_ANY ||
12706              element == EL_BD_EXPANDABLE_WALL ||
12707              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12708              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12709              element == EL_EXPANDABLE_STEELWALL_ANY)
12710       CheckWallGrowing(x, y);
12711     else if (element == EL_FLAMES)
12712       CheckForDragon(x, y);
12713     else if (element == EL_EXPLOSION)
12714       ; // drawing of correct explosion animation is handled separately
12715     else if (element == EL_ELEMENT_SNAPPING ||
12716              element == EL_DIAGONAL_SHRINKING ||
12717              element == EL_DIAGONAL_GROWING)
12718     {
12719       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12720
12721       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12722     }
12723     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12724       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12725
12726     if (IS_BELT_ACTIVE(element))
12727       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12728
12729     if (game.magic_wall_active)
12730     {
12731       int jx = local_player->jx, jy = local_player->jy;
12732
12733       // play the element sound at the position nearest to the player
12734       if ((element == EL_MAGIC_WALL_FULL ||
12735            element == EL_MAGIC_WALL_ACTIVE ||
12736            element == EL_MAGIC_WALL_EMPTYING ||
12737            element == EL_BD_MAGIC_WALL_FULL ||
12738            element == EL_BD_MAGIC_WALL_ACTIVE ||
12739            element == EL_BD_MAGIC_WALL_EMPTYING ||
12740            element == EL_DC_MAGIC_WALL_FULL ||
12741            element == EL_DC_MAGIC_WALL_ACTIVE ||
12742            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12743           ABS(x - jx) + ABS(y - jy) <
12744           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12745       {
12746         magic_wall_x = x;
12747         magic_wall_y = y;
12748       }
12749     }
12750   }
12751
12752 #if USE_NEW_AMOEBA_CODE
12753   // new experimental amoeba growth stuff
12754   if (!(FrameCounter % 8))
12755   {
12756     static unsigned int random = 1684108901;
12757
12758     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12759     {
12760       x = RND(lev_fieldx);
12761       y = RND(lev_fieldy);
12762       element = Tile[x][y];
12763
12764       if (!IS_PLAYER(x, y) &&
12765           (element == EL_EMPTY ||
12766            CAN_GROW_INTO(element) ||
12767            element == EL_QUICKSAND_EMPTY ||
12768            element == EL_QUICKSAND_FAST_EMPTY ||
12769            element == EL_ACID_SPLASH_LEFT ||
12770            element == EL_ACID_SPLASH_RIGHT))
12771       {
12772         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12773             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12774             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12775             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12776           Tile[x][y] = EL_AMOEBA_DROP;
12777       }
12778
12779       random = random * 129 + 1;
12780     }
12781   }
12782 #endif
12783
12784   game.explosions_delayed = FALSE;
12785
12786   SCAN_PLAYFIELD(x, y)
12787   {
12788     element = Tile[x][y];
12789
12790     if (ExplodeField[x][y])
12791       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12792     else if (element == EL_EXPLOSION)
12793       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12794
12795     ExplodeField[x][y] = EX_TYPE_NONE;
12796   }
12797
12798   game.explosions_delayed = TRUE;
12799
12800   if (game.magic_wall_active)
12801   {
12802     if (!(game.magic_wall_time_left % 4))
12803     {
12804       int element = Tile[magic_wall_x][magic_wall_y];
12805
12806       if (element == EL_BD_MAGIC_WALL_FULL ||
12807           element == EL_BD_MAGIC_WALL_ACTIVE ||
12808           element == EL_BD_MAGIC_WALL_EMPTYING)
12809         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12810       else if (element == EL_DC_MAGIC_WALL_FULL ||
12811                element == EL_DC_MAGIC_WALL_ACTIVE ||
12812                element == EL_DC_MAGIC_WALL_EMPTYING)
12813         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12814       else
12815         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12816     }
12817
12818     if (game.magic_wall_time_left > 0)
12819     {
12820       game.magic_wall_time_left--;
12821
12822       if (!game.magic_wall_time_left)
12823       {
12824         SCAN_PLAYFIELD(x, y)
12825         {
12826           element = Tile[x][y];
12827
12828           if (element == EL_MAGIC_WALL_ACTIVE ||
12829               element == EL_MAGIC_WALL_FULL)
12830           {
12831             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12832             TEST_DrawLevelField(x, y);
12833           }
12834           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12835                    element == EL_BD_MAGIC_WALL_FULL)
12836           {
12837             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12838             TEST_DrawLevelField(x, y);
12839           }
12840           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12841                    element == EL_DC_MAGIC_WALL_FULL)
12842           {
12843             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12844             TEST_DrawLevelField(x, y);
12845           }
12846         }
12847
12848         game.magic_wall_active = FALSE;
12849       }
12850     }
12851   }
12852
12853   if (game.light_time_left > 0)
12854   {
12855     game.light_time_left--;
12856
12857     if (game.light_time_left == 0)
12858       RedrawAllLightSwitchesAndInvisibleElements();
12859   }
12860
12861   if (game.timegate_time_left > 0)
12862   {
12863     game.timegate_time_left--;
12864
12865     if (game.timegate_time_left == 0)
12866       CloseAllOpenTimegates();
12867   }
12868
12869   if (game.lenses_time_left > 0)
12870   {
12871     game.lenses_time_left--;
12872
12873     if (game.lenses_time_left == 0)
12874       RedrawAllInvisibleElementsForLenses();
12875   }
12876
12877   if (game.magnify_time_left > 0)
12878   {
12879     game.magnify_time_left--;
12880
12881     if (game.magnify_time_left == 0)
12882       RedrawAllInvisibleElementsForMagnifier();
12883   }
12884
12885   for (i = 0; i < MAX_PLAYERS; i++)
12886   {
12887     struct PlayerInfo *player = &stored_player[i];
12888
12889     if (SHIELD_ON(player))
12890     {
12891       if (player->shield_deadly_time_left)
12892         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12893       else if (player->shield_normal_time_left)
12894         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12895     }
12896   }
12897
12898 #if USE_DELAYED_GFX_REDRAW
12899   SCAN_PLAYFIELD(x, y)
12900   {
12901     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12902     {
12903       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12904          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12905
12906       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12907         DrawLevelField(x, y);
12908
12909       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12910         DrawLevelFieldCrumbled(x, y);
12911
12912       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12913         DrawLevelFieldCrumbledNeighbours(x, y);
12914
12915       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12916         DrawTwinkleOnField(x, y);
12917     }
12918
12919     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12920   }
12921 #endif
12922
12923   DrawAllPlayers();
12924   PlayAllPlayersSound();
12925
12926   for (i = 0; i < MAX_PLAYERS; i++)
12927   {
12928     struct PlayerInfo *player = &stored_player[i];
12929
12930     if (player->show_envelope != 0 && (!player->active ||
12931                                        player->MovPos == 0))
12932     {
12933       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12934
12935       player->show_envelope = 0;
12936     }
12937   }
12938
12939   // use random number generator in every frame to make it less predictable
12940   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12941     RND(1);
12942
12943   mouse_action_last = mouse_action;
12944 }
12945
12946 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12947 {
12948   int min_x = x, min_y = y, max_x = x, max_y = y;
12949   int scr_fieldx = getScreenFieldSizeX();
12950   int scr_fieldy = getScreenFieldSizeY();
12951   int i;
12952
12953   for (i = 0; i < MAX_PLAYERS; i++)
12954   {
12955     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12956
12957     if (!stored_player[i].active || &stored_player[i] == player)
12958       continue;
12959
12960     min_x = MIN(min_x, jx);
12961     min_y = MIN(min_y, jy);
12962     max_x = MAX(max_x, jx);
12963     max_y = MAX(max_y, jy);
12964   }
12965
12966   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12967 }
12968
12969 static boolean AllPlayersInVisibleScreen(void)
12970 {
12971   int i;
12972
12973   for (i = 0; i < MAX_PLAYERS; i++)
12974   {
12975     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12976
12977     if (!stored_player[i].active)
12978       continue;
12979
12980     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12981       return FALSE;
12982   }
12983
12984   return TRUE;
12985 }
12986
12987 void ScrollLevel(int dx, int dy)
12988 {
12989   int scroll_offset = 2 * TILEX_VAR;
12990   int x, y;
12991
12992   BlitBitmap(drawto_field, drawto_field,
12993              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12994              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12995              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12996              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12997              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12998              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12999
13000   if (dx != 0)
13001   {
13002     x = (dx == 1 ? BX1 : BX2);
13003     for (y = BY1; y <= BY2; y++)
13004       DrawScreenField(x, y);
13005   }
13006
13007   if (dy != 0)
13008   {
13009     y = (dy == 1 ? BY1 : BY2);
13010     for (x = BX1; x <= BX2; x++)
13011       DrawScreenField(x, y);
13012   }
13013
13014   redraw_mask |= REDRAW_FIELD;
13015 }
13016
13017 static boolean canFallDown(struct PlayerInfo *player)
13018 {
13019   int jx = player->jx, jy = player->jy;
13020
13021   return (IN_LEV_FIELD(jx, jy + 1) &&
13022           (IS_FREE(jx, jy + 1) ||
13023            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13024           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13025           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13026 }
13027
13028 static boolean canPassField(int x, int y, int move_dir)
13029 {
13030   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13031   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13032   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13033   int nextx = x + dx;
13034   int nexty = y + dy;
13035   int element = Tile[x][y];
13036
13037   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13038           !CAN_MOVE(element) &&
13039           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13040           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13041           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13042 }
13043
13044 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13045 {
13046   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13047   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13048   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13049   int newx = x + dx;
13050   int newy = y + dy;
13051
13052   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13053           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13054           (IS_DIGGABLE(Tile[newx][newy]) ||
13055            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13056            canPassField(newx, newy, move_dir)));
13057 }
13058
13059 static void CheckGravityMovement(struct PlayerInfo *player)
13060 {
13061   if (player->gravity && !player->programmed_action)
13062   {
13063     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13064     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13065     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13066     int jx = player->jx, jy = player->jy;
13067     boolean player_is_moving_to_valid_field =
13068       (!player_is_snapping &&
13069        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13070         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13071     boolean player_can_fall_down = canFallDown(player);
13072
13073     if (player_can_fall_down &&
13074         !player_is_moving_to_valid_field)
13075       player->programmed_action = MV_DOWN;
13076   }
13077 }
13078
13079 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13080 {
13081   return CheckGravityMovement(player);
13082
13083   if (player->gravity && !player->programmed_action)
13084   {
13085     int jx = player->jx, jy = player->jy;
13086     boolean field_under_player_is_free =
13087       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13088     boolean player_is_standing_on_valid_field =
13089       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13090        (IS_WALKABLE(Tile[jx][jy]) &&
13091         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13092
13093     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13094       player->programmed_action = MV_DOWN;
13095   }
13096 }
13097
13098 /*
13099   MovePlayerOneStep()
13100   -----------------------------------------------------------------------------
13101   dx, dy:               direction (non-diagonal) to try to move the player to
13102   real_dx, real_dy:     direction as read from input device (can be diagonal)
13103 */
13104
13105 boolean MovePlayerOneStep(struct PlayerInfo *player,
13106                           int dx, int dy, int real_dx, int real_dy)
13107 {
13108   int jx = player->jx, jy = player->jy;
13109   int new_jx = jx + dx, new_jy = jy + dy;
13110   int can_move;
13111   boolean player_can_move = !player->cannot_move;
13112
13113   if (!player->active || (!dx && !dy))
13114     return MP_NO_ACTION;
13115
13116   player->MovDir = (dx < 0 ? MV_LEFT :
13117                     dx > 0 ? MV_RIGHT :
13118                     dy < 0 ? MV_UP :
13119                     dy > 0 ? MV_DOWN :  MV_NONE);
13120
13121   if (!IN_LEV_FIELD(new_jx, new_jy))
13122     return MP_NO_ACTION;
13123
13124   if (!player_can_move)
13125   {
13126     if (player->MovPos == 0)
13127     {
13128       player->is_moving = FALSE;
13129       player->is_digging = FALSE;
13130       player->is_collecting = FALSE;
13131       player->is_snapping = FALSE;
13132       player->is_pushing = FALSE;
13133     }
13134   }
13135
13136   if (!network.enabled && game.centered_player_nr == -1 &&
13137       !AllPlayersInSight(player, new_jx, new_jy))
13138     return MP_NO_ACTION;
13139
13140   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13141   if (can_move != MP_MOVING)
13142     return can_move;
13143
13144   // check if DigField() has caused relocation of the player
13145   if (player->jx != jx || player->jy != jy)
13146     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13147
13148   StorePlayer[jx][jy] = 0;
13149   player->last_jx = jx;
13150   player->last_jy = jy;
13151   player->jx = new_jx;
13152   player->jy = new_jy;
13153   StorePlayer[new_jx][new_jy] = player->element_nr;
13154
13155   if (player->move_delay_value_next != -1)
13156   {
13157     player->move_delay_value = player->move_delay_value_next;
13158     player->move_delay_value_next = -1;
13159   }
13160
13161   player->MovPos =
13162     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13163
13164   player->step_counter++;
13165
13166   PlayerVisit[jx][jy] = FrameCounter;
13167
13168   player->is_moving = TRUE;
13169
13170 #if 1
13171   // should better be called in MovePlayer(), but this breaks some tapes
13172   ScrollPlayer(player, SCROLL_INIT);
13173 #endif
13174
13175   return MP_MOVING;
13176 }
13177
13178 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13179 {
13180   int jx = player->jx, jy = player->jy;
13181   int old_jx = jx, old_jy = jy;
13182   int moved = MP_NO_ACTION;
13183
13184   if (!player->active)
13185     return FALSE;
13186
13187   if (!dx && !dy)
13188   {
13189     if (player->MovPos == 0)
13190     {
13191       player->is_moving = FALSE;
13192       player->is_digging = FALSE;
13193       player->is_collecting = FALSE;
13194       player->is_snapping = FALSE;
13195       player->is_pushing = FALSE;
13196     }
13197
13198     return FALSE;
13199   }
13200
13201   if (player->move_delay > 0)
13202     return FALSE;
13203
13204   player->move_delay = -1;              // set to "uninitialized" value
13205
13206   // store if player is automatically moved to next field
13207   player->is_auto_moving = (player->programmed_action != MV_NONE);
13208
13209   // remove the last programmed player action
13210   player->programmed_action = 0;
13211
13212   if (player->MovPos)
13213   {
13214     // should only happen if pre-1.2 tape recordings are played
13215     // this is only for backward compatibility
13216
13217     int original_move_delay_value = player->move_delay_value;
13218
13219 #if DEBUG
13220     Debug("game:playing:MovePlayer",
13221           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13222           tape.counter);
13223 #endif
13224
13225     // scroll remaining steps with finest movement resolution
13226     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13227
13228     while (player->MovPos)
13229     {
13230       ScrollPlayer(player, SCROLL_GO_ON);
13231       ScrollScreen(NULL, SCROLL_GO_ON);
13232
13233       AdvanceFrameAndPlayerCounters(player->index_nr);
13234
13235       DrawAllPlayers();
13236       BackToFront_WithFrameDelay(0);
13237     }
13238
13239     player->move_delay_value = original_move_delay_value;
13240   }
13241
13242   player->is_active = FALSE;
13243
13244   if (player->last_move_dir & MV_HORIZONTAL)
13245   {
13246     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13247       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13248   }
13249   else
13250   {
13251     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13252       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13253   }
13254
13255   if (!moved && !player->is_active)
13256   {
13257     player->is_moving = FALSE;
13258     player->is_digging = FALSE;
13259     player->is_collecting = FALSE;
13260     player->is_snapping = FALSE;
13261     player->is_pushing = FALSE;
13262   }
13263
13264   jx = player->jx;
13265   jy = player->jy;
13266
13267   if (moved & MP_MOVING && !ScreenMovPos &&
13268       (player->index_nr == game.centered_player_nr ||
13269        game.centered_player_nr == -1))
13270   {
13271     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13272
13273     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13274     {
13275       // actual player has left the screen -- scroll in that direction
13276       if (jx != old_jx)         // player has moved horizontally
13277         scroll_x += (jx - old_jx);
13278       else                      // player has moved vertically
13279         scroll_y += (jy - old_jy);
13280     }
13281     else
13282     {
13283       int offset_raw = game.scroll_delay_value;
13284
13285       if (jx != old_jx)         // player has moved horizontally
13286       {
13287         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13288         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13289         int new_scroll_x = jx - MIDPOSX + offset_x;
13290
13291         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13292             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13293           scroll_x = new_scroll_x;
13294
13295         // don't scroll over playfield boundaries
13296         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13297
13298         // don't scroll more than one field at a time
13299         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13300
13301         // don't scroll against the player's moving direction
13302         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13303             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13304           scroll_x = old_scroll_x;
13305       }
13306       else                      // player has moved vertically
13307       {
13308         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13309         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13310         int new_scroll_y = jy - MIDPOSY + offset_y;
13311
13312         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13313             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13314           scroll_y = new_scroll_y;
13315
13316         // don't scroll over playfield boundaries
13317         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13318
13319         // don't scroll more than one field at a time
13320         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13321
13322         // don't scroll against the player's moving direction
13323         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13324             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13325           scroll_y = old_scroll_y;
13326       }
13327     }
13328
13329     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13330     {
13331       if (!network.enabled && game.centered_player_nr == -1 &&
13332           !AllPlayersInVisibleScreen())
13333       {
13334         scroll_x = old_scroll_x;
13335         scroll_y = old_scroll_y;
13336       }
13337       else
13338       {
13339         ScrollScreen(player, SCROLL_INIT);
13340         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13341       }
13342     }
13343   }
13344
13345   player->StepFrame = 0;
13346
13347   if (moved & MP_MOVING)
13348   {
13349     if (old_jx != jx && old_jy == jy)
13350       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13351     else if (old_jx == jx && old_jy != jy)
13352       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13353
13354     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13355
13356     player->last_move_dir = player->MovDir;
13357     player->is_moving = TRUE;
13358     player->is_snapping = FALSE;
13359     player->is_switching = FALSE;
13360     player->is_dropping = FALSE;
13361     player->is_dropping_pressed = FALSE;
13362     player->drop_pressed_delay = 0;
13363
13364 #if 0
13365     // should better be called here than above, but this breaks some tapes
13366     ScrollPlayer(player, SCROLL_INIT);
13367 #endif
13368   }
13369   else
13370   {
13371     CheckGravityMovementWhenNotMoving(player);
13372
13373     player->is_moving = FALSE;
13374
13375     /* at this point, the player is allowed to move, but cannot move right now
13376        (e.g. because of something blocking the way) -- ensure that the player
13377        is also allowed to move in the next frame (in old versions before 3.1.1,
13378        the player was forced to wait again for eight frames before next try) */
13379
13380     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13381       player->move_delay = 0;   // allow direct movement in the next frame
13382   }
13383
13384   if (player->move_delay == -1)         // not yet initialized by DigField()
13385     player->move_delay = player->move_delay_value;
13386
13387   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13388   {
13389     TestIfPlayerTouchesBadThing(jx, jy);
13390     TestIfPlayerTouchesCustomElement(jx, jy);
13391   }
13392
13393   if (!player->active)
13394     RemovePlayer(player);
13395
13396   return moved;
13397 }
13398
13399 void ScrollPlayer(struct PlayerInfo *player, int mode)
13400 {
13401   int jx = player->jx, jy = player->jy;
13402   int last_jx = player->last_jx, last_jy = player->last_jy;
13403   int move_stepsize = TILEX / player->move_delay_value;
13404
13405   if (!player->active)
13406     return;
13407
13408   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13409     return;
13410
13411   if (mode == SCROLL_INIT)
13412   {
13413     player->actual_frame_counter.count = FrameCounter;
13414     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13415
13416     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13417         Tile[last_jx][last_jy] == EL_EMPTY)
13418     {
13419       int last_field_block_delay = 0;   // start with no blocking at all
13420       int block_delay_adjustment = player->block_delay_adjustment;
13421
13422       // if player blocks last field, add delay for exactly one move
13423       if (player->block_last_field)
13424       {
13425         last_field_block_delay += player->move_delay_value;
13426
13427         // when blocking enabled, prevent moving up despite gravity
13428         if (player->gravity && player->MovDir == MV_UP)
13429           block_delay_adjustment = -1;
13430       }
13431
13432       // add block delay adjustment (also possible when not blocking)
13433       last_field_block_delay += block_delay_adjustment;
13434
13435       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13436       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13437     }
13438
13439     if (player->MovPos != 0)    // player has not yet reached destination
13440       return;
13441   }
13442   else if (!FrameReached(&player->actual_frame_counter))
13443     return;
13444
13445   if (player->MovPos != 0)
13446   {
13447     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13448     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13449
13450     // before DrawPlayer() to draw correct player graphic for this case
13451     if (player->MovPos == 0)
13452       CheckGravityMovement(player);
13453   }
13454
13455   if (player->MovPos == 0)      // player reached destination field
13456   {
13457     if (player->move_delay_reset_counter > 0)
13458     {
13459       player->move_delay_reset_counter--;
13460
13461       if (player->move_delay_reset_counter == 0)
13462       {
13463         // continue with normal speed after quickly moving through gate
13464         HALVE_PLAYER_SPEED(player);
13465
13466         // be able to make the next move without delay
13467         player->move_delay = 0;
13468       }
13469     }
13470
13471     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13472         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13473         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13474         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13475         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13476         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13477         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13478         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13479     {
13480       ExitPlayer(player);
13481
13482       if (game.players_still_needed == 0 &&
13483           (game.friends_still_needed == 0 ||
13484            IS_SP_ELEMENT(Tile[jx][jy])))
13485         LevelSolved();
13486     }
13487
13488     player->last_jx = jx;
13489     player->last_jy = jy;
13490
13491     // this breaks one level: "machine", level 000
13492     {
13493       int move_direction = player->MovDir;
13494       int enter_side = MV_DIR_OPPOSITE(move_direction);
13495       int leave_side = move_direction;
13496       int old_jx = last_jx;
13497       int old_jy = last_jy;
13498       int old_element = Tile[old_jx][old_jy];
13499       int new_element = Tile[jx][jy];
13500
13501       if (IS_CUSTOM_ELEMENT(old_element))
13502         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13503                                    CE_LEFT_BY_PLAYER,
13504                                    player->index_bit, leave_side);
13505
13506       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13507                                           CE_PLAYER_LEAVES_X,
13508                                           player->index_bit, leave_side);
13509
13510       // needed because pushed element has not yet reached its destination,
13511       // so it would trigger a change event at its previous field location
13512       if (!player->is_pushing)
13513       {
13514         if (IS_CUSTOM_ELEMENT(new_element))
13515           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13516                                      player->index_bit, enter_side);
13517
13518         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13519                                             CE_PLAYER_ENTERS_X,
13520                                             player->index_bit, enter_side);
13521       }
13522
13523       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13524                                         CE_MOVE_OF_X, move_direction);
13525     }
13526
13527     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13528     {
13529       TestIfPlayerTouchesBadThing(jx, jy);
13530       TestIfPlayerTouchesCustomElement(jx, jy);
13531
13532       // needed because pushed element has not yet reached its destination,
13533       // so it would trigger a change event at its previous field location
13534       if (!player->is_pushing)
13535         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13536
13537       if (level.finish_dig_collect &&
13538           (player->is_digging || player->is_collecting))
13539       {
13540         int last_element = player->last_removed_element;
13541         int move_direction = player->MovDir;
13542         int enter_side = MV_DIR_OPPOSITE(move_direction);
13543         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13544                             CE_PLAYER_COLLECTS_X);
13545
13546         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13547                                             player->index_bit, enter_side);
13548
13549         player->last_removed_element = EL_UNDEFINED;
13550       }
13551
13552       if (!player->active)
13553         RemovePlayer(player);
13554     }
13555
13556     if (level.use_step_counter)
13557       CheckLevelTime_StepCounter();
13558
13559     if (tape.single_step && tape.recording && !tape.pausing &&
13560         !player->programmed_action)
13561       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13562
13563     if (!player->programmed_action)
13564       CheckSaveEngineSnapshot(player);
13565   }
13566 }
13567
13568 void ScrollScreen(struct PlayerInfo *player, int mode)
13569 {
13570   static DelayCounter screen_frame_counter = { 0 };
13571
13572   if (mode == SCROLL_INIT)
13573   {
13574     // set scrolling step size according to actual player's moving speed
13575     ScrollStepSize = TILEX / player->move_delay_value;
13576
13577     screen_frame_counter.count = FrameCounter;
13578     screen_frame_counter.value = 1;
13579
13580     ScreenMovDir = player->MovDir;
13581     ScreenMovPos = player->MovPos;
13582     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13583     return;
13584   }
13585   else if (!FrameReached(&screen_frame_counter))
13586     return;
13587
13588   if (ScreenMovPos)
13589   {
13590     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13591     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13592     redraw_mask |= REDRAW_FIELD;
13593   }
13594   else
13595     ScreenMovDir = MV_NONE;
13596 }
13597
13598 void CheckNextToConditions(int x, int y)
13599 {
13600   int element = Tile[x][y];
13601
13602   if (IS_PLAYER(x, y))
13603     TestIfPlayerNextToCustomElement(x, y);
13604
13605   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13606       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13607     TestIfElementNextToCustomElement(x, y);
13608 }
13609
13610 void TestIfPlayerNextToCustomElement(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 i;
13622
13623   if (!IS_PLAYER(x, y))
13624     return;
13625
13626   struct PlayerInfo *player = PLAYERINFO(x, y);
13627
13628   if (player->is_moving)
13629     return;
13630
13631   for (i = 0; i < NUM_DIRECTIONS; i++)
13632   {
13633     int xx = x + xy[i].x;
13634     int yy = y + xy[i].y;
13635     int border_side = trigger_sides[i][1];
13636     int border_element;
13637
13638     if (!IN_LEV_FIELD(xx, yy))
13639       continue;
13640
13641     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13642       continue;         // center and border element not connected
13643
13644     border_element = Tile[xx][yy];
13645
13646     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13647                                player->index_bit, border_side);
13648     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13649                                         CE_PLAYER_NEXT_TO_X,
13650                                         player->index_bit, border_side);
13651
13652     /* use player element that is initially defined in the level playfield,
13653        not the player element that corresponds to the runtime player number
13654        (example: a level that contains EL_PLAYER_3 as the only player would
13655        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13656
13657     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13658                              CE_NEXT_TO_X, border_side);
13659   }
13660 }
13661
13662 void TestIfPlayerTouchesCustomElement(int x, int y)
13663 {
13664   struct XY *xy = xy_topdown;
13665   static int trigger_sides[4][2] =
13666   {
13667     // center side       border side
13668     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13669     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13670     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13671     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13672   };
13673   static int touch_dir[4] =
13674   {
13675     MV_LEFT | MV_RIGHT,
13676     MV_UP   | MV_DOWN,
13677     MV_UP   | MV_DOWN,
13678     MV_LEFT | MV_RIGHT
13679   };
13680   int center_element = Tile[x][y];      // should always be non-moving!
13681   int i;
13682
13683   for (i = 0; i < NUM_DIRECTIONS; i++)
13684   {
13685     int xx = x + xy[i].x;
13686     int yy = y + xy[i].y;
13687     int center_side = trigger_sides[i][0];
13688     int border_side = trigger_sides[i][1];
13689     int border_element;
13690
13691     if (!IN_LEV_FIELD(xx, yy))
13692       continue;
13693
13694     if (IS_PLAYER(x, y))                // player found at center element
13695     {
13696       struct PlayerInfo *player = PLAYERINFO(x, y);
13697
13698       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13699         border_element = Tile[xx][yy];          // may be moving!
13700       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13701         border_element = Tile[xx][yy];
13702       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13703         border_element = MovingOrBlocked2Element(xx, yy);
13704       else
13705         continue;               // center and border element do not touch
13706
13707       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13708                                  player->index_bit, border_side);
13709       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13710                                           CE_PLAYER_TOUCHES_X,
13711                                           player->index_bit, border_side);
13712
13713       {
13714         /* use player element that is initially defined in the level playfield,
13715            not the player element that corresponds to the runtime player number
13716            (example: a level that contains EL_PLAYER_3 as the only player would
13717            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13718         int player_element = PLAYERINFO(x, y)->initial_element;
13719
13720         // as element "X" is the player here, check opposite (center) side
13721         CheckElementChangeBySide(xx, yy, border_element, player_element,
13722                                  CE_TOUCHING_X, center_side);
13723       }
13724     }
13725     else if (IS_PLAYER(xx, yy))         // player found at border element
13726     {
13727       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13728
13729       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13730       {
13731         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13732           continue;             // center and border element do not touch
13733       }
13734
13735       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13736                                  player->index_bit, center_side);
13737       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13738                                           CE_PLAYER_TOUCHES_X,
13739                                           player->index_bit, center_side);
13740
13741       {
13742         /* use player element that is initially defined in the level playfield,
13743            not the player element that corresponds to the runtime player number
13744            (example: a level that contains EL_PLAYER_3 as the only player would
13745            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13746         int player_element = PLAYERINFO(xx, yy)->initial_element;
13747
13748         // as element "X" is the player here, check opposite (border) side
13749         CheckElementChangeBySide(x, y, center_element, player_element,
13750                                  CE_TOUCHING_X, border_side);
13751       }
13752
13753       break;
13754     }
13755   }
13756 }
13757
13758 void TestIfElementNextToCustomElement(int x, int y)
13759 {
13760   struct XY *xy = xy_topdown;
13761   static int trigger_sides[4][2] =
13762   {
13763     // center side      border side
13764     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13765     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13766     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13767     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13768   };
13769   int center_element = Tile[x][y];      // should always be non-moving!
13770   int i;
13771
13772   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13773     return;
13774
13775   for (i = 0; i < NUM_DIRECTIONS; i++)
13776   {
13777     int xx = x + xy[i].x;
13778     int yy = y + xy[i].y;
13779     int border_side = trigger_sides[i][1];
13780     int border_element;
13781
13782     if (!IN_LEV_FIELD(xx, yy))
13783       continue;
13784
13785     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13786       continue;                 // center and border element not connected
13787
13788     border_element = Tile[xx][yy];
13789
13790     // check for change of center element (but change it only once)
13791     if (CheckElementChangeBySide(x, y, center_element, border_element,
13792                                  CE_NEXT_TO_X, border_side))
13793       break;
13794   }
13795 }
13796
13797 void TestIfElementTouchesCustomElement(int x, int y)
13798 {
13799   struct XY *xy = xy_topdown;
13800   static int trigger_sides[4][2] =
13801   {
13802     // center side      border side
13803     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13804     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13805     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13806     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13807   };
13808   static int touch_dir[4] =
13809   {
13810     MV_LEFT | MV_RIGHT,
13811     MV_UP   | MV_DOWN,
13812     MV_UP   | MV_DOWN,
13813     MV_LEFT | MV_RIGHT
13814   };
13815   boolean change_center_element = FALSE;
13816   int center_element = Tile[x][y];      // should always be non-moving!
13817   int border_element_old[NUM_DIRECTIONS];
13818   int i;
13819
13820   for (i = 0; i < NUM_DIRECTIONS; i++)
13821   {
13822     int xx = x + xy[i].x;
13823     int yy = y + xy[i].y;
13824     int border_element;
13825
13826     border_element_old[i] = -1;
13827
13828     if (!IN_LEV_FIELD(xx, yy))
13829       continue;
13830
13831     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13832       border_element = Tile[xx][yy];    // may be moving!
13833     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13834       border_element = Tile[xx][yy];
13835     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13836       border_element = MovingOrBlocked2Element(xx, yy);
13837     else
13838       continue;                 // center and border element do not touch
13839
13840     border_element_old[i] = border_element;
13841   }
13842
13843   for (i = 0; i < NUM_DIRECTIONS; i++)
13844   {
13845     int xx = x + xy[i].x;
13846     int yy = y + xy[i].y;
13847     int center_side = trigger_sides[i][0];
13848     int border_element = border_element_old[i];
13849
13850     if (border_element == -1)
13851       continue;
13852
13853     // check for change of border element
13854     CheckElementChangeBySide(xx, yy, border_element, center_element,
13855                              CE_TOUCHING_X, center_side);
13856
13857     // (center element cannot be player, so we don't have to check this here)
13858   }
13859
13860   for (i = 0; i < NUM_DIRECTIONS; i++)
13861   {
13862     int xx = x + xy[i].x;
13863     int yy = y + xy[i].y;
13864     int border_side = trigger_sides[i][1];
13865     int border_element = border_element_old[i];
13866
13867     if (border_element == -1)
13868       continue;
13869
13870     // check for change of center element (but change it only once)
13871     if (!change_center_element)
13872       change_center_element =
13873         CheckElementChangeBySide(x, y, center_element, border_element,
13874                                  CE_TOUCHING_X, border_side);
13875
13876     if (IS_PLAYER(xx, yy))
13877     {
13878       /* use player element that is initially defined in the level playfield,
13879          not the player element that corresponds to the runtime player number
13880          (example: a level that contains EL_PLAYER_3 as the only player would
13881          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13882       int player_element = PLAYERINFO(xx, yy)->initial_element;
13883
13884       // as element "X" is the player here, check opposite (border) side
13885       CheckElementChangeBySide(x, y, center_element, player_element,
13886                                CE_TOUCHING_X, border_side);
13887     }
13888   }
13889 }
13890
13891 void TestIfElementHitsCustomElement(int x, int y, int direction)
13892 {
13893   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13894   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13895   int hitx = x + dx, hity = y + dy;
13896   int hitting_element = Tile[x][y];
13897   int touched_element;
13898
13899   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13900     return;
13901
13902   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13903                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13904
13905   if (IN_LEV_FIELD(hitx, hity))
13906   {
13907     int opposite_direction = MV_DIR_OPPOSITE(direction);
13908     int hitting_side = direction;
13909     int touched_side = opposite_direction;
13910     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13911                           MovDir[hitx][hity] != direction ||
13912                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13913
13914     object_hit = TRUE;
13915
13916     if (object_hit)
13917     {
13918       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13919                                CE_HITTING_X, touched_side);
13920
13921       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13922                                CE_HIT_BY_X, hitting_side);
13923
13924       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13925                                CE_HIT_BY_SOMETHING, opposite_direction);
13926
13927       if (IS_PLAYER(hitx, hity))
13928       {
13929         /* use player element that is initially defined in the level playfield,
13930            not the player element that corresponds to the runtime player number
13931            (example: a level that contains EL_PLAYER_3 as the only player would
13932            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13933         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13934
13935         CheckElementChangeBySide(x, y, hitting_element, player_element,
13936                                  CE_HITTING_X, touched_side);
13937       }
13938     }
13939   }
13940
13941   // "hitting something" is also true when hitting the playfield border
13942   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13943                            CE_HITTING_SOMETHING, direction);
13944 }
13945
13946 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13947 {
13948   int i, kill_x = -1, kill_y = -1;
13949
13950   int bad_element = -1;
13951   struct XY *test_xy = xy_topdown;
13952   static int test_dir[4] =
13953   {
13954     MV_UP,
13955     MV_LEFT,
13956     MV_RIGHT,
13957     MV_DOWN
13958   };
13959
13960   for (i = 0; i < NUM_DIRECTIONS; i++)
13961   {
13962     int test_x, test_y, test_move_dir, test_element;
13963
13964     test_x = good_x + test_xy[i].x;
13965     test_y = good_y + test_xy[i].y;
13966
13967     if (!IN_LEV_FIELD(test_x, test_y))
13968       continue;
13969
13970     test_move_dir =
13971       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13972
13973     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13974
13975     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13976        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13977     */
13978     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13979         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13980     {
13981       kill_x = test_x;
13982       kill_y = test_y;
13983       bad_element = test_element;
13984
13985       break;
13986     }
13987   }
13988
13989   if (kill_x != -1 || kill_y != -1)
13990   {
13991     if (IS_PLAYER(good_x, good_y))
13992     {
13993       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13994
13995       if (player->shield_deadly_time_left > 0 &&
13996           !IS_INDESTRUCTIBLE(bad_element))
13997         Bang(kill_x, kill_y);
13998       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13999         KillPlayer(player);
14000     }
14001     else
14002       Bang(good_x, good_y);
14003   }
14004 }
14005
14006 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14007 {
14008   int i, kill_x = -1, kill_y = -1;
14009   int bad_element = Tile[bad_x][bad_y];
14010   struct XY *test_xy = xy_topdown;
14011   static int touch_dir[4] =
14012   {
14013     MV_LEFT | MV_RIGHT,
14014     MV_UP   | MV_DOWN,
14015     MV_UP   | MV_DOWN,
14016     MV_LEFT | MV_RIGHT
14017   };
14018   static int test_dir[4] =
14019   {
14020     MV_UP,
14021     MV_LEFT,
14022     MV_RIGHT,
14023     MV_DOWN
14024   };
14025
14026   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14027     return;
14028
14029   for (i = 0; i < NUM_DIRECTIONS; i++)
14030   {
14031     int test_x, test_y, test_move_dir, test_element;
14032
14033     test_x = bad_x + test_xy[i].x;
14034     test_y = bad_y + test_xy[i].y;
14035
14036     if (!IN_LEV_FIELD(test_x, test_y))
14037       continue;
14038
14039     test_move_dir =
14040       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14041
14042     test_element = Tile[test_x][test_y];
14043
14044     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14045        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14046     */
14047     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14048         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14049     {
14050       // good thing is player or penguin that does not move away
14051       if (IS_PLAYER(test_x, test_y))
14052       {
14053         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14054
14055         if (bad_element == EL_ROBOT && player->is_moving)
14056           continue;     // robot does not kill player if he is moving
14057
14058         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14059         {
14060           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14061             continue;           // center and border element do not touch
14062         }
14063
14064         kill_x = test_x;
14065         kill_y = test_y;
14066
14067         break;
14068       }
14069       else if (test_element == EL_PENGUIN)
14070       {
14071         kill_x = test_x;
14072         kill_y = test_y;
14073
14074         break;
14075       }
14076     }
14077   }
14078
14079   if (kill_x != -1 || kill_y != -1)
14080   {
14081     if (IS_PLAYER(kill_x, kill_y))
14082     {
14083       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14084
14085       if (player->shield_deadly_time_left > 0 &&
14086           !IS_INDESTRUCTIBLE(bad_element))
14087         Bang(bad_x, bad_y);
14088       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14089         KillPlayer(player);
14090     }
14091     else
14092       Bang(kill_x, kill_y);
14093   }
14094 }
14095
14096 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14097 {
14098   int bad_element = Tile[bad_x][bad_y];
14099   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14100   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14101   int test_x = bad_x + dx, test_y = bad_y + dy;
14102   int test_move_dir, test_element;
14103   int kill_x = -1, kill_y = -1;
14104
14105   if (!IN_LEV_FIELD(test_x, test_y))
14106     return;
14107
14108   test_move_dir =
14109     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14110
14111   test_element = Tile[test_x][test_y];
14112
14113   if (test_move_dir != bad_move_dir)
14114   {
14115     // good thing can be player or penguin that does not move away
14116     if (IS_PLAYER(test_x, test_y))
14117     {
14118       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14119
14120       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14121          player as being hit when he is moving towards the bad thing, because
14122          the "get hit by" condition would be lost after the player stops) */
14123       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14124         return;         // player moves away from bad thing
14125
14126       kill_x = test_x;
14127       kill_y = test_y;
14128     }
14129     else if (test_element == EL_PENGUIN)
14130     {
14131       kill_x = test_x;
14132       kill_y = test_y;
14133     }
14134   }
14135
14136   if (kill_x != -1 || kill_y != -1)
14137   {
14138     if (IS_PLAYER(kill_x, kill_y))
14139     {
14140       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14141
14142       if (player->shield_deadly_time_left > 0 &&
14143           !IS_INDESTRUCTIBLE(bad_element))
14144         Bang(bad_x, bad_y);
14145       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14146         KillPlayer(player);
14147     }
14148     else
14149       Bang(kill_x, kill_y);
14150   }
14151 }
14152
14153 void TestIfPlayerTouchesBadThing(int x, int y)
14154 {
14155   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14156 }
14157
14158 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14159 {
14160   TestIfGoodThingHitsBadThing(x, y, move_dir);
14161 }
14162
14163 void TestIfBadThingTouchesPlayer(int x, int y)
14164 {
14165   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14166 }
14167
14168 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14169 {
14170   TestIfBadThingHitsGoodThing(x, y, move_dir);
14171 }
14172
14173 void TestIfFriendTouchesBadThing(int x, int y)
14174 {
14175   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14176 }
14177
14178 void TestIfBadThingTouchesFriend(int x, int y)
14179 {
14180   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14181 }
14182
14183 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14184 {
14185   int i, kill_x = bad_x, kill_y = bad_y;
14186   struct XY *xy = xy_topdown;
14187
14188   for (i = 0; i < NUM_DIRECTIONS; i++)
14189   {
14190     int x, y, element;
14191
14192     x = bad_x + xy[i].x;
14193     y = bad_y + xy[i].y;
14194     if (!IN_LEV_FIELD(x, y))
14195       continue;
14196
14197     element = Tile[x][y];
14198     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14199         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14200     {
14201       kill_x = x;
14202       kill_y = y;
14203       break;
14204     }
14205   }
14206
14207   if (kill_x != bad_x || kill_y != bad_y)
14208     Bang(bad_x, bad_y);
14209 }
14210
14211 void KillPlayer(struct PlayerInfo *player)
14212 {
14213   int jx = player->jx, jy = player->jy;
14214
14215   if (!player->active)
14216     return;
14217
14218 #if 0
14219   Debug("game:playing:KillPlayer",
14220         "0: killed == %d, active == %d, reanimated == %d",
14221         player->killed, player->active, player->reanimated);
14222 #endif
14223
14224   /* the following code was introduced to prevent an infinite loop when calling
14225      -> Bang()
14226      -> CheckTriggeredElementChangeExt()
14227      -> ExecuteCustomElementAction()
14228      -> KillPlayer()
14229      -> (infinitely repeating the above sequence of function calls)
14230      which occurs when killing the player while having a CE with the setting
14231      "kill player X when explosion of <player X>"; the solution using a new
14232      field "player->killed" was chosen for backwards compatibility, although
14233      clever use of the fields "player->active" etc. would probably also work */
14234 #if 1
14235   if (player->killed)
14236     return;
14237 #endif
14238
14239   player->killed = TRUE;
14240
14241   // remove accessible field at the player's position
14242   RemoveField(jx, jy);
14243
14244   // deactivate shield (else Bang()/Explode() would not work right)
14245   player->shield_normal_time_left = 0;
14246   player->shield_deadly_time_left = 0;
14247
14248 #if 0
14249   Debug("game:playing:KillPlayer",
14250         "1: killed == %d, active == %d, reanimated == %d",
14251         player->killed, player->active, player->reanimated);
14252 #endif
14253
14254   Bang(jx, jy);
14255
14256 #if 0
14257   Debug("game:playing:KillPlayer",
14258         "2: killed == %d, active == %d, reanimated == %d",
14259         player->killed, player->active, player->reanimated);
14260 #endif
14261
14262   if (player->reanimated)       // killed player may have been reanimated
14263     player->killed = player->reanimated = FALSE;
14264   else
14265     BuryPlayer(player);
14266 }
14267
14268 static void KillPlayerUnlessEnemyProtected(int x, int y)
14269 {
14270   if (!PLAYER_ENEMY_PROTECTED(x, y))
14271     KillPlayer(PLAYERINFO(x, y));
14272 }
14273
14274 static void KillPlayerUnlessExplosionProtected(int x, int y)
14275 {
14276   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14277     KillPlayer(PLAYERINFO(x, y));
14278 }
14279
14280 void BuryPlayer(struct PlayerInfo *player)
14281 {
14282   int jx = player->jx, jy = player->jy;
14283
14284   if (!player->active)
14285     return;
14286
14287   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14288
14289   RemovePlayer(player);
14290
14291   player->buried = TRUE;
14292
14293   if (game.all_players_gone)
14294     game.GameOver = TRUE;
14295 }
14296
14297 void RemovePlayer(struct PlayerInfo *player)
14298 {
14299   int jx = player->jx, jy = player->jy;
14300   int i, found = FALSE;
14301
14302   player->present = FALSE;
14303   player->active = FALSE;
14304
14305   // required for some CE actions (even if the player is not active anymore)
14306   player->MovPos = 0;
14307
14308   if (!ExplodeField[jx][jy])
14309     StorePlayer[jx][jy] = 0;
14310
14311   if (player->is_moving)
14312     TEST_DrawLevelField(player->last_jx, player->last_jy);
14313
14314   for (i = 0; i < MAX_PLAYERS; i++)
14315     if (stored_player[i].active)
14316       found = TRUE;
14317
14318   if (!found)
14319   {
14320     game.all_players_gone = TRUE;
14321     game.GameOver = TRUE;
14322   }
14323
14324   game.exit_x = game.robot_wheel_x = jx;
14325   game.exit_y = game.robot_wheel_y = jy;
14326 }
14327
14328 void ExitPlayer(struct PlayerInfo *player)
14329 {
14330   DrawPlayer(player);   // needed here only to cleanup last field
14331   RemovePlayer(player);
14332
14333   if (game.players_still_needed > 0)
14334     game.players_still_needed--;
14335 }
14336
14337 static void SetFieldForSnapping(int x, int y, int element, int direction,
14338                                 int player_index_bit)
14339 {
14340   struct ElementInfo *ei = &element_info[element];
14341   int direction_bit = MV_DIR_TO_BIT(direction);
14342   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14343   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14344                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14345
14346   Tile[x][y] = EL_ELEMENT_SNAPPING;
14347   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14348   MovDir[x][y] = direction;
14349   Store[x][y] = element;
14350   Store2[x][y] = player_index_bit;
14351
14352   ResetGfxAnimation(x, y);
14353
14354   GfxElement[x][y] = element;
14355   GfxAction[x][y] = action;
14356   GfxDir[x][y] = direction;
14357   GfxFrame[x][y] = -1;
14358 }
14359
14360 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14361                                    int player_index_bit)
14362 {
14363   TestIfElementTouchesCustomElement(x, y);      // for empty space
14364
14365   if (level.finish_dig_collect)
14366   {
14367     int dig_side = MV_DIR_OPPOSITE(direction);
14368     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14369                         CE_PLAYER_COLLECTS_X);
14370
14371     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14372                                         player_index_bit, dig_side);
14373     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14374                                         player_index_bit, dig_side);
14375   }
14376 }
14377
14378 /*
14379   =============================================================================
14380   checkDiagonalPushing()
14381   -----------------------------------------------------------------------------
14382   check if diagonal input device direction results in pushing of object
14383   (by checking if the alternative direction is walkable, diggable, ...)
14384   =============================================================================
14385 */
14386
14387 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14388                                     int x, int y, int real_dx, int real_dy)
14389 {
14390   int jx, jy, dx, dy, xx, yy;
14391
14392   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14393     return TRUE;
14394
14395   // diagonal direction: check alternative direction
14396   jx = player->jx;
14397   jy = player->jy;
14398   dx = x - jx;
14399   dy = y - jy;
14400   xx = jx + (dx == 0 ? real_dx : 0);
14401   yy = jy + (dy == 0 ? real_dy : 0);
14402
14403   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14404 }
14405
14406 /*
14407   =============================================================================
14408   DigField()
14409   -----------------------------------------------------------------------------
14410   x, y:                 field next to player (non-diagonal) to try to dig to
14411   real_dx, real_dy:     direction as read from input device (can be diagonal)
14412   =============================================================================
14413 */
14414
14415 static int DigField(struct PlayerInfo *player,
14416                     int oldx, int oldy, int x, int y,
14417                     int real_dx, int real_dy, int mode)
14418 {
14419   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14420   boolean player_was_pushing = player->is_pushing;
14421   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14422   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14423   int jx = oldx, jy = oldy;
14424   int dx = x - jx, dy = y - jy;
14425   int nextx = x + dx, nexty = y + dy;
14426   int move_direction = (dx == -1 ? MV_LEFT  :
14427                         dx == +1 ? MV_RIGHT :
14428                         dy == -1 ? MV_UP    :
14429                         dy == +1 ? MV_DOWN  : MV_NONE);
14430   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14431   int dig_side = MV_DIR_OPPOSITE(move_direction);
14432   int old_element = Tile[jx][jy];
14433   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14434   int collect_count;
14435
14436   if (is_player)                // function can also be called by EL_PENGUIN
14437   {
14438     if (player->MovPos == 0)
14439     {
14440       player->is_digging = FALSE;
14441       player->is_collecting = FALSE;
14442     }
14443
14444     if (player->MovPos == 0)    // last pushing move finished
14445       player->is_pushing = FALSE;
14446
14447     if (mode == DF_NO_PUSH)     // player just stopped pushing
14448     {
14449       player->is_switching = FALSE;
14450       player->push_delay = -1;
14451
14452       return MP_NO_ACTION;
14453     }
14454   }
14455   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14456     old_element = Back[jx][jy];
14457
14458   // in case of element dropped at player position, check background
14459   else if (Back[jx][jy] != EL_EMPTY &&
14460            game.engine_version >= VERSION_IDENT(2,2,0,0))
14461     old_element = Back[jx][jy];
14462
14463   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14464     return MP_NO_ACTION;        // field has no opening in this direction
14465
14466   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14467     return MP_NO_ACTION;        // field has no opening in this direction
14468
14469   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14470   {
14471     SplashAcid(x, y);
14472
14473     Tile[jx][jy] = player->artwork_element;
14474     InitMovingField(jx, jy, MV_DOWN);
14475     Store[jx][jy] = EL_ACID;
14476     ContinueMoving(jx, jy);
14477     BuryPlayer(player);
14478
14479     return MP_DONT_RUN_INTO;
14480   }
14481
14482   if (player_can_move && DONT_RUN_INTO(element))
14483   {
14484     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14485
14486     return MP_DONT_RUN_INTO;
14487   }
14488
14489   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14490     return MP_NO_ACTION;
14491
14492   collect_count = element_info[element].collect_count_initial;
14493
14494   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14495     return MP_NO_ACTION;
14496
14497   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14498     player_can_move = player_can_move_or_snap;
14499
14500   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14501       game.engine_version >= VERSION_IDENT(2,2,0,0))
14502   {
14503     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14504                                player->index_bit, dig_side);
14505     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14506                                         player->index_bit, dig_side);
14507
14508     if (element == EL_DC_LANDMINE)
14509       Bang(x, y);
14510
14511     if (Tile[x][y] != element)          // field changed by snapping
14512       return MP_ACTION;
14513
14514     return MP_NO_ACTION;
14515   }
14516
14517   if (player->gravity && is_player && !player->is_auto_moving &&
14518       canFallDown(player) && move_direction != MV_DOWN &&
14519       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14520     return MP_NO_ACTION;        // player cannot walk here due to gravity
14521
14522   if (player_can_move &&
14523       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14524   {
14525     int sound_element = SND_ELEMENT(element);
14526     int sound_action = ACTION_WALKING;
14527
14528     if (IS_RND_GATE(element))
14529     {
14530       if (!player->key[RND_GATE_NR(element)])
14531         return MP_NO_ACTION;
14532     }
14533     else if (IS_RND_GATE_GRAY(element))
14534     {
14535       if (!player->key[RND_GATE_GRAY_NR(element)])
14536         return MP_NO_ACTION;
14537     }
14538     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14539     {
14540       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14541         return MP_NO_ACTION;
14542     }
14543     else if (element == EL_EXIT_OPEN ||
14544              element == EL_EM_EXIT_OPEN ||
14545              element == EL_EM_EXIT_OPENING ||
14546              element == EL_STEEL_EXIT_OPEN ||
14547              element == EL_EM_STEEL_EXIT_OPEN ||
14548              element == EL_EM_STEEL_EXIT_OPENING ||
14549              element == EL_SP_EXIT_OPEN ||
14550              element == EL_SP_EXIT_OPENING)
14551     {
14552       sound_action = ACTION_PASSING;    // player is passing exit
14553     }
14554     else if (element == EL_EMPTY)
14555     {
14556       sound_action = ACTION_MOVING;             // nothing to walk on
14557     }
14558
14559     // play sound from background or player, whatever is available
14560     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14561       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14562     else
14563       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14564   }
14565   else if (player_can_move &&
14566            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14567   {
14568     if (!ACCESS_FROM(element, opposite_direction))
14569       return MP_NO_ACTION;      // field not accessible from this direction
14570
14571     if (CAN_MOVE(element))      // only fixed elements can be passed!
14572       return MP_NO_ACTION;
14573
14574     if (IS_EM_GATE(element))
14575     {
14576       if (!player->key[EM_GATE_NR(element)])
14577         return MP_NO_ACTION;
14578     }
14579     else if (IS_EM_GATE_GRAY(element))
14580     {
14581       if (!player->key[EM_GATE_GRAY_NR(element)])
14582         return MP_NO_ACTION;
14583     }
14584     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14585     {
14586       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14587         return MP_NO_ACTION;
14588     }
14589     else if (IS_EMC_GATE(element))
14590     {
14591       if (!player->key[EMC_GATE_NR(element)])
14592         return MP_NO_ACTION;
14593     }
14594     else if (IS_EMC_GATE_GRAY(element))
14595     {
14596       if (!player->key[EMC_GATE_GRAY_NR(element)])
14597         return MP_NO_ACTION;
14598     }
14599     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14600     {
14601       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14602         return MP_NO_ACTION;
14603     }
14604     else if (element == EL_DC_GATE_WHITE ||
14605              element == EL_DC_GATE_WHITE_GRAY ||
14606              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14607     {
14608       if (player->num_white_keys == 0)
14609         return MP_NO_ACTION;
14610
14611       player->num_white_keys--;
14612     }
14613     else if (IS_SP_PORT(element))
14614     {
14615       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14616           element == EL_SP_GRAVITY_PORT_RIGHT ||
14617           element == EL_SP_GRAVITY_PORT_UP ||
14618           element == EL_SP_GRAVITY_PORT_DOWN)
14619         player->gravity = !player->gravity;
14620       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14621                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14622                element == EL_SP_GRAVITY_ON_PORT_UP ||
14623                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14624         player->gravity = TRUE;
14625       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14626                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14627                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14628                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14629         player->gravity = FALSE;
14630     }
14631
14632     // automatically move to the next field with double speed
14633     player->programmed_action = move_direction;
14634
14635     if (player->move_delay_reset_counter == 0)
14636     {
14637       player->move_delay_reset_counter = 2;     // two double speed steps
14638
14639       DOUBLE_PLAYER_SPEED(player);
14640     }
14641
14642     PlayLevelSoundAction(x, y, ACTION_PASSING);
14643   }
14644   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14645   {
14646     RemoveField(x, y);
14647
14648     if (mode != DF_SNAP)
14649     {
14650       GfxElement[x][y] = GFX_ELEMENT(element);
14651       player->is_digging = TRUE;
14652     }
14653
14654     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14655
14656     // use old behaviour for old levels (digging)
14657     if (!level.finish_dig_collect)
14658     {
14659       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14660                                           player->index_bit, dig_side);
14661
14662       // if digging triggered player relocation, finish digging tile
14663       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14664         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14665     }
14666
14667     if (mode == DF_SNAP)
14668     {
14669       if (level.block_snap_field)
14670         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14671       else
14672         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14673
14674       // use old behaviour for old levels (snapping)
14675       if (!level.finish_dig_collect)
14676         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14677                                             player->index_bit, dig_side);
14678     }
14679   }
14680   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14681   {
14682     RemoveField(x, y);
14683
14684     if (is_player && mode != DF_SNAP)
14685     {
14686       GfxElement[x][y] = element;
14687       player->is_collecting = TRUE;
14688     }
14689
14690     if (element == EL_SPEED_PILL)
14691     {
14692       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14693     }
14694     else if (element == EL_EXTRA_TIME && level.time > 0)
14695     {
14696       TimeLeft += level.extra_time;
14697
14698       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14699
14700       DisplayGameControlValues();
14701     }
14702     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14703     {
14704       int shield_time = (element == EL_SHIELD_DEADLY ?
14705                          level.shield_deadly_time :
14706                          level.shield_normal_time);
14707
14708       player->shield_normal_time_left += shield_time;
14709       if (element == EL_SHIELD_DEADLY)
14710         player->shield_deadly_time_left += shield_time;
14711     }
14712     else if (element == EL_DYNAMITE ||
14713              element == EL_EM_DYNAMITE ||
14714              element == EL_SP_DISK_RED)
14715     {
14716       if (player->inventory_size < MAX_INVENTORY_SIZE)
14717         player->inventory_element[player->inventory_size++] = element;
14718
14719       DrawGameDoorValues();
14720     }
14721     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14722     {
14723       player->dynabomb_count++;
14724       player->dynabombs_left++;
14725     }
14726     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14727     {
14728       player->dynabomb_size++;
14729     }
14730     else if (element == EL_DYNABOMB_INCREASE_POWER)
14731     {
14732       player->dynabomb_xl = TRUE;
14733     }
14734     else if (IS_KEY(element))
14735     {
14736       player->key[KEY_NR(element)] = TRUE;
14737
14738       DrawGameDoorValues();
14739     }
14740     else if (element == EL_DC_KEY_WHITE)
14741     {
14742       player->num_white_keys++;
14743
14744       // display white keys?
14745       // DrawGameDoorValues();
14746     }
14747     else if (IS_ENVELOPE(element))
14748     {
14749       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14750
14751       if (!wait_for_snapping)
14752         player->show_envelope = element;
14753     }
14754     else if (element == EL_EMC_LENSES)
14755     {
14756       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14757
14758       RedrawAllInvisibleElementsForLenses();
14759     }
14760     else if (element == EL_EMC_MAGNIFIER)
14761     {
14762       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14763
14764       RedrawAllInvisibleElementsForMagnifier();
14765     }
14766     else if (IS_DROPPABLE(element) ||
14767              IS_THROWABLE(element))     // can be collected and dropped
14768     {
14769       int i;
14770
14771       if (collect_count == 0)
14772         player->inventory_infinite_element = element;
14773       else
14774         for (i = 0; i < collect_count; i++)
14775           if (player->inventory_size < MAX_INVENTORY_SIZE)
14776             player->inventory_element[player->inventory_size++] = element;
14777
14778       DrawGameDoorValues();
14779     }
14780     else if (collect_count > 0)
14781     {
14782       game.gems_still_needed -= collect_count;
14783       if (game.gems_still_needed < 0)
14784         game.gems_still_needed = 0;
14785
14786       game.snapshot.collected_item = TRUE;
14787
14788       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14789
14790       DisplayGameControlValues();
14791     }
14792
14793     RaiseScoreElement(element);
14794     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14795
14796     // use old behaviour for old levels (collecting)
14797     if (!level.finish_dig_collect && is_player)
14798     {
14799       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14800                                           player->index_bit, dig_side);
14801
14802       // if collecting triggered player relocation, finish collecting tile
14803       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14804         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14805     }
14806
14807     if (mode == DF_SNAP)
14808     {
14809       if (level.block_snap_field)
14810         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14811       else
14812         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14813
14814       // use old behaviour for old levels (snapping)
14815       if (!level.finish_dig_collect)
14816         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14817                                             player->index_bit, dig_side);
14818     }
14819   }
14820   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14821   {
14822     if (mode == DF_SNAP && element != EL_BD_ROCK)
14823       return MP_NO_ACTION;
14824
14825     if (CAN_FALL(element) && dy)
14826       return MP_NO_ACTION;
14827
14828     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14829         !(element == EL_SPRING && level.use_spring_bug))
14830       return MP_NO_ACTION;
14831
14832     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14833         ((move_direction & MV_VERTICAL &&
14834           ((element_info[element].move_pattern & MV_LEFT &&
14835             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14836            (element_info[element].move_pattern & MV_RIGHT &&
14837             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14838          (move_direction & MV_HORIZONTAL &&
14839           ((element_info[element].move_pattern & MV_UP &&
14840             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14841            (element_info[element].move_pattern & MV_DOWN &&
14842             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14843       return MP_NO_ACTION;
14844
14845     // do not push elements already moving away faster than player
14846     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14847         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14848       return MP_NO_ACTION;
14849
14850     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14851     {
14852       if (player->push_delay_value == -1 || !player_was_pushing)
14853         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14854     }
14855     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14856     {
14857       if (player->push_delay_value == -1)
14858         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14859     }
14860     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14861     {
14862       if (!player->is_pushing)
14863         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14864     }
14865
14866     player->is_pushing = TRUE;
14867     player->is_active = TRUE;
14868
14869     if (!(IN_LEV_FIELD(nextx, nexty) &&
14870           (IS_FREE(nextx, nexty) ||
14871            (IS_SB_ELEMENT(element) &&
14872             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14873            (IS_CUSTOM_ELEMENT(element) &&
14874             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14875       return MP_NO_ACTION;
14876
14877     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14878       return MP_NO_ACTION;
14879
14880     if (player->push_delay == -1)       // new pushing; restart delay
14881       player->push_delay = 0;
14882
14883     if (player->push_delay < player->push_delay_value &&
14884         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14885         element != EL_SPRING && element != EL_BALLOON)
14886     {
14887       // make sure that there is no move delay before next try to push
14888       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14889         player->move_delay = 0;
14890
14891       return MP_NO_ACTION;
14892     }
14893
14894     if (IS_CUSTOM_ELEMENT(element) &&
14895         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14896     {
14897       if (!DigFieldByCE(nextx, nexty, element))
14898         return MP_NO_ACTION;
14899     }
14900
14901     if (IS_SB_ELEMENT(element))
14902     {
14903       boolean sokoban_task_solved = FALSE;
14904
14905       if (element == EL_SOKOBAN_FIELD_FULL)
14906       {
14907         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14908
14909         IncrementSokobanFieldsNeeded();
14910         IncrementSokobanObjectsNeeded();
14911       }
14912
14913       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14914       {
14915         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14916
14917         DecrementSokobanFieldsNeeded();
14918         DecrementSokobanObjectsNeeded();
14919
14920         // sokoban object was pushed from empty field to sokoban field
14921         if (Back[x][y] == EL_EMPTY)
14922           sokoban_task_solved = TRUE;
14923       }
14924
14925       Tile[x][y] = EL_SOKOBAN_OBJECT;
14926
14927       if (Back[x][y] == Back[nextx][nexty])
14928         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14929       else if (Back[x][y] != 0)
14930         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14931                                     ACTION_EMPTYING);
14932       else
14933         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14934                                     ACTION_FILLING);
14935
14936       if (sokoban_task_solved &&
14937           game.sokoban_fields_still_needed == 0 &&
14938           game.sokoban_objects_still_needed == 0 &&
14939           level.auto_exit_sokoban)
14940       {
14941         game.players_still_needed = 0;
14942
14943         LevelSolved();
14944
14945         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14946       }
14947     }
14948     else
14949       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14950
14951     InitMovingField(x, y, move_direction);
14952     GfxAction[x][y] = ACTION_PUSHING;
14953
14954     if (mode == DF_SNAP)
14955       ContinueMoving(x, y);
14956     else
14957       MovPos[x][y] = (dx != 0 ? dx : dy);
14958
14959     Pushed[x][y] = TRUE;
14960     Pushed[nextx][nexty] = TRUE;
14961
14962     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14963       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14964     else
14965       player->push_delay_value = -1;    // get new value later
14966
14967     // check for element change _after_ element has been pushed
14968     if (game.use_change_when_pushing_bug)
14969     {
14970       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14971                                  player->index_bit, dig_side);
14972       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14973                                           player->index_bit, dig_side);
14974     }
14975   }
14976   else if (IS_SWITCHABLE(element))
14977   {
14978     if (PLAYER_SWITCHING(player, x, y))
14979     {
14980       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14981                                           player->index_bit, dig_side);
14982
14983       return MP_ACTION;
14984     }
14985
14986     player->is_switching = TRUE;
14987     player->switch_x = x;
14988     player->switch_y = y;
14989
14990     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14991
14992     if (element == EL_ROBOT_WHEEL)
14993     {
14994       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14995
14996       game.robot_wheel_x = x;
14997       game.robot_wheel_y = y;
14998       game.robot_wheel_active = TRUE;
14999
15000       TEST_DrawLevelField(x, y);
15001     }
15002     else if (element == EL_SP_TERMINAL)
15003     {
15004       int xx, yy;
15005
15006       SCAN_PLAYFIELD(xx, yy)
15007       {
15008         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
15009         {
15010           Bang(xx, yy);
15011         }
15012         else if (Tile[xx][yy] == EL_SP_TERMINAL)
15013         {
15014           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15015
15016           ResetGfxAnimation(xx, yy);
15017           TEST_DrawLevelField(xx, yy);
15018         }
15019       }
15020     }
15021     else if (IS_BELT_SWITCH(element))
15022     {
15023       ToggleBeltSwitch(x, y);
15024     }
15025     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15026              element == EL_SWITCHGATE_SWITCH_DOWN ||
15027              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15028              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15029     {
15030       ToggleSwitchgateSwitch();
15031     }
15032     else if (element == EL_LIGHT_SWITCH ||
15033              element == EL_LIGHT_SWITCH_ACTIVE)
15034     {
15035       ToggleLightSwitch(x, y);
15036     }
15037     else if (element == EL_TIMEGATE_SWITCH ||
15038              element == EL_DC_TIMEGATE_SWITCH)
15039     {
15040       ActivateTimegateSwitch(x, y);
15041     }
15042     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15043              element == EL_BALLOON_SWITCH_RIGHT ||
15044              element == EL_BALLOON_SWITCH_UP    ||
15045              element == EL_BALLOON_SWITCH_DOWN  ||
15046              element == EL_BALLOON_SWITCH_NONE  ||
15047              element == EL_BALLOON_SWITCH_ANY)
15048     {
15049       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15050                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15051                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15052                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15053                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15054                              move_direction);
15055     }
15056     else if (element == EL_LAMP)
15057     {
15058       Tile[x][y] = EL_LAMP_ACTIVE;
15059       game.lights_still_needed--;
15060
15061       ResetGfxAnimation(x, y);
15062       TEST_DrawLevelField(x, y);
15063     }
15064     else if (element == EL_TIME_ORB_FULL)
15065     {
15066       Tile[x][y] = EL_TIME_ORB_EMPTY;
15067
15068       if (level.time > 0 || level.use_time_orb_bug)
15069       {
15070         TimeLeft += level.time_orb_time;
15071         game.no_level_time_limit = FALSE;
15072
15073         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15074
15075         DisplayGameControlValues();
15076       }
15077
15078       ResetGfxAnimation(x, y);
15079       TEST_DrawLevelField(x, y);
15080     }
15081     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15082              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15083     {
15084       int xx, yy;
15085
15086       game.ball_active = !game.ball_active;
15087
15088       SCAN_PLAYFIELD(xx, yy)
15089       {
15090         int e = Tile[xx][yy];
15091
15092         if (game.ball_active)
15093         {
15094           if (e == EL_EMC_MAGIC_BALL)
15095             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15096           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15097             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15098         }
15099         else
15100         {
15101           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15102             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15103           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15104             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15105         }
15106       }
15107     }
15108
15109     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15110                                         player->index_bit, dig_side);
15111
15112     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15113                                         player->index_bit, dig_side);
15114
15115     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15116                                         player->index_bit, dig_side);
15117
15118     return MP_ACTION;
15119   }
15120   else
15121   {
15122     if (!PLAYER_SWITCHING(player, x, y))
15123     {
15124       player->is_switching = TRUE;
15125       player->switch_x = x;
15126       player->switch_y = y;
15127
15128       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15129                                  player->index_bit, dig_side);
15130       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15131                                           player->index_bit, dig_side);
15132
15133       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15134                                  player->index_bit, dig_side);
15135       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15136                                           player->index_bit, dig_side);
15137     }
15138
15139     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15140                                player->index_bit, dig_side);
15141     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15142                                         player->index_bit, dig_side);
15143
15144     return MP_NO_ACTION;
15145   }
15146
15147   player->push_delay = -1;
15148
15149   if (is_player)                // function can also be called by EL_PENGUIN
15150   {
15151     if (Tile[x][y] != element)          // really digged/collected something
15152     {
15153       player->is_collecting = !player->is_digging;
15154       player->is_active = TRUE;
15155
15156       player->last_removed_element = element;
15157     }
15158   }
15159
15160   return MP_MOVING;
15161 }
15162
15163 static boolean DigFieldByCE(int x, int y, int digging_element)
15164 {
15165   int element = Tile[x][y];
15166
15167   if (!IS_FREE(x, y))
15168   {
15169     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15170                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15171                   ACTION_BREAKING);
15172
15173     // no element can dig solid indestructible elements
15174     if (IS_INDESTRUCTIBLE(element) &&
15175         !IS_DIGGABLE(element) &&
15176         !IS_COLLECTIBLE(element))
15177       return FALSE;
15178
15179     if (AmoebaNr[x][y] &&
15180         (element == EL_AMOEBA_FULL ||
15181          element == EL_BD_AMOEBA ||
15182          element == EL_AMOEBA_GROWING))
15183     {
15184       AmoebaCnt[AmoebaNr[x][y]]--;
15185       AmoebaCnt2[AmoebaNr[x][y]]--;
15186     }
15187
15188     if (IS_MOVING(x, y))
15189       RemoveMovingField(x, y);
15190     else
15191     {
15192       RemoveField(x, y);
15193       TEST_DrawLevelField(x, y);
15194     }
15195
15196     // if digged element was about to explode, prevent the explosion
15197     ExplodeField[x][y] = EX_TYPE_NONE;
15198
15199     PlayLevelSoundAction(x, y, action);
15200   }
15201
15202   Store[x][y] = EL_EMPTY;
15203
15204   // this makes it possible to leave the removed element again
15205   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15206     Store[x][y] = element;
15207
15208   return TRUE;
15209 }
15210
15211 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15212 {
15213   int jx = player->jx, jy = player->jy;
15214   int x = jx + dx, y = jy + dy;
15215   int snap_direction = (dx == -1 ? MV_LEFT  :
15216                         dx == +1 ? MV_RIGHT :
15217                         dy == -1 ? MV_UP    :
15218                         dy == +1 ? MV_DOWN  : MV_NONE);
15219   boolean can_continue_snapping = (level.continuous_snapping &&
15220                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15221
15222   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15223     return FALSE;
15224
15225   if (!player->active || !IN_LEV_FIELD(x, y))
15226     return FALSE;
15227
15228   if (dx && dy)
15229     return FALSE;
15230
15231   if (!dx && !dy)
15232   {
15233     if (player->MovPos == 0)
15234       player->is_pushing = FALSE;
15235
15236     player->is_snapping = FALSE;
15237
15238     if (player->MovPos == 0)
15239     {
15240       player->is_moving = FALSE;
15241       player->is_digging = FALSE;
15242       player->is_collecting = FALSE;
15243     }
15244
15245     return FALSE;
15246   }
15247
15248   // prevent snapping with already pressed snap key when not allowed
15249   if (player->is_snapping && !can_continue_snapping)
15250     return FALSE;
15251
15252   player->MovDir = snap_direction;
15253
15254   if (player->MovPos == 0)
15255   {
15256     player->is_moving = FALSE;
15257     player->is_digging = FALSE;
15258     player->is_collecting = FALSE;
15259   }
15260
15261   player->is_dropping = FALSE;
15262   player->is_dropping_pressed = FALSE;
15263   player->drop_pressed_delay = 0;
15264
15265   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15266     return FALSE;
15267
15268   player->is_snapping = TRUE;
15269   player->is_active = TRUE;
15270
15271   if (player->MovPos == 0)
15272   {
15273     player->is_moving = FALSE;
15274     player->is_digging = FALSE;
15275     player->is_collecting = FALSE;
15276   }
15277
15278   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15279     TEST_DrawLevelField(player->last_jx, player->last_jy);
15280
15281   TEST_DrawLevelField(x, y);
15282
15283   return TRUE;
15284 }
15285
15286 static boolean DropElement(struct PlayerInfo *player)
15287 {
15288   int old_element, new_element;
15289   int dropx = player->jx, dropy = player->jy;
15290   int drop_direction = player->MovDir;
15291   int drop_side = drop_direction;
15292   int drop_element = get_next_dropped_element(player);
15293
15294   /* do not drop an element on top of another element; when holding drop key
15295      pressed without moving, dropped element must move away before the next
15296      element can be dropped (this is especially important if the next element
15297      is dynamite, which can be placed on background for historical reasons) */
15298   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15299     return MP_ACTION;
15300
15301   if (IS_THROWABLE(drop_element))
15302   {
15303     dropx += GET_DX_FROM_DIR(drop_direction);
15304     dropy += GET_DY_FROM_DIR(drop_direction);
15305
15306     if (!IN_LEV_FIELD(dropx, dropy))
15307       return FALSE;
15308   }
15309
15310   old_element = Tile[dropx][dropy];     // old element at dropping position
15311   new_element = drop_element;           // default: no change when dropping
15312
15313   // check if player is active, not moving and ready to drop
15314   if (!player->active || player->MovPos || player->drop_delay > 0)
15315     return FALSE;
15316
15317   // check if player has anything that can be dropped
15318   if (new_element == EL_UNDEFINED)
15319     return FALSE;
15320
15321   // only set if player has anything that can be dropped
15322   player->is_dropping_pressed = TRUE;
15323
15324   // check if drop key was pressed long enough for EM style dynamite
15325   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15326     return FALSE;
15327
15328   // check if anything can be dropped at the current position
15329   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15330     return FALSE;
15331
15332   // collected custom elements can only be dropped on empty fields
15333   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15334     return FALSE;
15335
15336   if (old_element != EL_EMPTY)
15337     Back[dropx][dropy] = old_element;   // store old element on this field
15338
15339   ResetGfxAnimation(dropx, dropy);
15340   ResetRandomAnimationValue(dropx, dropy);
15341
15342   if (player->inventory_size > 0 ||
15343       player->inventory_infinite_element != EL_UNDEFINED)
15344   {
15345     if (player->inventory_size > 0)
15346     {
15347       player->inventory_size--;
15348
15349       DrawGameDoorValues();
15350
15351       if (new_element == EL_DYNAMITE)
15352         new_element = EL_DYNAMITE_ACTIVE;
15353       else if (new_element == EL_EM_DYNAMITE)
15354         new_element = EL_EM_DYNAMITE_ACTIVE;
15355       else if (new_element == EL_SP_DISK_RED)
15356         new_element = EL_SP_DISK_RED_ACTIVE;
15357     }
15358
15359     Tile[dropx][dropy] = new_element;
15360
15361     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15362       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15363                           el2img(Tile[dropx][dropy]), 0);
15364
15365     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15366
15367     // needed if previous element just changed to "empty" in the last frame
15368     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15369
15370     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15371                                player->index_bit, drop_side);
15372     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15373                                         CE_PLAYER_DROPS_X,
15374                                         player->index_bit, drop_side);
15375
15376     TestIfElementTouchesCustomElement(dropx, dropy);
15377   }
15378   else          // player is dropping a dyna bomb
15379   {
15380     player->dynabombs_left--;
15381
15382     Tile[dropx][dropy] = new_element;
15383
15384     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15385       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15386                           el2img(Tile[dropx][dropy]), 0);
15387
15388     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15389   }
15390
15391   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15392     InitField_WithBug1(dropx, dropy, FALSE);
15393
15394   new_element = Tile[dropx][dropy];     // element might have changed
15395
15396   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15397       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15398   {
15399     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15400       MovDir[dropx][dropy] = drop_direction;
15401
15402     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15403
15404     // do not cause impact style collision by dropping elements that can fall
15405     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15406   }
15407
15408   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15409   player->is_dropping = TRUE;
15410
15411   player->drop_pressed_delay = 0;
15412   player->is_dropping_pressed = FALSE;
15413
15414   player->drop_x = dropx;
15415   player->drop_y = dropy;
15416
15417   return TRUE;
15418 }
15419
15420 // ----------------------------------------------------------------------------
15421 // game sound playing functions
15422 // ----------------------------------------------------------------------------
15423
15424 static int *loop_sound_frame = NULL;
15425 static int *loop_sound_volume = NULL;
15426
15427 void InitPlayLevelSound(void)
15428 {
15429   int num_sounds = getSoundListSize();
15430
15431   checked_free(loop_sound_frame);
15432   checked_free(loop_sound_volume);
15433
15434   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15435   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15436 }
15437
15438 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15439 {
15440   int sx = SCREENX(x), sy = SCREENY(y);
15441   int volume, stereo_position;
15442   int max_distance = 8;
15443   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15444
15445   if ((!setup.sound_simple && !is_loop_sound) ||
15446       (!setup.sound_loops && is_loop_sound))
15447     return;
15448
15449   if (!IN_LEV_FIELD(x, y) ||
15450       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15451       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15452     return;
15453
15454   volume = SOUND_MAX_VOLUME;
15455
15456   if (!IN_SCR_FIELD(sx, sy))
15457   {
15458     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15459     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15460
15461     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15462   }
15463
15464   stereo_position = (SOUND_MAX_LEFT +
15465                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15466                      (SCR_FIELDX + 2 * max_distance));
15467
15468   if (is_loop_sound)
15469   {
15470     /* This assures that quieter loop sounds do not overwrite louder ones,
15471        while restarting sound volume comparison with each new game frame. */
15472
15473     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15474       return;
15475
15476     loop_sound_volume[nr] = volume;
15477     loop_sound_frame[nr] = FrameCounter;
15478   }
15479
15480   PlaySoundExt(nr, volume, stereo_position, type);
15481 }
15482
15483 static void PlayLevelSound(int x, int y, int nr)
15484 {
15485   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15486 }
15487
15488 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15489 {
15490   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15491                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15492                  y < LEVELY(BY1) ? LEVELY(BY1) :
15493                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15494                  sound_action);
15495 }
15496
15497 static void PlayLevelSoundAction(int x, int y, int action)
15498 {
15499   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15500 }
15501
15502 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15503 {
15504   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15505
15506   if (sound_effect != SND_UNDEFINED)
15507     PlayLevelSound(x, y, sound_effect);
15508 }
15509
15510 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15511                                               int action)
15512 {
15513   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15514
15515   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15516     PlayLevelSound(x, y, sound_effect);
15517 }
15518
15519 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15520 {
15521   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15522
15523   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15524     PlayLevelSound(x, y, sound_effect);
15525 }
15526
15527 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15528 {
15529   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15530
15531   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15532     StopSound(sound_effect);
15533 }
15534
15535 static int getLevelMusicNr(void)
15536 {
15537   int level_pos = level_nr - leveldir_current->first_level;
15538
15539   if (levelset.music[level_nr] != MUS_UNDEFINED)
15540     return levelset.music[level_nr];            // from config file
15541   else
15542     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15543 }
15544
15545 static void FadeLevelSounds(void)
15546 {
15547   FadeSounds();
15548 }
15549
15550 static void FadeLevelMusic(void)
15551 {
15552   int music_nr = getLevelMusicNr();
15553   char *curr_music = getCurrentlyPlayingMusicFilename();
15554   char *next_music = getMusicInfoEntryFilename(music_nr);
15555
15556   if (!strEqual(curr_music, next_music))
15557     FadeMusic();
15558 }
15559
15560 void FadeLevelSoundsAndMusic(void)
15561 {
15562   FadeLevelSounds();
15563   FadeLevelMusic();
15564 }
15565
15566 static void PlayLevelMusic(void)
15567 {
15568   int music_nr = getLevelMusicNr();
15569   char *curr_music = getCurrentlyPlayingMusicFilename();
15570   char *next_music = getMusicInfoEntryFilename(music_nr);
15571
15572   if (!strEqual(curr_music, next_music))
15573     PlayMusicLoop(music_nr);
15574 }
15575
15576 static int getSoundAction_BD(int sample)
15577 {
15578   switch (sample)
15579   {
15580     case GD_S_STONE_PUSHING:
15581     case GD_S_MEGA_STONE_PUSHING:
15582     case GD_S_FLYING_STONE_PUSHING:
15583     case GD_S_WAITING_STONE_PUSHING:
15584     case GD_S_CHASING_STONE_PUSHING:
15585     case GD_S_NUT_PUSHING:
15586     case GD_S_NITRO_PACK_PUSHING:
15587     case GD_S_BLADDER_PUSHING:
15588     case GD_S_BOX_PUSHING:
15589       return ACTION_PUSHING;
15590
15591     case GD_S_STONE_FALLING:
15592     case GD_S_MEGA_STONE_FALLING:
15593     case GD_S_FLYING_STONE_FALLING:
15594     case GD_S_NUT_FALLING:
15595     case GD_S_DIRT_BALL_FALLING:
15596     case GD_S_DIRT_LOOSE_FALLING:
15597     case GD_S_NITRO_PACK_FALLING:
15598     case GD_S_FALLING_WALL_FALLING:
15599       return ACTION_FALLING;
15600
15601     case GD_S_STONE_IMPACT:
15602     case GD_S_MEGA_STONE_IMPACT:
15603     case GD_S_FLYING_STONE_IMPACT:
15604     case GD_S_NUT_IMPACT:
15605     case GD_S_DIRT_BALL_IMPACT:
15606     case GD_S_DIRT_LOOSE_IMPACT:
15607     case GD_S_NITRO_PACK_IMPACT:
15608     case GD_S_FALLING_WALL_IMPACT:
15609       return ACTION_IMPACT;
15610
15611     case GD_S_NUT_CRACKING:
15612       return ACTION_BREAKING;
15613
15614     case GD_S_EXPANDING_WALL:
15615     case GD_S_WALL_REAPPEARING:
15616     case GD_S_SLIME:
15617     case GD_S_LAVA:
15618     case GD_S_ACID_SPREADING:
15619       return ACTION_GROWING;
15620
15621     case GD_S_DIAMOND_COLLECTING:
15622     case GD_S_FLYING_DIAMOND_COLLECTING:
15623     case GD_S_SKELETON_COLLECTING:
15624     case GD_S_PNEUMATIC_COLLECTING:
15625     case GD_S_BOMB_COLLECTING:
15626     case GD_S_CLOCK_COLLECTING:
15627     case GD_S_SWEET_COLLECTING:
15628     case GD_S_KEY_COLLECTING:
15629     case GD_S_DIAMOND_KEY_COLLECTING:
15630       return ACTION_COLLECTING;
15631
15632     case GD_S_BOMB_PLACING:
15633     case GD_S_REPLICATOR:
15634       return ACTION_DROPPING;
15635
15636     case GD_S_BLADDER_MOVING:
15637       return ACTION_MOVING;
15638
15639     case GD_S_BLADDER_SPENDER:
15640     case GD_S_BLADDER_CONVERTING:
15641     case GD_S_GRAVITY_CHANGING:
15642       return ACTION_CHANGING;
15643
15644     case GD_S_BITER_EATING:
15645       return ACTION_EATING;
15646
15647     case GD_S_DOOR_OPENING:
15648     case GD_S_CRACKING:
15649       return ACTION_OPENING;
15650
15651     case GD_S_DIRT_WALKING:
15652       return ACTION_DIGGING;
15653
15654     case GD_S_EMPTY_WALKING:
15655       return ACTION_WALKING;
15656
15657     case GD_S_SWITCH_BITER:
15658     case GD_S_SWITCH_CREATURES:
15659     case GD_S_SWITCH_GRAVITY:
15660     case GD_S_SWITCH_EXPANDING:
15661     case GD_S_SWITCH_CONVEYOR:
15662     case GD_S_SWITCH_REPLICATOR:
15663     case GD_S_STIRRING:
15664       return ACTION_ACTIVATING;
15665
15666     case GD_S_TELEPORTER:
15667       return ACTION_PASSING;
15668
15669     case GD_S_EXPLODING:
15670     case GD_S_BOMB_EXPLODING:
15671     case GD_S_GHOST_EXPLODING:
15672     case GD_S_VOODOO_EXPLODING:
15673     case GD_S_NITRO_PACK_EXPLODING:
15674       return ACTION_EXPLODING;
15675
15676     case GD_S_COVERING:
15677     case GD_S_AMOEBA:
15678     case GD_S_MAGIC_WALL:
15679     case GD_S_PNEUMATIC_HAMMER:
15680     case GD_S_WATER:
15681       return ACTION_ACTIVE;
15682
15683     case GD_S_DIAMOND_FALLING_RANDOM:
15684     case GD_S_DIAMOND_FALLING_1:
15685     case GD_S_DIAMOND_FALLING_2:
15686     case GD_S_DIAMOND_FALLING_3:
15687     case GD_S_DIAMOND_FALLING_4:
15688     case GD_S_DIAMOND_FALLING_5:
15689     case GD_S_DIAMOND_FALLING_6:
15690     case GD_S_DIAMOND_FALLING_7:
15691     case GD_S_DIAMOND_FALLING_8:
15692     case GD_S_DIAMOND_IMPACT_RANDOM:
15693     case GD_S_DIAMOND_IMPACT_1:
15694     case GD_S_DIAMOND_IMPACT_2:
15695     case GD_S_DIAMOND_IMPACT_3:
15696     case GD_S_DIAMOND_IMPACT_4:
15697     case GD_S_DIAMOND_IMPACT_5:
15698     case GD_S_DIAMOND_IMPACT_6:
15699     case GD_S_DIAMOND_IMPACT_7:
15700     case GD_S_DIAMOND_IMPACT_8:
15701     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15702     case GD_S_FLYING_DIAMOND_FALLING_1:
15703     case GD_S_FLYING_DIAMOND_FALLING_2:
15704     case GD_S_FLYING_DIAMOND_FALLING_3:
15705     case GD_S_FLYING_DIAMOND_FALLING_4:
15706     case GD_S_FLYING_DIAMOND_FALLING_5:
15707     case GD_S_FLYING_DIAMOND_FALLING_6:
15708     case GD_S_FLYING_DIAMOND_FALLING_7:
15709     case GD_S_FLYING_DIAMOND_FALLING_8:
15710     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15711     case GD_S_FLYING_DIAMOND_IMPACT_1:
15712     case GD_S_FLYING_DIAMOND_IMPACT_2:
15713     case GD_S_FLYING_DIAMOND_IMPACT_3:
15714     case GD_S_FLYING_DIAMOND_IMPACT_4:
15715     case GD_S_FLYING_DIAMOND_IMPACT_5:
15716     case GD_S_FLYING_DIAMOND_IMPACT_6:
15717     case GD_S_FLYING_DIAMOND_IMPACT_7:
15718     case GD_S_FLYING_DIAMOND_IMPACT_8:
15719     case GD_S_TIMEOUT_0:
15720     case GD_S_TIMEOUT_1:
15721     case GD_S_TIMEOUT_2:
15722     case GD_S_TIMEOUT_3:
15723     case GD_S_TIMEOUT_4:
15724     case GD_S_TIMEOUT_5:
15725     case GD_S_TIMEOUT_6:
15726     case GD_S_TIMEOUT_7:
15727     case GD_S_TIMEOUT_8:
15728     case GD_S_TIMEOUT_9:
15729     case GD_S_TIMEOUT_10:
15730     case GD_S_BONUS_LIFE:
15731       // trigger special post-processing (and force sound to be non-looping)
15732       return ACTION_OTHER;
15733
15734     case GD_S_AMOEBA_MAGIC:
15735     case GD_S_FINISHED:
15736       // trigger special post-processing (and force sound to be looping)
15737       return ACTION_DEFAULT;
15738
15739     default:
15740       return ACTION_DEFAULT;
15741   }
15742 }
15743
15744 static int getSoundEffect_BD(int element_bd, int sample)
15745 {
15746   int sound_action = getSoundAction_BD(sample);
15747   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15748   int nr;
15749
15750   // standard sounds
15751   if (sound_action != ACTION_OTHER &&
15752       sound_action != ACTION_DEFAULT)
15753     return sound_effect;
15754
15755   // special post-processing for some sounds
15756   switch (sample)
15757   {
15758     case GD_S_DIAMOND_FALLING_RANDOM:
15759     case GD_S_DIAMOND_FALLING_1:
15760     case GD_S_DIAMOND_FALLING_2:
15761     case GD_S_DIAMOND_FALLING_3:
15762     case GD_S_DIAMOND_FALLING_4:
15763     case GD_S_DIAMOND_FALLING_5:
15764     case GD_S_DIAMOND_FALLING_6:
15765     case GD_S_DIAMOND_FALLING_7:
15766     case GD_S_DIAMOND_FALLING_8:
15767       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15768             sample - GD_S_DIAMOND_FALLING_1);
15769       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15770
15771       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15772         sound_effect = SND_BD_DIAMOND_FALLING;
15773       break;
15774
15775     case GD_S_DIAMOND_IMPACT_RANDOM:
15776     case GD_S_DIAMOND_IMPACT_1:
15777     case GD_S_DIAMOND_IMPACT_2:
15778     case GD_S_DIAMOND_IMPACT_3:
15779     case GD_S_DIAMOND_IMPACT_4:
15780     case GD_S_DIAMOND_IMPACT_5:
15781     case GD_S_DIAMOND_IMPACT_6:
15782     case GD_S_DIAMOND_IMPACT_7:
15783     case GD_S_DIAMOND_IMPACT_8:
15784       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15785             sample - GD_S_DIAMOND_IMPACT_1);
15786       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15787
15788       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15789         sound_effect = SND_BD_DIAMOND_IMPACT;
15790       break;
15791
15792     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15793     case GD_S_FLYING_DIAMOND_FALLING_1:
15794     case GD_S_FLYING_DIAMOND_FALLING_2:
15795     case GD_S_FLYING_DIAMOND_FALLING_3:
15796     case GD_S_FLYING_DIAMOND_FALLING_4:
15797     case GD_S_FLYING_DIAMOND_FALLING_5:
15798     case GD_S_FLYING_DIAMOND_FALLING_6:
15799     case GD_S_FLYING_DIAMOND_FALLING_7:
15800     case GD_S_FLYING_DIAMOND_FALLING_8:
15801       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15802             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15803       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15804
15805       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15806         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15807       break;
15808
15809     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15810     case GD_S_FLYING_DIAMOND_IMPACT_1:
15811     case GD_S_FLYING_DIAMOND_IMPACT_2:
15812     case GD_S_FLYING_DIAMOND_IMPACT_3:
15813     case GD_S_FLYING_DIAMOND_IMPACT_4:
15814     case GD_S_FLYING_DIAMOND_IMPACT_5:
15815     case GD_S_FLYING_DIAMOND_IMPACT_6:
15816     case GD_S_FLYING_DIAMOND_IMPACT_7:
15817     case GD_S_FLYING_DIAMOND_IMPACT_8:
15818       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15819             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15820       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15821
15822       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15823         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15824       break;
15825
15826     case GD_S_TIMEOUT_0:
15827     case GD_S_TIMEOUT_1:
15828     case GD_S_TIMEOUT_2:
15829     case GD_S_TIMEOUT_3:
15830     case GD_S_TIMEOUT_4:
15831     case GD_S_TIMEOUT_5:
15832     case GD_S_TIMEOUT_6:
15833     case GD_S_TIMEOUT_7:
15834     case GD_S_TIMEOUT_8:
15835     case GD_S_TIMEOUT_9:
15836     case GD_S_TIMEOUT_10:
15837       nr = sample - GD_S_TIMEOUT_0;
15838       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15839
15840       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15841         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15842       break;
15843
15844     case GD_S_BONUS_LIFE:
15845       sound_effect = SND_GAME_HEALTH_BONUS;
15846       break;
15847
15848     case GD_S_AMOEBA_MAGIC:
15849       sound_effect = SND_BD_AMOEBA_OTHER;
15850       break;
15851
15852     case GD_S_FINISHED:
15853       sound_effect = SND_GAME_LEVELTIME_BONUS;
15854       break;
15855
15856     default:
15857       sound_effect = SND_UNDEFINED;
15858       break;
15859   }
15860
15861   return sound_effect;
15862 }
15863
15864 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15865 {
15866   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15867   int sound_effect = getSoundEffect_BD(element, sample);
15868   int sound_action = getSoundAction_BD(sample);
15869   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15870   int offset = 0;
15871   int x = xx - offset;
15872   int y = yy - offset;
15873
15874   // some sound actions are always looping in native BD game engine
15875   if (sound_action == ACTION_DEFAULT)
15876     is_loop_sound = TRUE;
15877
15878   // some sound actions are always non-looping in native BD game engine
15879   if (sound_action == ACTION_FALLING ||
15880       sound_action == ACTION_MOVING ||
15881       sound_action == ACTION_OTHER)
15882     is_loop_sound = FALSE;
15883
15884   if (sound_effect != SND_UNDEFINED)
15885     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15886 }
15887
15888 void StopSound_BD(int element_bd, int sample)
15889 {
15890   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15891   int sound_effect = getSoundEffect_BD(element, sample);
15892
15893   if (sound_effect != SND_UNDEFINED)
15894     StopSound(sound_effect);
15895 }
15896
15897 boolean isSoundPlaying_BD(int element_bd, int sample)
15898 {
15899   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15900   int sound_effect = getSoundEffect_BD(element, sample);
15901
15902   if (sound_effect != SND_UNDEFINED)
15903     return isSoundPlaying(sound_effect);
15904
15905   return FALSE;
15906 }
15907
15908 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15909 {
15910   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15911   int offset = 0;
15912   int x = xx - offset;
15913   int y = yy - offset;
15914
15915   switch (sample)
15916   {
15917     case SOUND_blank:
15918       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15919       break;
15920
15921     case SOUND_roll:
15922       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15923       break;
15924
15925     case SOUND_stone:
15926       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15927       break;
15928
15929     case SOUND_nut:
15930       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15931       break;
15932
15933     case SOUND_crack:
15934       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15935       break;
15936
15937     case SOUND_bug:
15938       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15939       break;
15940
15941     case SOUND_tank:
15942       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15943       break;
15944
15945     case SOUND_android_clone:
15946       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15947       break;
15948
15949     case SOUND_android_move:
15950       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15951       break;
15952
15953     case SOUND_spring:
15954       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15955       break;
15956
15957     case SOUND_slurp:
15958       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15959       break;
15960
15961     case SOUND_eater:
15962       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15963       break;
15964
15965     case SOUND_eater_eat:
15966       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15967       break;
15968
15969     case SOUND_alien:
15970       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15971       break;
15972
15973     case SOUND_collect:
15974       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15975       break;
15976
15977     case SOUND_diamond:
15978       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15979       break;
15980
15981     case SOUND_squash:
15982       // !!! CHECK THIS !!!
15983 #if 1
15984       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15985 #else
15986       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15987 #endif
15988       break;
15989
15990     case SOUND_wonderfall:
15991       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15992       break;
15993
15994     case SOUND_drip:
15995       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15996       break;
15997
15998     case SOUND_push:
15999       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16000       break;
16001
16002     case SOUND_dirt:
16003       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16004       break;
16005
16006     case SOUND_acid:
16007       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16008       break;
16009
16010     case SOUND_ball:
16011       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16012       break;
16013
16014     case SOUND_slide:
16015       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16016       break;
16017
16018     case SOUND_wonder:
16019       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16020       break;
16021
16022     case SOUND_door:
16023       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16024       break;
16025
16026     case SOUND_exit_open:
16027       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16028       break;
16029
16030     case SOUND_exit_leave:
16031       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16032       break;
16033
16034     case SOUND_dynamite:
16035       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16036       break;
16037
16038     case SOUND_tick:
16039       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16040       break;
16041
16042     case SOUND_press:
16043       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16044       break;
16045
16046     case SOUND_wheel:
16047       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16048       break;
16049
16050     case SOUND_boom:
16051       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16052       break;
16053
16054     case SOUND_die:
16055       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16056       break;
16057
16058     case SOUND_time:
16059       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16060       break;
16061
16062     default:
16063       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16064       break;
16065   }
16066 }
16067
16068 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16069 {
16070   int element = map_element_SP_to_RND(element_sp);
16071   int action = map_action_SP_to_RND(action_sp);
16072   int offset = (setup.sp_show_border_elements ? 0 : 1);
16073   int x = xx - offset;
16074   int y = yy - offset;
16075
16076   PlayLevelSoundElementAction(x, y, element, action);
16077 }
16078
16079 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16080 {
16081   int element = map_element_MM_to_RND(element_mm);
16082   int action = map_action_MM_to_RND(action_mm);
16083   int offset = 0;
16084   int x = xx - offset;
16085   int y = yy - offset;
16086
16087   if (!IS_MM_ELEMENT(element))
16088     element = EL_MM_DEFAULT;
16089
16090   PlayLevelSoundElementAction(x, y, element, action);
16091 }
16092
16093 void PlaySound_MM(int sound_mm)
16094 {
16095   int sound = map_sound_MM_to_RND(sound_mm);
16096
16097   if (sound == SND_UNDEFINED)
16098     return;
16099
16100   PlaySound(sound);
16101 }
16102
16103 void PlaySoundLoop_MM(int sound_mm)
16104 {
16105   int sound = map_sound_MM_to_RND(sound_mm);
16106
16107   if (sound == SND_UNDEFINED)
16108     return;
16109
16110   PlaySoundLoop(sound);
16111 }
16112
16113 void StopSound_MM(int sound_mm)
16114 {
16115   int sound = map_sound_MM_to_RND(sound_mm);
16116
16117   if (sound == SND_UNDEFINED)
16118     return;
16119
16120   StopSound(sound);
16121 }
16122
16123 void RaiseScore(int value)
16124 {
16125   game.score += value;
16126
16127   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16128
16129   DisplayGameControlValues();
16130 }
16131
16132 void RaiseScoreElement(int element)
16133 {
16134   switch (element)
16135   {
16136     case EL_EMERALD:
16137     case EL_BD_DIAMOND:
16138     case EL_EMERALD_YELLOW:
16139     case EL_EMERALD_RED:
16140     case EL_EMERALD_PURPLE:
16141     case EL_SP_INFOTRON:
16142       RaiseScore(level.score[SC_EMERALD]);
16143       break;
16144     case EL_DIAMOND:
16145       RaiseScore(level.score[SC_DIAMOND]);
16146       break;
16147     case EL_CRYSTAL:
16148       RaiseScore(level.score[SC_CRYSTAL]);
16149       break;
16150     case EL_PEARL:
16151       RaiseScore(level.score[SC_PEARL]);
16152       break;
16153     case EL_BUG:
16154     case EL_BD_BUTTERFLY:
16155     case EL_SP_ELECTRON:
16156       RaiseScore(level.score[SC_BUG]);
16157       break;
16158     case EL_SPACESHIP:
16159     case EL_BD_FIREFLY:
16160     case EL_SP_SNIKSNAK:
16161       RaiseScore(level.score[SC_SPACESHIP]);
16162       break;
16163     case EL_YAMYAM:
16164     case EL_DARK_YAMYAM:
16165       RaiseScore(level.score[SC_YAMYAM]);
16166       break;
16167     case EL_ROBOT:
16168       RaiseScore(level.score[SC_ROBOT]);
16169       break;
16170     case EL_PACMAN:
16171       RaiseScore(level.score[SC_PACMAN]);
16172       break;
16173     case EL_NUT:
16174       RaiseScore(level.score[SC_NUT]);
16175       break;
16176     case EL_DYNAMITE:
16177     case EL_EM_DYNAMITE:
16178     case EL_SP_DISK_RED:
16179     case EL_DYNABOMB_INCREASE_NUMBER:
16180     case EL_DYNABOMB_INCREASE_SIZE:
16181     case EL_DYNABOMB_INCREASE_POWER:
16182       RaiseScore(level.score[SC_DYNAMITE]);
16183       break;
16184     case EL_SHIELD_NORMAL:
16185     case EL_SHIELD_DEADLY:
16186       RaiseScore(level.score[SC_SHIELD]);
16187       break;
16188     case EL_EXTRA_TIME:
16189       RaiseScore(level.extra_time_score);
16190       break;
16191     case EL_KEY_1:
16192     case EL_KEY_2:
16193     case EL_KEY_3:
16194     case EL_KEY_4:
16195     case EL_EM_KEY_1:
16196     case EL_EM_KEY_2:
16197     case EL_EM_KEY_3:
16198     case EL_EM_KEY_4:
16199     case EL_EMC_KEY_5:
16200     case EL_EMC_KEY_6:
16201     case EL_EMC_KEY_7:
16202     case EL_EMC_KEY_8:
16203     case EL_DC_KEY_WHITE:
16204       RaiseScore(level.score[SC_KEY]);
16205       break;
16206     default:
16207       RaiseScore(element_info[element].collect_score);
16208       break;
16209   }
16210 }
16211
16212 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16213 {
16214   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16215   {
16216     if (!quick_quit)
16217     {
16218       // prevent short reactivation of overlay buttons while closing door
16219       SetOverlayActive(FALSE);
16220       UnmapGameButtons();
16221
16222       // door may still be open due to skipped or envelope style request
16223       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16224     }
16225
16226     if (network.enabled)
16227     {
16228       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16229     }
16230     else
16231     {
16232       // when using BD game engine, cover screen before fading out
16233       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16234         game_bd.cover_screen = TRUE;
16235
16236       if (quick_quit)
16237         FadeSkipNextFadeIn();
16238
16239       SetGameStatus(GAME_MODE_MAIN);
16240
16241       DrawMainMenu();
16242     }
16243   }
16244   else          // continue playing the game
16245   {
16246     if (tape.playing && tape.deactivate_display)
16247       TapeDeactivateDisplayOff(TRUE);
16248
16249     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16250
16251     if (tape.playing && tape.deactivate_display)
16252       TapeDeactivateDisplayOn();
16253   }
16254 }
16255
16256 void RequestQuitGame(boolean escape_key_pressed)
16257 {
16258   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16259   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16260                         level_editor_test_game);
16261   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16262                           quick_quit || score_info_tape_play);
16263
16264   RequestQuitGameExt(skip_request, quick_quit,
16265                      "Do you really want to quit the game?");
16266 }
16267
16268 static char *getRestartGameMessage(void)
16269 {
16270   boolean play_again = hasStartedNetworkGame();
16271   static char message[MAX_OUTPUT_LINESIZE];
16272   char *game_over_text = "Game over!";
16273   char *play_again_text = " Play it again?";
16274
16275   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16276       game_mm.game_over_message != NULL)
16277     game_over_text = game_mm.game_over_message;
16278
16279   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16280            (play_again ? play_again_text : ""));
16281
16282   return message;
16283 }
16284
16285 static void RequestRestartGame(void)
16286 {
16287   char *message = getRestartGameMessage();
16288   boolean has_started_game = hasStartedNetworkGame();
16289   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16290   int door_state = DOOR_CLOSE_1;
16291
16292   boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game);
16293
16294   // if no restart wanted, continue with next level for BD style intermission levels
16295   if (!restart_wanted && !level_editor_test_game && level.bd_intermission)
16296   {
16297     boolean success = AdvanceToNextLevel();
16298
16299     restart_wanted = (success && setup.auto_play_next_level);
16300   }
16301
16302   if (restart_wanted)
16303   {
16304     CloseDoor(door_state);
16305
16306     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16307   }
16308   else
16309   {
16310     // if game was invoked from level editor, also close tape recorder door
16311     if (level_editor_test_game)
16312       door_state = DOOR_CLOSE_ALL;
16313
16314     CloseDoor(door_state);
16315
16316     SetGameStatus(GAME_MODE_MAIN);
16317
16318     DrawMainMenu();
16319   }
16320 }
16321
16322 boolean CheckRestartGame(void)
16323 {
16324   static int game_over_delay = 0;
16325   int game_over_delay_value = 50;
16326   boolean game_over = checkGameFailed();
16327
16328   if (!game_over)
16329   {
16330     game_over_delay = game_over_delay_value;
16331
16332     return FALSE;
16333   }
16334
16335   if (game_over_delay > 0)
16336   {
16337     if (game_over_delay == game_over_delay_value / 2)
16338       PlaySound(SND_GAME_LOSING);
16339
16340     game_over_delay--;
16341
16342     return FALSE;
16343   }
16344
16345   // do not ask to play again if request dialog is already active
16346   if (game.request_active)
16347     return FALSE;
16348
16349   // do not ask to play again if request dialog already handled
16350   if (game.RestartGameRequested)
16351     return FALSE;
16352
16353   // do not ask to play again if game was never actually played
16354   if (!game.GamePlayed)
16355     return FALSE;
16356
16357   // do not ask to play again if this was disabled in setup menu
16358   if (!setup.ask_on_game_over)
16359     return FALSE;
16360
16361   game.RestartGameRequested = TRUE;
16362
16363   RequestRestartGame();
16364
16365   return TRUE;
16366 }
16367
16368 boolean checkGameRunning(void)
16369 {
16370   if (game_status != GAME_MODE_PLAYING)
16371     return FALSE;
16372
16373   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16374     return FALSE;
16375
16376   return TRUE;
16377 }
16378
16379 boolean checkGamePlaying(void)
16380 {
16381   if (game_status != GAME_MODE_PLAYING)
16382     return FALSE;
16383
16384   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16385     return FALSE;
16386
16387   return TRUE;
16388 }
16389
16390 boolean checkGameSolved(void)
16391 {
16392   // set for all game engines if level was solved
16393   return game.LevelSolved_GameEnd;
16394 }
16395
16396 boolean checkGameFailed(void)
16397 {
16398   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16399     return (game_bd.game_over && !game_bd.level_solved);
16400   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16401     return (game_em.game_over && !game_em.level_solved);
16402   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16403     return (game_sp.game_over && !game_sp.level_solved);
16404   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16405     return (game_mm.game_over && !game_mm.level_solved);
16406   else                          // GAME_ENGINE_TYPE_RND
16407     return (game.GameOver && !game.LevelSolved);
16408 }
16409
16410 boolean checkGameEnded(void)
16411 {
16412   return (checkGameSolved() || checkGameFailed());
16413 }
16414
16415
16416 // ----------------------------------------------------------------------------
16417 // random generator functions
16418 // ----------------------------------------------------------------------------
16419
16420 unsigned int InitEngineRandom_RND(int seed)
16421 {
16422   game.num_random_calls = 0;
16423
16424   return InitEngineRandom(seed);
16425 }
16426
16427 unsigned int RND(int max)
16428 {
16429   if (max > 0)
16430   {
16431     game.num_random_calls++;
16432
16433     return GetEngineRandom(max);
16434   }
16435
16436   return 0;
16437 }
16438
16439
16440 // ----------------------------------------------------------------------------
16441 // game engine snapshot handling functions
16442 // ----------------------------------------------------------------------------
16443
16444 struct EngineSnapshotInfo
16445 {
16446   // runtime values for custom element collect score
16447   int collect_score[NUM_CUSTOM_ELEMENTS];
16448
16449   // runtime values for group element choice position
16450   int choice_pos[NUM_GROUP_ELEMENTS];
16451
16452   // runtime values for belt position animations
16453   int belt_graphic[4][NUM_BELT_PARTS];
16454   int belt_anim_mode[4][NUM_BELT_PARTS];
16455 };
16456
16457 static struct EngineSnapshotInfo engine_snapshot_rnd;
16458 static char *snapshot_level_identifier = NULL;
16459 static int snapshot_level_nr = -1;
16460
16461 static void SaveEngineSnapshotValues_RND(void)
16462 {
16463   static int belt_base_active_element[4] =
16464   {
16465     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16466     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16467     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16468     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16469   };
16470   int i, j;
16471
16472   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16473   {
16474     int element = EL_CUSTOM_START + i;
16475
16476     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16477   }
16478
16479   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16480   {
16481     int element = EL_GROUP_START + i;
16482
16483     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16484   }
16485
16486   for (i = 0; i < 4; i++)
16487   {
16488     for (j = 0; j < NUM_BELT_PARTS; j++)
16489     {
16490       int element = belt_base_active_element[i] + j;
16491       int graphic = el2img(element);
16492       int anim_mode = graphic_info[graphic].anim_mode;
16493
16494       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16495       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16496     }
16497   }
16498 }
16499
16500 static void LoadEngineSnapshotValues_RND(void)
16501 {
16502   unsigned int num_random_calls = game.num_random_calls;
16503   int i, j;
16504
16505   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16506   {
16507     int element = EL_CUSTOM_START + i;
16508
16509     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16510   }
16511
16512   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16513   {
16514     int element = EL_GROUP_START + i;
16515
16516     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16517   }
16518
16519   for (i = 0; i < 4; i++)
16520   {
16521     for (j = 0; j < NUM_BELT_PARTS; j++)
16522     {
16523       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16524       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16525
16526       graphic_info[graphic].anim_mode = anim_mode;
16527     }
16528   }
16529
16530   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16531   {
16532     InitRND(tape.random_seed);
16533     for (i = 0; i < num_random_calls; i++)
16534       RND(1);
16535   }
16536
16537   if (game.num_random_calls != num_random_calls)
16538   {
16539     Error("number of random calls out of sync");
16540     Error("number of random calls should be %d", num_random_calls);
16541     Error("number of random calls is %d", game.num_random_calls);
16542
16543     Fail("this should not happen -- please debug");
16544   }
16545 }
16546
16547 void FreeEngineSnapshotSingle(void)
16548 {
16549   FreeSnapshotSingle();
16550
16551   setString(&snapshot_level_identifier, NULL);
16552   snapshot_level_nr = -1;
16553 }
16554
16555 void FreeEngineSnapshotList(void)
16556 {
16557   FreeSnapshotList();
16558 }
16559
16560 static ListNode *SaveEngineSnapshotBuffers(void)
16561 {
16562   ListNode *buffers = NULL;
16563
16564   // copy some special values to a structure better suited for the snapshot
16565
16566   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16567     SaveEngineSnapshotValues_RND();
16568   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16569     SaveEngineSnapshotValues_EM();
16570   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16571     SaveEngineSnapshotValues_SP(&buffers);
16572   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16573     SaveEngineSnapshotValues_MM();
16574
16575   // save values stored in special snapshot structure
16576
16577   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16578     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16579   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16580     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16581   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16582     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16583   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16584     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16585
16586   // save further RND engine values
16587
16588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16591
16592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16598
16599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16600   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16602
16603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16604
16605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16607
16608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16621   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16624   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16625   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16626
16627   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16628   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16629
16630   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16631   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16632   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16633
16634   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16635   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16636
16637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16638   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16640   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16642   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16643
16644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16646
16647 #if 0
16648   ListNode *node = engine_snapshot_list_rnd;
16649   int num_bytes = 0;
16650
16651   while (node != NULL)
16652   {
16653     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16654
16655     node = node->next;
16656   }
16657
16658   Debug("game:playing:SaveEngineSnapshotBuffers",
16659         "size of engine snapshot: %d bytes", num_bytes);
16660 #endif
16661
16662   return buffers;
16663 }
16664
16665 void SaveEngineSnapshotSingle(void)
16666 {
16667   ListNode *buffers = SaveEngineSnapshotBuffers();
16668
16669   // finally save all snapshot buffers to single snapshot
16670   SaveSnapshotSingle(buffers);
16671
16672   // save level identification information
16673   setString(&snapshot_level_identifier, leveldir_current->identifier);
16674   snapshot_level_nr = level_nr;
16675 }
16676
16677 boolean CheckSaveEngineSnapshotToList(void)
16678 {
16679   boolean save_snapshot =
16680     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16681      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16682       game.snapshot.changed_action) ||
16683      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16684       game.snapshot.collected_item));
16685
16686   game.snapshot.changed_action = FALSE;
16687   game.snapshot.collected_item = FALSE;
16688   game.snapshot.save_snapshot = save_snapshot;
16689
16690   return save_snapshot;
16691 }
16692
16693 void SaveEngineSnapshotToList(void)
16694 {
16695   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16696       tape.quick_resume)
16697     return;
16698
16699   ListNode *buffers = SaveEngineSnapshotBuffers();
16700
16701   // finally save all snapshot buffers to snapshot list
16702   SaveSnapshotToList(buffers);
16703 }
16704
16705 void SaveEngineSnapshotToListInitial(void)
16706 {
16707   FreeEngineSnapshotList();
16708
16709   SaveEngineSnapshotToList();
16710 }
16711
16712 static void LoadEngineSnapshotValues(void)
16713 {
16714   // restore special values from snapshot structure
16715
16716   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16717     LoadEngineSnapshotValues_RND();
16718   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16719     LoadEngineSnapshotValues_EM();
16720   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16721     LoadEngineSnapshotValues_SP();
16722   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16723     LoadEngineSnapshotValues_MM();
16724 }
16725
16726 void LoadEngineSnapshotSingle(void)
16727 {
16728   LoadSnapshotSingle();
16729
16730   LoadEngineSnapshotValues();
16731 }
16732
16733 static void LoadEngineSnapshot_Undo(int steps)
16734 {
16735   LoadSnapshotFromList_Older(steps);
16736
16737   LoadEngineSnapshotValues();
16738 }
16739
16740 static void LoadEngineSnapshot_Redo(int steps)
16741 {
16742   LoadSnapshotFromList_Newer(steps);
16743
16744   LoadEngineSnapshotValues();
16745 }
16746
16747 boolean CheckEngineSnapshotSingle(void)
16748 {
16749   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16750           snapshot_level_nr == level_nr);
16751 }
16752
16753 boolean CheckEngineSnapshotList(void)
16754 {
16755   return CheckSnapshotList();
16756 }
16757
16758
16759 // ---------- new game button stuff -------------------------------------------
16760
16761 static struct
16762 {
16763   int graphic;
16764   struct XY *pos;
16765   int gadget_id;
16766   boolean *setup_value;
16767   boolean allowed_on_tape;
16768   boolean is_touch_button;
16769   char *infotext;
16770 } gamebutton_info[NUM_GAME_BUTTONS] =
16771 {
16772   {
16773     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16774     GAME_CTRL_ID_STOP,                          NULL,
16775     TRUE, FALSE,                                "stop game"
16776   },
16777   {
16778     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16779     GAME_CTRL_ID_PAUSE,                         NULL,
16780     TRUE, FALSE,                                "pause game"
16781   },
16782   {
16783     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16784     GAME_CTRL_ID_PLAY,                          NULL,
16785     TRUE, FALSE,                                "play game"
16786   },
16787   {
16788     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16789     GAME_CTRL_ID_UNDO,                          NULL,
16790     TRUE, FALSE,                                "undo step"
16791   },
16792   {
16793     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16794     GAME_CTRL_ID_REDO,                          NULL,
16795     TRUE, FALSE,                                "redo step"
16796   },
16797   {
16798     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16799     GAME_CTRL_ID_SAVE,                          NULL,
16800     TRUE, FALSE,                                "save game"
16801   },
16802   {
16803     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16804     GAME_CTRL_ID_PAUSE2,                        NULL,
16805     TRUE, FALSE,                                "pause game"
16806   },
16807   {
16808     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16809     GAME_CTRL_ID_LOAD,                          NULL,
16810     TRUE, FALSE,                                "load game"
16811   },
16812   {
16813     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16814     GAME_CTRL_ID_RESTART,                       NULL,
16815     TRUE, FALSE,                                "restart game"
16816   },
16817   {
16818     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16819     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16820     FALSE, FALSE,                               "stop game"
16821   },
16822   {
16823     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16824     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16825     FALSE, FALSE,                               "pause game"
16826   },
16827   {
16828     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16829     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16830     FALSE, FALSE,                               "play game"
16831   },
16832   {
16833     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16834     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16835     FALSE, FALSE,                               "restart game"
16836   },
16837   {
16838     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16839     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16840     FALSE, TRUE,                                "stop game"
16841   },
16842   {
16843     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16844     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16845     FALSE, TRUE,                                "pause game"
16846   },
16847   {
16848     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16849     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16850     FALSE, TRUE,                                "restart game"
16851   },
16852   {
16853     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16854     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16855     TRUE, FALSE,                                "background music on/off"
16856   },
16857   {
16858     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16859     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16860     TRUE, FALSE,                                "sound loops on/off"
16861   },
16862   {
16863     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16864     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16865     TRUE, FALSE,                                "normal sounds on/off"
16866   },
16867   {
16868     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16869     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16870     FALSE, FALSE,                               "background music on/off"
16871   },
16872   {
16873     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16874     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16875     FALSE, FALSE,                               "sound loops on/off"
16876   },
16877   {
16878     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16879     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16880     FALSE, FALSE,                               "normal sounds on/off"
16881   }
16882 };
16883
16884 void CreateGameButtons(void)
16885 {
16886   int i;
16887
16888   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16889   {
16890     int graphic = gamebutton_info[i].graphic;
16891     struct GraphicInfo *gfx = &graphic_info[graphic];
16892     struct XY *pos = gamebutton_info[i].pos;
16893     struct GadgetInfo *gi;
16894     int button_type;
16895     boolean checked;
16896     unsigned int event_mask;
16897     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16898     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16899     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16900     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16901     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16902     int gd_x   = gfx->src_x;
16903     int gd_y   = gfx->src_y;
16904     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16905     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16906     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16907     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16908     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16909     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16910     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16911     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16912     int id = i;
16913
16914     // do not use touch buttons if overlay touch buttons are disabled
16915     if (is_touch_button && !setup.touch.overlay_buttons)
16916       continue;
16917
16918     if (gfx->bitmap == NULL)
16919     {
16920       game_gadget[id] = NULL;
16921
16922       continue;
16923     }
16924
16925     if (id == GAME_CTRL_ID_STOP ||
16926         id == GAME_CTRL_ID_PANEL_STOP ||
16927         id == GAME_CTRL_ID_TOUCH_STOP ||
16928         id == GAME_CTRL_ID_PLAY ||
16929         id == GAME_CTRL_ID_PANEL_PLAY ||
16930         id == GAME_CTRL_ID_SAVE ||
16931         id == GAME_CTRL_ID_LOAD ||
16932         id == GAME_CTRL_ID_RESTART ||
16933         id == GAME_CTRL_ID_PANEL_RESTART ||
16934         id == GAME_CTRL_ID_TOUCH_RESTART)
16935     {
16936       button_type = GD_TYPE_NORMAL_BUTTON;
16937       checked = FALSE;
16938       event_mask = GD_EVENT_RELEASED;
16939     }
16940     else if (id == GAME_CTRL_ID_UNDO ||
16941              id == GAME_CTRL_ID_REDO)
16942     {
16943       button_type = GD_TYPE_NORMAL_BUTTON;
16944       checked = FALSE;
16945       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16946     }
16947     else
16948     {
16949       button_type = GD_TYPE_CHECK_BUTTON;
16950       checked = (gamebutton_info[i].setup_value != NULL ?
16951                  *gamebutton_info[i].setup_value : FALSE);
16952       event_mask = GD_EVENT_PRESSED;
16953     }
16954
16955     gi = CreateGadget(GDI_CUSTOM_ID, id,
16956                       GDI_IMAGE_ID, graphic,
16957                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16958                       GDI_X, base_x + x,
16959                       GDI_Y, base_y + y,
16960                       GDI_WIDTH, gfx->width,
16961                       GDI_HEIGHT, gfx->height,
16962                       GDI_TYPE, button_type,
16963                       GDI_STATE, GD_BUTTON_UNPRESSED,
16964                       GDI_CHECKED, checked,
16965                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16966                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16967                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16968                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16969                       GDI_DIRECT_DRAW, FALSE,
16970                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16971                       GDI_EVENT_MASK, event_mask,
16972                       GDI_CALLBACK_ACTION, HandleGameButtons,
16973                       GDI_END);
16974
16975     if (gi == NULL)
16976       Fail("cannot create gadget");
16977
16978     game_gadget[id] = gi;
16979   }
16980 }
16981
16982 void FreeGameButtons(void)
16983 {
16984   int i;
16985
16986   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16987     FreeGadget(game_gadget[i]);
16988 }
16989
16990 static void UnmapGameButtonsAtSamePosition(int id)
16991 {
16992   int i;
16993
16994   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16995     if (i != id &&
16996         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16997         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16998       UnmapGadget(game_gadget[i]);
16999 }
17000
17001 static void UnmapGameButtonsAtSamePosition_All(void)
17002 {
17003   if (setup.show_load_save_buttons)
17004   {
17005     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17006     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17007     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17008   }
17009   else if (setup.show_undo_redo_buttons)
17010   {
17011     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17012     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17013     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17014   }
17015   else
17016   {
17017     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
17018     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
17019     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
17020
17021     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
17022     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
17023     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
17024   }
17025 }
17026
17027 void MapLoadSaveButtons(void)
17028 {
17029   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17030   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17031
17032   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17033   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17034 }
17035
17036 void MapUndoRedoButtons(void)
17037 {
17038   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17039   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17040
17041   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17042   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17043 }
17044
17045 void ModifyPauseButtons(void)
17046 {
17047   static int ids[] =
17048   {
17049     GAME_CTRL_ID_PAUSE,
17050     GAME_CTRL_ID_PAUSE2,
17051     GAME_CTRL_ID_PANEL_PAUSE,
17052     GAME_CTRL_ID_TOUCH_PAUSE,
17053     -1
17054   };
17055   int i;
17056
17057   // do not redraw pause button on closed door (may happen when restarting game)
17058   if (!(GetDoorState() & DOOR_OPEN_1))
17059     return;
17060
17061   for (i = 0; ids[i] > -1; i++)
17062     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17063 }
17064
17065 static void MapGameButtonsExt(boolean on_tape)
17066 {
17067   int i;
17068
17069   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17070   {
17071     if ((i == GAME_CTRL_ID_UNDO ||
17072          i == GAME_CTRL_ID_REDO) &&
17073         game_status != GAME_MODE_PLAYING)
17074       continue;
17075
17076     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17077       MapGadget(game_gadget[i]);
17078   }
17079
17080   UnmapGameButtonsAtSamePosition_All();
17081
17082   RedrawGameButtons();
17083 }
17084
17085 static void UnmapGameButtonsExt(boolean on_tape)
17086 {
17087   int i;
17088
17089   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17090     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17091       UnmapGadget(game_gadget[i]);
17092 }
17093
17094 static void RedrawGameButtonsExt(boolean on_tape)
17095 {
17096   int i;
17097
17098   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17099     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17100       RedrawGadget(game_gadget[i]);
17101 }
17102
17103 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17104 {
17105   if (gi == NULL)
17106     return;
17107
17108   gi->checked = state;
17109 }
17110
17111 static void RedrawSoundButtonGadget(int id)
17112 {
17113   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17114              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17115              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17116              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17117              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17118              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17119              id);
17120
17121   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17122   RedrawGadget(game_gadget[id2]);
17123 }
17124
17125 void MapGameButtons(void)
17126 {
17127   MapGameButtonsExt(FALSE);
17128 }
17129
17130 void UnmapGameButtons(void)
17131 {
17132   UnmapGameButtonsExt(FALSE);
17133 }
17134
17135 void RedrawGameButtons(void)
17136 {
17137   RedrawGameButtonsExt(FALSE);
17138 }
17139
17140 void MapGameButtonsOnTape(void)
17141 {
17142   MapGameButtonsExt(TRUE);
17143 }
17144
17145 void UnmapGameButtonsOnTape(void)
17146 {
17147   UnmapGameButtonsExt(TRUE);
17148 }
17149
17150 void RedrawGameButtonsOnTape(void)
17151 {
17152   RedrawGameButtonsExt(TRUE);
17153 }
17154
17155 static void GameUndoRedoExt(void)
17156 {
17157   ClearPlayerAction();
17158
17159   tape.pausing = TRUE;
17160
17161   RedrawPlayfield();
17162   UpdateAndDisplayGameControlValues();
17163
17164   DrawCompleteVideoDisplay();
17165   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17166   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17167   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17168
17169   ModifyPauseButtons();
17170
17171   BackToFront();
17172 }
17173
17174 static void GameUndo(int steps)
17175 {
17176   if (!CheckEngineSnapshotList())
17177     return;
17178
17179   int tape_property_bits = tape.property_bits;
17180
17181   LoadEngineSnapshot_Undo(steps);
17182
17183   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17184
17185   GameUndoRedoExt();
17186 }
17187
17188 static void GameRedo(int steps)
17189 {
17190   if (!CheckEngineSnapshotList())
17191     return;
17192
17193   int tape_property_bits = tape.property_bits;
17194
17195   LoadEngineSnapshot_Redo(steps);
17196
17197   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17198
17199   GameUndoRedoExt();
17200 }
17201
17202 static void HandleGameButtonsExt(int id, int button)
17203 {
17204   static boolean game_undo_executed = FALSE;
17205   int steps = BUTTON_STEPSIZE(button);
17206   boolean handle_game_buttons =
17207     (game_status == GAME_MODE_PLAYING ||
17208      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17209
17210   if (!handle_game_buttons)
17211     return;
17212
17213   switch (id)
17214   {
17215     case GAME_CTRL_ID_STOP:
17216     case GAME_CTRL_ID_PANEL_STOP:
17217     case GAME_CTRL_ID_TOUCH_STOP:
17218       TapeStopGame();
17219
17220       break;
17221
17222     case GAME_CTRL_ID_PAUSE:
17223     case GAME_CTRL_ID_PAUSE2:
17224     case GAME_CTRL_ID_PANEL_PAUSE:
17225     case GAME_CTRL_ID_TOUCH_PAUSE:
17226       if (network.enabled && game_status == GAME_MODE_PLAYING)
17227       {
17228         if (tape.pausing)
17229           SendToServer_ContinuePlaying();
17230         else
17231           SendToServer_PausePlaying();
17232       }
17233       else
17234         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17235
17236       game_undo_executed = FALSE;
17237
17238       break;
17239
17240     case GAME_CTRL_ID_PLAY:
17241     case GAME_CTRL_ID_PANEL_PLAY:
17242       if (game_status == GAME_MODE_MAIN)
17243       {
17244         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17245       }
17246       else if (tape.pausing)
17247       {
17248         if (network.enabled)
17249           SendToServer_ContinuePlaying();
17250         else
17251           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17252       }
17253       break;
17254
17255     case GAME_CTRL_ID_UNDO:
17256       // Important: When using "save snapshot when collecting an item" mode,
17257       // load last (current) snapshot for first "undo" after pressing "pause"
17258       // (else the last-but-one snapshot would be loaded, because the snapshot
17259       // pointer already points to the last snapshot when pressing "pause",
17260       // which is fine for "every step/move" mode, but not for "every collect")
17261       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17262           !game_undo_executed)
17263         steps--;
17264
17265       game_undo_executed = TRUE;
17266
17267       GameUndo(steps);
17268       break;
17269
17270     case GAME_CTRL_ID_REDO:
17271       GameRedo(steps);
17272       break;
17273
17274     case GAME_CTRL_ID_SAVE:
17275       TapeQuickSave();
17276       break;
17277
17278     case GAME_CTRL_ID_LOAD:
17279       TapeQuickLoad();
17280       break;
17281
17282     case GAME_CTRL_ID_RESTART:
17283     case GAME_CTRL_ID_PANEL_RESTART:
17284     case GAME_CTRL_ID_TOUCH_RESTART:
17285       TapeRestartGame();
17286
17287       break;
17288
17289     case SOUND_CTRL_ID_MUSIC:
17290     case SOUND_CTRL_ID_PANEL_MUSIC:
17291       if (setup.sound_music)
17292       { 
17293         setup.sound_music = FALSE;
17294
17295         FadeMusic();
17296       }
17297       else if (audio.music_available)
17298       { 
17299         setup.sound = setup.sound_music = TRUE;
17300
17301         SetAudioMode(setup.sound);
17302
17303         if (game_status == GAME_MODE_PLAYING)
17304           PlayLevelMusic();
17305       }
17306
17307       RedrawSoundButtonGadget(id);
17308
17309       break;
17310
17311     case SOUND_CTRL_ID_LOOPS:
17312     case SOUND_CTRL_ID_PANEL_LOOPS:
17313       if (setup.sound_loops)
17314         setup.sound_loops = FALSE;
17315       else if (audio.loops_available)
17316       {
17317         setup.sound = setup.sound_loops = TRUE;
17318
17319         SetAudioMode(setup.sound);
17320       }
17321
17322       RedrawSoundButtonGadget(id);
17323
17324       break;
17325
17326     case SOUND_CTRL_ID_SIMPLE:
17327     case SOUND_CTRL_ID_PANEL_SIMPLE:
17328       if (setup.sound_simple)
17329         setup.sound_simple = FALSE;
17330       else if (audio.sound_available)
17331       {
17332         setup.sound = setup.sound_simple = TRUE;
17333
17334         SetAudioMode(setup.sound);
17335       }
17336
17337       RedrawSoundButtonGadget(id);
17338
17339       break;
17340
17341     default:
17342       break;
17343   }
17344 }
17345
17346 static void HandleGameButtons(struct GadgetInfo *gi)
17347 {
17348   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17349 }
17350
17351 void HandleSoundButtonKeys(Key key)
17352 {
17353   if (key == setup.shortcut.sound_simple)
17354     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17355   else if (key == setup.shortcut.sound_loops)
17356     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17357   else if (key == setup.shortcut.sound_music)
17358     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17359 }