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