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